/************************************************************************
       -= SEER - C-Scripting engine v 0.94a =-
State      : under implementation
Author     : Przemyslaw Podsiadly
             email: ppodsiad@elka.pw.edu.pl
             WWW:   http://home.elka.pw.edu.pl/~ppodsiad
File:seerc.c
Desc:SeeRC compiler
************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include "seer.h"
#include "src/internal.h"
#include <math.h>
#include "export.h"
#include <string.h>

#define SeeRC_STR "SeeR compiler version "SeeR_VERSION_STR" by Theur(*Przemyslaw Podsiadly*) "SeeR_DATE_STR"\n"

int random_(int x)
{
 return (rand()%x);
}

int compiler_debug=0;
//char *compiler_debfname;
char *help=
"Syntax:
  seerc {Input files} {Options}
  seerc {Options} -r {Input file} {parameters-for-function-main}
 Options:
  -a, -author author-name         -d, -debug
  -h, -help                       -i, -info
  -o output-filename
  -r, -run {other-options} filename.sc||.cs arguments-for-main(argc,argv)
  -s, -show                       -t, -title {title-name}
  -v, -version
  -O :-Oe (generate EXPR)         -Os:stack_size_in_bytes

  SeeRC defines internal header named stdio,that contains some standard C
 functions (see readme.txt).

 Examples:
 seerc -i *.cs
 seerc -c -d -Os1000e script.sc       {compile with debug,EXPR and stack 1000b}
 seerc -r script.sc parameter1 parameter2
";

//script imports these:
#define MAXINPUTFILENAMES 256

char *FNOutput=NULL,*DEBOutput=NULL,*FNInput[MAXINPUTFILENAMES];
int InputFiles=0;
int FLShow=0;
enum {FLCompile,FLRun,FLInfo};
int FLOp=FLCompile;

int StackSize=-1;

#define SCError(i,c...) {if (!FLShow)ShowName();printf("SeeRC:");printf(c);exit(i);}
void ShowName()
{
 printf(SeeRC_STR);
}

void Compilation();
int Run(int,char**);
void Info();

int main(int argc,char *argv[])
{
 {
  int i;
        exportSeeRCSymbols();
        scAdd_External_Symbol("scrandom",random_);
        scAddExtSym(printf);
        scAddExtSym(scanf);
        scAddExtSym(sprintf);
        scAddExtSym(rand);
        scAddExtSym(srand);
        scAddExtSym(scanf);
        scAddExtSym(getchar);
        scAddExtSym(strcpy);
        scAddExtSym(strcmp);
        scAddExtSym(strdup);
        scAddExtSym(strtoul);
        scAddExtSym(strtol);
        scAddExtSym(strtod);
        scAddExtSym(atoi);
        scAddExtSym(atof);
        scAddExtSym(sqrt);
        scAddExtSym(sin);
        scAddExtSym(cos);
        scAddExtSym(malloc);
        scAddExtSym(free);

  for (i=1;i<argc;i++)
  {if (argv[i][0]!='-')//FileName;
   {
    if (InputFiles+1>MAXINPUTFILENAMES) SCError(-1,"Too many input files!\n");
//    printf("%d:%s\n",InputFiles,argv[i]);
    if (strcmp("/?",argv[i])==0)
      SCError(3,"Invalid file specification `%s'.\n",argv[i]);
    FNInput[InputFiles++]=argv[i];
    if (FLOp==FLRun)
    {
       return Run(argc-i,&argv[i]);
    }
   }
   else
   {//options
    if (strcmp(argv[i],"-s")==0||strcmp(argv[i],"-show")==0 )
      {
       FLShow=1;
      }
      else
    if (strcmp(argv[i],"-d")==0||strcmp(argv[i],"-debug")==0 )
      {
       compiler_debug=1;
      }
      else
    if (strcmp(argv[i],"-r")==0||strcmp(argv[i],"-run")==0 )
      {
       FLOp=FLRun;
       if (InputFiles!=0) SCError(-1,"Script should be specified after -run option!\n");
      }
      else
    if (strcmp(argv[i],"-i")==0||strcmp(argv[i],"-info")==0 )
      {
       FLOp=FLInfo;
      }
      else
    if (strcmp(argv[i],"-v")==0||strcmp(argv[i],"-version")==0)
      {
       ShowName();return 0;
      }
      else
    if (strcmp(argv[i],"-h")==0||strcmp(argv[i],"-help")==0 )
      {ShowName();
       printf("%s",help);
       return 0;
      }
      else
    if (strcmp(argv[i],"-t")==0||strcmp(argv[i],"-title")==0)
      {
        strcpy(scScript_Title,argv[++i]);
      }
      else
    if (strcmp(argv[i],"-a")==0||strcmp(argv[i],"-author")==0)
      {
        strcpy(scScript_Author,argv[++i]);
      }
      else
    if (strcmp(argv[i],"-o")==0)
      {
       FNOutput=strdup(argv[++i]);
      }
      else
    if (strncmp(argv[i],"-O",2)==0)
      {char *c=argv[i]+2;
       while (*c)
       {switch(*c)
        {
         case 'e':scSeeR_Set(scSeeR_ExprOpcode,1);break;
         case 's':StackSize=strtoul(c,&c,10);
                  scSeeR_Set(scSeeR_StackSize,StackSize);break;
         default:
           SCError(5,"Unknown option -O%c.\n",c);
        }
        c++;
       }
      }
      else
      SCError(3,"Unrecognized option `%s'.\n",argv[i]);
   }
  }
 }

 if (!InputFiles) SCError(2,"No input files (-h for help)\n");
 switch (FLOp){
 case FLRun:
//   Run();
   SCError(-1,"Internal error:invalid run\n");
   break;
 case FLInfo:
   Info();
   break;
 case FLCompile:
   Compilation();
   break;
 default:
   SCError(-1,"Internal error:unknown job\n");
 }

 return 0;
}

/*************Worx***************************************/
#define ReadINT(ptr,x) memcpy(&(x),ptr,4)

