#include "config.hh"

#include "ast.hh"

#include "range_checking.hh"
#include "type_checking.hh"

#include "codegen.hh"

#include <iostream>
#include <vector>
#include <string>

#if HAVE_LIBPOPT
#  include <popt.h>
#endif
#include "options.hh"


#if HAVE_LIBPOPT
static struct poptOption info_options[] = {
    {
	"version",
	'\0',
	POPT_ARG_NONE,
	&options::version,
	0,
	"Print version of dprog.",
	0
    },
    {
	"contact",
	'\0',
	POPT_ARG_NONE,
	&options::contact,
	0,
	"Print contact information.",
	0
    },
    { 0 } // sentinel
};

static struct poptOption analysis_options[] = {
    {
	"strict-decl",
	's',
	POPT_ARG_NONE,
	&options::strict_decl,
	0,
	"Disable implicit symbol declarations.",
	0
    },
    {
	"strict-upd-decl",
	'\0',
	POPT_ARG_NONE,
	&options::strict_upd_decl,
	0,
	"Disable implicit symbol declarations of update matrices. "
	"(This is not done with the --strict-decl option, since usually "
	"it is not what you want)." ,
	0
    },
    { 0 } // sentinel
};

static struct poptOption backend_options[] = {
    {
	"lang",
	'l',
	POPT_ARG_STRING,
	&options::language,
	0,
	"Set output language.",
	"language"
    },
    { 0 } // sentinel
};


static struct poptOption main_options[] = {
    {
	"verbose",
	'v',
	POPT_ARG_NONE,
	&options::verbose,
	0,
	"Toggle verbose output.",
	0
    },
    {
	0,
	'\0',
	POPT_ARG_INCLUDE_TABLE,
	&info_options,
	0,
	"Information options",
	0
    },
    {
	0,
	'\0',
	POPT_ARG_INCLUDE_TABLE,
	&analysis_options,
	0,
	"Analysis options",
	0
    },
    {
	0,
	'\0',
	POPT_ARG_INCLUDE_TABLE,
	&backend_options,
	0,
	"Backend options",
	0
    },
    POPT_AUTOHELP
    { 0 } // sentinel
};
#endif


// parser
extern int yyparse();
ast::DProg *dprog = 0;


int
main (int argc, const char *argv[])
{
#if  (HAVE_LIBPOPT != 1)
  // at this point, no popt means no option parsing!

    const char *infile =      (argc > 1) ? argv[1] : 0;
    const char *output_name = (argc > 2) ? argv[2] : 0;

#else


  poptContext ctxt = poptGetContext(0, argc, argv, main_options, 0);
  poptSetOtherOptionHelp(ctxt, "[infile [output name]]");

  int opt = poptGetNextOpt(ctxt);
  if (opt < -1)
      {
	  std::cerr << poptBadOption(ctxt, POPT_BADOPTION_NOALIAS)
		    << ':' << poptStrerror(opt) << std::endl;
	  return 2;
      }

  if (options::version)
      {
	  std::cout << PACKAGE_STRING
		    << " [" << DATE << " (" << ARCH << ")]\n";

	  return 0;
      }

  if (options::contact)
      {
	  std::cout << PACKAGE_STRING << "\n\n";
	  std::cout << "For questions or comments on dprog, contact:\n"
		    << "\tThomas Mailund <thomas@mailund.dk>, <mailund@birc.dk>\n"
		    << "or\tPeter Mechlenborg <metch@daimi.au.dk>\n"
		    << "\n"
		    << "For submitting bug-reports, please use "
		    << '<' << PACKAGE_BUGREPORT << ">\n\n";

	  return 0;
      }

  const char *infile = poptGetArg(ctxt);
  const char *output_name = poptGetArg(ctxt);

  const char *extra = poptGetArg(ctxt);
  if (extra)
      {
	  std::cerr << "WARNING: "
		    << "Extra ignored options after input and output files."
		    << std::endl;
      }
#endif // HAVE_LIBPOPT


  // strict_upd_decl implies strict_decl
  if (options::strict_upd_decl)
      options::strict_decl = 1;


  if (!codegen::supported(options::language))
      {
	  std::cerr << "Backend language "
		    << options::language << " not supported.\n";
	  std::cerr << "Supported languages are:\n";
	  std::vector<std::string>::const_iterator i;
	  for (i = codegen::supported().begin();
	       i != codegen::supported().end();
	       ++i)
	      std::cerr << '\t' << *i << '\n';
	  std::cerr << std::endl;

	  return 2;
      }

  if (infile)
      {
	  FILE *f = freopen(infile, "r", stdin);
	  if (f == NULL)
	      {
		  std::cerr << "Couldn't open input file `"
			    << infile << "'\n";
		  return 2;
	      }
      }


  try {
      if (options::verbose) std::cerr << "Parsing input file...\n";
      yyparse();
      if (!dprog) return 2;			// parse error!
      
      if (options::verbose) std::cerr << "Analysing program...\n";
      range_checking::check(dprog);
      type_checking::check(dprog);
      // FIXME: more analysis here

      if (options::verbose) std::cerr << "Emitting code...\n";
      codegen::emit_code(options::language, dprog, output_name);

      if (options::verbose) std::cerr << "Done.\n";

  } catch (ast::Exception *ex) {
      ex->print_error(std::cerr);
      delete ex;
      return 2;
  }

  return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1