#define _MPC_MAIN

#include "context.h"
#include "gnu_kw.h"
#include "options.h"
#include "Tree.h"
#include "mpcScan.h"
#include "mpcPars.h"
#include "mpc_pass2.h"
#include "mpc_diag.h"
#ifndef __CB_COMPILER__
#include "mpc_adv.h"
#endif
#ifdef __WRK
#include "c2mpC.h"
#endif

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "Positions.h"

#if defined CLOCKS_PER_SEC | defined CLK_TCK
# ifndef CLOCKS_PER_SEC
#  define CLOCKS_PER_SEC CLK_TCK
# endif
# define TIME_TYPE clock_t
# define TIME_FUNC clock
#else
#include <Time.h>
# define CLOCKS_PER_SEC 1000
# define TIME_TYPE long
# define TIME_FUNC StepTime
#endif

#ifdef __CB_COMPILER__
#define MPC_VERSION "1.x.x"
#else
#define MPC_VERSION "3.x.x"
#endif

/************** compiler options (see "options.h"): **************/

int	
#ifdef __CB_COMPILER__
	CBRACKETS = YES,
#else
	CBRACKETS = NO,
#endif
	C_2_MPC = NO,
	DEMO_MODE = NO,
	VERBOSE = NO,
	DEBUG = NO,
	DUMP = NO,
	STORE_MESSAGES = YES,
	EVAL_CONST = YES,
	BLOCK_TO_GRID = NO,
	PUT_TREE = NO,
	GET_TREE = NO,
	WRITE_TREE = NO,
	READ_TREE = NO,
	KEYWORDS =
#ifdef __CB_COMPILER__
		   K_ANSI,
#else
		   K_SHORT, /* ANSI_C + mpC 'short' keywords (without 'mpc_') */
#endif
	MAX_NET = 64,
	PRAGMA_BIG = YES,
	xopt = 0,
	XOPT = 0,
	OPT_LEVEL = 0;      /* no optimization; */

/********************** local switches: **************************/

static
int	CPP_ONLY     = NO,
	COMPILE_ONLY = NO
#ifndef __CB_COMPILER__
	,
	ADVICE	     = NO,
	ADVICE_MODE  = DIAGNOST_MODE,
	RECOMPILE    = YES /* source file must be compiled at least 1 time; */
#endif
	;

char		ADVICE_FILE[100] = "",  *CURR_ADVICE_FILE;
tPosition	ADVICE_POS;

/*****************************************************************/

FILE *fp;
FILE *tree;

char argv1[100], treefile[100], mpcpp[100],
# ifdef WIN32
     rm_mpcpp[512]="del "
# else
     rm_mpcpp[512]="rm -f "
#endif
;