void Info()
{int i,size,data1;
 scInstance *in;
// FILE *file;
 scScript script;
 for(i=0;i<InputFiles;i++)
 {
//read:
 script=scLoad_Script(FNInput[i]);
 if (!script)
    printf("Invalid input file:%s.\n",FNInput[i]);
 else
 {
//info:
 printf("File:%s\n",FNInput[i]);
 if (*((int*)script)!=CHARS2INT('S','e','e','R'))
    printf("Not a SeeR script!\n");
 else
 if (*((int*)script+1)!=CHARS2INT('V','C','P','U'))
    printf("SeeR script not VCPU!\n");
   else
   {char *au,*ti;
    data1=*((int*)script+2);
    ti=scGet_Title(script);
    au=scGet_Author(script);
    printf("SeeR %d.%d script, \"%s\" by %s\n",data1>>16,data1&0xFFFF,ti,au);
    data1=*((int*)script+hdrSymbolOffset);//symbols
    printf("Data size:%d\n",*((int*)script+hdrDataSize));
    printf("Stack size:%d\n",*((int*)script+hdrStackSize));
    printf(" Symbols:\n");
    {char namer[1000];
     int addr,cfg;
     char *x=(char *)script+data1;
     do{
     strcpy(namer,x);
     if (namer[0])
     {
      x+=strlen(namer)+1;
      ALIGN_INT(x);
      ReadINT(x,addr);x+=4;
      ReadINT(x,cfg);x+=4;
      printf("  %-16s - ",namer);
      if (cfg>=0) printf(" function ");
          else printf(" variable ");
      printf(" at %04x \n",addr);
     }
     } while(namer[0]);
    }

    data1=*((int*)script+hdrImportedOffset);//imports
    printf(" Imports:\n");
    {char namer[1000];
     int addr,cfg;
     char *x=(char *)script+data1;
     do{
     strcpy(namer,x);addr=x-((char *)script+data1);
     if (namer[0])
     {
      x+=strlen(namer)+1;
      ALIGN_INT(x);
      ReadINT(x,cfg);x+=4;
      ReadINT(x,cfg);x+=4;
      printf("  %-16s - ",namer);
      if (cfg>=0) printf(" function ");
          else printf(" variable ");
      printf(" at %04x \n",addr);
     }
     } while(namer[0]);
    }
    printf("\n");
   }//else
  }
 }
}

int Run(int argc,char **argv)
{int i;
 scInstance *in;
 FILE *file;
 scScript script;
 if (InputFiles>1) SCError(5,"Unable to run script for many inputs\n");

//read
 i=STfilesize(FNInput[0]);script=malloc(i+1);
 script[i]=0;
 file=fopen(FNInput[0],"rb");
 if (!file)
    SCError(6,"Unable to open input file.\n");
 fread(script,i,1,file);
 fclose(file);
 if (!scGet_Script_Size(script)) //it may be source
 {char *x=script;
  script=scCompile_Text(x);
  if (scErrorNo)
     SCError(scErrorNo,"\nIn file %s:\n%s\n> %s\n",FNInput[0],scErrorMsg,scErrorLine);
  free(x);
 }

 in=scCreate_Instance(script,"");
 if (!in)
    SCError(scErrorNo,"Runtime error :\n%s\n",scErrorMsg);
 i=scCall_Instance(in,scGet_Symbol(in,"main"),argc,argv);
 scFree_Instance(in);
 free(script);
 if (scErrorNo)
    SCError(scErrorNo,"Runtime error :\n%s\n",scErrorMsg);
  return i;
}

void Compilation()
{FILE *file;
 scScript scrpt;
 int i;
 if (InputFiles>1&&FNOutput) SCError(4,"Single output filename for many inputs\n");

  for(i=0;i<InputFiles;i++)
  {strcpy(scScript_Title,FNInput[i]);
   if (!FNOutput)
    {int j;
     FNOutput=malloc(strlen(FNInput[i])+10);
     for(j=strlen(FNInput[i])-1;j>0&&FNInput[i][j]!='.';j--);
     strcpy(FNOutput,FNInput[i]);
     if (!j) {j=strlen(FNOutput);FNOutput[j]='.';}
     FNOutput[j+1]='c';
     FNOutput[j+2]='s';
     FNOutput[j+3]=0;
    }

   if (compiler_debug)
    {int j;
     DEBOutput=malloc(strlen(FNInput[i])+10);
     for(j=strlen(FNInput[i])-1;j>0&&FNInput[i][j]!='.';j--);
     strcpy(DEBOutput,FNInput[i]);
     if (!j) {j=strlen(DEBOutput);DEBOutput[j]='.';}
     DEBOutput[j+1]='d';
     DEBOutput[j+2]='e';
     DEBOutput[j+3]='b';
     DEBOutput[j+4]=0;
    }

   if (FLShow) {
     ShowName();
     if (compiler_debug)
                printf("Compiling %s to %s (debug:%s)...",FNInput[i],FNOutput,DEBOutput);
       else
                printf("Compiling %s to %s...",FNInput[i],FNOutput);
                fflush(stdin);
               }
   if (compiler_debug) //compiler_debfname=DEBOutput;
   {
    scOpen_Debug(DEBOutput);
    scToggle_Debug(true);
   }

   scrpt=scCompile_File(FNInput[i]);
   if (scErrorNo)
      SCError(scErrorNo,"\nIn file %s:\n%s\n> %s\n",FNInput[i],scErrorMsg,scErrorLine);
   if (!scrpt)
      SCError(5,"\nFile `%s' not found.\n",FNInput[i]);
   if (compiler_debug)
   {
    scClose_Debug();
   }
   file=fopen(FNOutput,"wb");
   fwrite(scrpt,scGet_Script_Size(scrpt),1,file);
   fclose(file);
   free(scrpt);
   if (FLShow) printf("\x8\x8\x8(%d lines).  \rūCompiled \n",scSeeR_Get(scSeeR_LinesCompiled));
   if (InputFiles>1) {
       free(FNOutput);FNOutput=NULL;
       if (compiler_debug) free(DEBOutput);
      }
  }
 }