int main(int argc, char **argv)
{

   int RC=0;
   int NO_FILE=YES;
   int i,j, fn_num=1;
   char cpp[512]="";
   TIME_TYPE b_time, e_time;
   extern void back_end(int argc, char **argv);
   static int Store_Messages;


   b_time = TIME_FUNC();

#ifdef __CB_COMPILER__
   ADVICE_POS = NoPosition;
#endif

   /* prepare the cpp call:                                             */
   /* NOTE: __STDC__ and __STRICT_ANSI__ are defined for correct compi- */
   /*       lation of native .h-files, __MPC__ - for mpC.h, ...         */

#ifdef WIN32
   strcat(cpp, "cl /E /nologo /C /D_INTEGRAL_MAX_BITS=32 /D__MPC__ /D__STDC__ /D__STRICT_ANSI__ ");
#else
   if(getenv("MPCPREPDIR"))
     strcat(cpp,"$MPCPREPDIR/cpp -C -D__MPC__ -D__STDC__ -D__STRICT_ANSI__ -I$MPCHOME/h __SF__ ");
   else
     strcat(cpp,"CPPDIR/cpp -C -D__MPC__ -D__STDC__ -D__STRICT_ANSI__ -I$MPCHOME/h __SF__ ");
#endif

   for(i=1;i<argc;i++) {
     bool OPT=false;  /* mpC option ? */
     if(argv[i][0]=='-') {   /*** options: ***/
       if( ! strcmp(argv[i],"-v") )         OPT = true, VERBOSE = true;
	 /*** -- ADD LATER -vs, -vi, -ve, -vd, -va options; ***/
#ifndef __CB_COMPILER__
       if( ! strcmp(argv[i],"-cbr") )       OPT = true, CBRACKETS = true;
       if( ! strcmp(argv[i],"-demo") )      OPT = true, DEMO_MODE = true;
#endif
#ifdef __WRK
       if( ! strcmp(argv[i],"-c2mpC") )     OPT = true, C_2_MPC = true;
#endif
       if( ! strcmp(argv[i],"-no_const") ) {
	  OPT = true, EVAL_CONST = false;
	  fprintf(stderr, "Warning: Evaluation of constant expressions disabled;"
			  " some checks can't be performed and some errors"
			  " may be not detected\n");
       }
       if( ! strcmp(argv[i],"-const") )     OPT = true, EVAL_CONST = true;
       if( ! strcmp(argv[i],"-block") )     OPT = true, BLOCK_TO_GRID = true;
       if( ! strcmp(argv[i],"-debug") )     OPT = true, DEBUG = true;
       if( ! strcmp(argv[i],"-dump") )      OPT = true, DUMP  = true;
       if( ! strcmp(argv[i],"-mess") )      OPT = true, STORE_MESSAGES = false;
       if( ! strcmp(argv[i],"-ptree") )     OPT = true, PUT_TREE = true;
       if( ! strcmp(argv[i],"-gtree") )     OPT = true, GET_TREE = true;
       if( ! strcmp(argv[i],"-wtree") )     OPT = true, WRITE_TREE = true;
       if( ! strcmp(argv[i],"-rtree") )     OPT = true, READ_TREE = true;
#ifndef __CB_COMPILER__
       if( ! strcmp(argv[i],"-kANSI") )     OPT = true, KEYWORDS = K_ANSI;
       if( ! strcmp(argv[i],"-kSHORT"))     OPT = true, KEYWORDS = K_SHORT;
       if( ! strcmp(argv[i],"-kLONG") )     OPT = true, KEYWORDS = K_LONG;
       if( ! strcmp(argv[i],"-kALL") )      OPT = true, KEYWORDS = K_ALL;
#endif
       if( ! strcmp(argv[i],"-Woff") )      OPT = true, WngMode = WNG_OFF,
					    ActualWng = NoWngs;
       if( ! strcmp(argv[i],"-Wsome") )     OPT = true, WngMode = WNG_SOME;
       if( ! strcmp(argv[i],"-Wall") )      OPT = true, WngMode = WNG_ALL;

       if( ! strcmp(argv[i],"-Wdecl") )     OPT = true, ActualWng.Decl = YES;
       if( ! strcmp(argv[i],"-Wscope") )    OPT = true, ActualWng.Scope = YES;
       if( ! strcmp(argv[i],"-Winit") )     OPT = true, ActualWng.Init = YES;
       if( ! strcmp(argv[i],"-Wsize") )     OPT = true, ActualWng.Sizes = YES;
#ifndef __CB_COMPILER__
       if( ! strcmp(argv[i],"-Wdistr") )    OPT = true, ActualWng.Distr = YES;
#endif
       if( ! strcmp(argv[i],"-Wstats") )    OPT = true, ActualWng.Stats = YES;
       if( ! strcmp(argv[i],"-Wlab") )      OPT = true, ActualWng.Label = YES;
       if( ! strcmp(argv[i],"-Wargs") )     OPT = true, ActualWng.Args = YES;

       if( ! strcmp(argv[i],"-Wdecl-") )     OPT = true, ActualWng.Decl = NO;
       if( ! strcmp(argv[i],"-Wscope-") )    OPT = true, ActualWng.Scope = NO;
       if( ! strcmp(argv[i],"-Winit-") )     OPT = true, ActualWng.Init = NO;
       if( ! strcmp(argv[i],"-Wsize-") )     OPT = true, ActualWng.Sizes = NO;
#ifndef __CB_COMPILER__
       if( ! strcmp(argv[i],"-Wdistr-") )    OPT = true, ActualWng.Distr = NO;
#endif
       if( ! strcmp(argv[i],"-Wstats-") )    OPT = true, ActualWng.Stats = NO;
       if( ! strcmp(argv[i],"-Wlab-") )      OPT = true, ActualWng.Label = NO;
       if( ! strcmp(argv[i],"-Wargs-") )     OPT = true, ActualWng.Args = NO;

#ifndef __CB_COMPILER__
       if( ! strcmp(argv[i],"-a") )	    OPT = true, ADVICE = YES,
					    ADVICE_MODE = DIAGNOST_MODE;
       if( ! strcmp(argv[i],"-aD") )	    OPT = true, ADVICE = YES,
					    ADVICE_MODE = DIAGNOST_MODE;
       if( ! strcmp(argv[i],"-aE") )	    OPT = true, ADVICE = YES,
					    ADVICE_MODE = ERRINTER_MODE;
       if( ! strncmp(argv[i],"-aI", 3) ) {  /* INTERACT_MODE: */
	  OPT = true, ADVICE = YES,
	  ADVICE_MODE = INTERACT_MODE;
	/* file name maybe after -aI: */
	  if(argv[i][3]!='\0') {
	    strcpy(ADVICE_FILE,argv[i]+3);
/***	    if(!(fp=fopen(ADVICE_FILE,"r"))) {
	      fprintf(stderr, "Cannot open file '%s' specified after -aI,\n"
		      "mpC Advisor will explore the source file.\n",
		      ADVICE_FILE);
	      ADVICE_FILE [0] = '\0';
	    }
	    else
	      fclose(fp);  ***/
	  }
       }
       if( ! strncmp(argv[i],"-aP", 3) ) {  /* ADVICE_POS for INTERACT_MODE: */
	 OPT = true, ADVICE = YES,
	 ADVICE_MODE = INTERACT_MODE;
	 /* file name may be default or specified by -aI option; */
	 /* get position for Locate() :                          */
	 if(argv[i][3]=='\0') {
	   fprintf(stderr, "Source line should be specified after -aP ,\n"
		   "mpC Advisor will explore the source from beginning\n");
	 }
	 else {
	   ADVICE_POS.Line = atoi(argv[i]+3);
	   ADVICE_POS.Column = 1;   /* currently Column can't be specified; */
/*****/	printf("--- SPECIFIED  -aP%d\n", ADVICE_POS.Line);
	 }
       }
       /*** ADD LATER  -f<file>, -p<position> options; ***/
       if( ! strncmp(argv[i], "-net", 4) ) {
	 if(isdigit(argv[i][4]))
	   OPT = true, MAX_NET = atoi(argv[i]+4);
	 else
	   fprintf(stderr,
		   "Warning: Unrecognized option '%s' passed to preprocessor\n",
		    argv[i]);
       }
#endif

       if( ! strncmp(argv[i], "-x", 2) ) {
	 if(isdigit(argv[i][2]))
	   OPT = true, xopt = atoi(argv[i]+2);
	 else
	   fprintf(stderr,
		   "Warning: Unrecognized option '%s' passed to preprocessor\n",
		    argv[i]);
       }
       if( ! strncmp(argv[i], "-X", 2) ) {
	 if(isdigit(argv[i][2]))
	   OPT = true, XOPT = atoi(argv[i]+2);
	 else
	   fprintf(stderr,
		   "Warning: Unrecognized option '%s' passed to preprocessor\n",
		    argv[i]);
       }

       if( ! strncmp(argv[i], "-O", 2) ) {
	 if(isdigit(argv[i][2])) {
	   OPT = true, OPT_LEVEL = atoi(argv[i]+2);
	   if(OPT_LEVEL>MAX_OPT_LEVEL) {
	     fprintf(stderr, "Invalid level of optimization '%d'; the maximal level "
		     "'%d' will be used\n", OPT_LEVEL, MAX_OPT_LEVEL);
	     OPT_LEVEL = MAX_OPT_LEVEL;
	   }
	 }
	 else if(argv[i][2]=='\0')
	   OPT = true, OPT_LEVEL = 2;
	 else
	   fprintf(stderr,
		   "Warning: Unrecognized option '%s' passed to preprocessor\n",
		    argv[i]);
       }

#ifndef __CB_COMPILER__
       if( ! strcmp(argv[i],"-big") )       OPT = true, PRAGMA_BIG = true;
       if( ! strcmp(argv[i],"-small") )     OPT = true, PRAGMA_BIG = false;
#endif
       if( ! strcmp(argv[i],"-E") )         OPT = true, CPP_ONLY = true;
       if( ! strcmp(argv[i],"-analyse") )   OPT = true, COMPILE_ONLY = true;
	 /* mpC Code Generator options: */
#ifndef __CB_COMPILER__
       if( ! strcmp(argv[i],"-het") )       OPT = true;
       if( ! strcmp(argv[i],"-macro") )     OPT = true;
       if( ! strcmp(argv[i],"-null_init") ) OPT = true;
#endif
       if( ! strcmp(argv[i],"-prt") )       OPT = true;
       if( ! strcmp(argv[i],"-out") )       OPT = true;
       else if(OPT==false) {  /* pass this option to preprocessor: */
	 if( strncmp(argv[i],"-D",2) &&
             strncmp(argv[i],"-I",2) 
# ifdef WIN32  /* add MS Visual C++ specific preprocessor options: */
          && strncmp(argv[i],"-C",2) &&
             strncmp(argv[i],"-FI",3) &&
             strncmp(argv[i],"-P",2) &&
             strncmp(argv[i],"-U",2) &&
             strncmp(argv[i],"-u",2) &&
             strncmp(argv[i],"-X",2) 
# endif
  )
	   fprintf(stderr,
		   "Warning: Unrecognized option '%s' passed to preprocessor\n",
		    argv[i]);
	 strcat(cpp,argv[i]);
	 strcat(cpp," ");
       }
     }
     else {   /*** source file name: ***/
	char *ext;
	int k;
	NO_FILE = NO;
	for(k=0; k<100 && argv[i][k]!='\0'; k++) ;
	for(j=k; j>0 && argv[i][j]!='.'; j--) ;
	if(argv[i][j]=='.' || j==0) {
	  ext = & argv[i][j];
#ifdef __CB_COMPILER__
	  if(!strcmp(ext, ".cb")) {
	    if(j==0)
	      fprintf(stderr,
		"Warning: You want compile the file without name '.cb'. "
		"Are you sure?\n");
#else
	  if(!strcmp(ext,".mpc")) {
	    if(j==0)
	      fprintf(stderr,
		"Warning: You want compile the file without name '.mpc'. "
		"Are you sure?\n");
#endif
	    fn_num = i;
   	    strcat(argv1, argv[i]);
#ifndef __CB_COMPILER__
	    if(!strcmp(ADVICE_FILE,""))
	      strcpy(ADVICE_FILE, argv1);
#endif
   	    strcat(cpp, argv1);


	    if(!CPP_ONLY) {
#ifdef WIN32
	      char* dot;
	      dot = strrchr(argv1, '.');  /* last '.' in argv1 */
	      strcat(cpp, " | mpccpp ");
	      *(++dot) = 'i';
	      *(++dot) = '\0';  /* argv1 is now 'filename.i' */
#else
   	      strcat(cpp, " > ");
   	      strcat(argv1, "pp");
#endif
	      strcpy(mpcpp, argv1);
   	      strcat(cpp, argv1);


   	      strcat(rm_mpcpp, mpcpp);
	    }
	  }
	  else if(!strcmp(ext,".sym") && READ_TREE) {
	    if(j==0)
	      fprintf(stderr,
		"Warning: You want read the AST file without name '.sym'. "
		"Are you sure?\n");
	    fn_num = i;
	  }
	  else if(!strcmp(ext,".bin") && GET_TREE) {
	    if(j==0)
	      fprintf(stderr,
		"Warning: You want read the AST file without name '.bin'. "
		"Are you sure?\n");
	    fn_num = i;
	  }
	  else {
#ifdef __CB_COMPILER__
	    fprintf(stderr,"Not C[] source file: '%s'\n", argv[i]);
#else
	    fprintf(stderr,"Not mpC source file: '%s'\n", argv[i]);
#endif
	    fflush(stderr);
	    exit(1);
	  }
	}
     }
   } /* end for; */

   if(NO_FILE) {
     if(VERBOSE) {
#ifdef __CB_COMPILER__
       printf("C[] compiler (version %s)\n", MPC_VERSION);
#else
       printf("mpC compiler (version %s)\n", MPC_VERSION);
#endif
       exit(0);
     }
#ifdef __CB_COMPILER__
     fprintf(stderr,"cbc: No input file\n");
#else
     fprintf(stderr,"mpcc: No input file\n");
#endif
     fflush(stderr);
     exit(1);
   }
   else {
     fp = fopen(argv[fn_num],"r");
     NO_FILE = !fp;
     if(!NO_FILE) fclose(fp);
   }
   if(NO_FILE) {
     fprintf(stderr,"No such source file: '%s'\n", argv[fn_num]);
     fflush(stderr);
     exit(1);
   }

   if(VERBOSE)
#ifdef __CB_COMPILER__
     printf("C[] compiler (version %s)\n", MPC_VERSION);
#else
     printf("mpC compiler (version %s)\n", MPC_VERSION);
#endif

   Store_Messages = STORE_MESSAGES;  /* save the initial option; */

#ifndef __CB_COMPILER__
   CURR_ADVICE_FILE = ADVICE_FILE;

   while(RECOMPILE) {
#endif

     STORE_MESSAGES = Store_Messages;  /* restore this option after every *
					* call of Print_Messages() !!!    */

     if(PUT_TREE) {
       strcat(treefile, argv[fn_num]);
       for(j=0;j<100;j++)
         if(treefile[j]=='.') {
	   treefile[j+1] = '\0';
	   strcat(treefile, "bin");
	   break;
         }
     }
     if(WRITE_TREE) {
       strcat(treefile, argv[fn_num]);
       for(j=0;j<100;j++)
         if(treefile[j]=='.') {
	   treefile[j+1] = '\0';
	   strcat(treefile, "sym");
	   break;
         }
     }
     if(GET_TREE) {    	/* read tree from binary file: */

       if(DEBUG) printf("MPCC/CBC:> READING TREE FROM BINARY FILE ...");
       tree = fopen(argv[fn_num], "r");
       TreeRoot = GetTree(tree);
       fclose(tree);
       if(DEBUG) printf(" DONE\n");
    /**if(DEBUG) QueryTree(TreeRoot);**/

     }
     else if(READ_TREE) {    	/* read tree from ascii file: */

       if(DEBUG) printf("MPCC/CBC:> READING TREE FROM ASCII FILE ...");
       tree = fopen(argv[fn_num], "r");
       TreeRoot = ReadTree(tree);
       fclose(tree);
       if(DEBUG) printf(" DONE\n");
    /**if(DEBUG) QueryTree(TreeRoot);**/

     }
     else {		/* read program from source file: */

       if(DEBUG)printf("%s\n", cpp);
       system(cpp);
       if(CPP_ONLY) exit(0);


       mpcScan_BeginScanner();
       if(argv[argc-2]!=NULL)
         mpcScan_BeginFile(argv1); 

       RC = mpcPars(); /*** RC!=0 ONLY AFTER SYNTAX ERRORS !!! ***/
       ClosempcPars();
       system(rm_mpcpp);

       /* check whether sem. errors detected by mpcPars(): */
       RC |= Scan_Messages();

   /** if(!RC && DEBUG) QueryTree(TreeRoot); **/

       if(DUMP /**|| PUT_TREE || WRITE_TREE**/) {  /* because _______________ */
         tree = fopen("Parser.out", "w");    /* __ Cocktail ___________ */
         WriteTree(tree, TreeRoot);          /* _____ has _____________ */
         fclose(tree);                       /* ________ bugs _________ */
         if(!DUMP) remove("Parser.out");     /* ___________ in Memory.c */
       }
 
       if(!RC) {
         RC = Semantics(TreeRoot); /**************************/
       }
  /** if(DEBUG) QueryTree(TreeRoot); **/

       if(STORE_MESSAGES) Print_Messages(); /* after this STORE_MESSAGES=NO; */

     } /* end if ... else ; */

     if(DUMP) {
       tree = fopen("Eval.out", "w");
       WriteTree(tree,TreeRoot);
       fclose(tree);
     }
     if(PUT_TREE) {
       tree = fopen(treefile, "w");
       PutTree(tree,TreeRoot);
       fclose(tree);
     }
     else if(WRITE_TREE) {
       tree = fopen(treefile, "w");
       WriteTree(tree,TreeRoot);
       fclose(tree);
     }

#ifndef __CB_COMPILER__
     if(ADVICE)
       RECOMPILE = mpc_Advice(ADVICE_MODE, CURR_ADVICE_FILE, ADVICE_POS);
     else
       RECOMPILE = NO;

     if(RECOMPILE)
       ReleaseTreeModule();

   }  /** end while(RECOMPILE) **/

#ifdef __WRK
   if(C_2_MPC) print_mpC(TreeRoot->MPC_Root.Decls);
#endif

#endif

   if(!RC && !COMPILE_ONLY) back_end(argc, argv);

   e_time = TIME_FUNC();
   if(VERBOSE)
     printf("Compilation finished with code %d\nCompilation time %f sec.\n",
	    RC, (double)e_time/CLOCKS_PER_SEC);
   if(RC)
     exit(1);
   else
     exit(0);

}
