#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <map>
using namespace std;
#include "nwcc.h"
#include "nwcclex.h"


// lexical analyzer             (C) H.Niwa 1993-2009

/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */


#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <sys/time.h>
#include <libgen.h>
#include <setjmp.h>

#include "syserr.h"

#include "bin_node.h"
#include "gc.h"
#include "var.h"
#include "pred.h"
#include "module.h"
#include "context.h"
#include "unify.h"
#include "builtin.h"
#include "sysmodule.h"

extern int pllexSpace (int c);
extern int pllexCkSpace (int c);

char TOKEN[TOKEN_LEN];
int tokenPt = 0;
int Char;

int ErrorFlg = 0;
FILE *RdFp, *pr;
char *MESG = NULL;

int EndFlg = 0;

#if 0
char OutputFileName[MAXPATHLEN + 1];

extern char *InputSuffics;
extern char *OutputSuffics;
#endif

int pargc;
char **pargv;

int
main (int argc, char **argv)
{
  int c, i;

#ifdef __CYGWIN__
  extern std::string code;

  code = "SJIS";

  char inputrcpath[MAXPATHLEN + 10];

  // set INPUTRC path for readline
  char *homepath = getenv ("HOME");
  strncpy (inputrcpath, homepath, MAXPATHLEN);
  strcat (inputrcpath, "/inputrc");
  setenv ("INPUTRC", inputrcpath, 0);
//printf("inputrcpath %s \n", inputrcpath);
#endif /* __CYGWIN__ */

#ifdef __MINGW32__
  extern std::string code;

  code = "SJIS";

  struct timeval tv;
  gettimeofday (&tv, NULL);
  srand (tv.tv_usec);
#else
  struct timeval tv;
  gettimeofday (&tv, NULL);
  srandom (tv.tv_usec);
#endif /* __MINGW32__ */

  extern void StackBase ();
  StackBase ();

  GetLibPath ();
  GetTmpPath ();
  GetEditorPath ();

  if (argc <= 1)
    {
      DResultFlag = 1;

      printf ("Descartes %s ", "0.3.0");
      printf ("Copyright (C) 2009 Hideyuki Niwa\n\n");

      GetlineEval ();

      exit (0);

    }
  else
    {

      char *dargv1 = strdup (argv[1]);
      char *bargv1 = strdup (argv[1]);
      char *dname = dirname (dargv1);
      char *bname = basename (bargv1);
      free (dargv1);
      free (bargv1);

//printf("fopen 0 argv[1] %s dname %s bname %s \n", argv[1], dname, bname);
      if ((dname[0] == '.') && (dname[1] == 0) && ((argv[1])[1] != '/'))
	{
	  Node *nd;
	  RdFp = NULL;

//PrintNode("dlibpathnode ", dlibpathnode);
	  if (dlibpathnode == Nil)
	    {
//printf("fopen 1 argv[1] %s \n", argv[1]);
	      RdFp = fopen (argv[1], "rb");
	    }
	  else
	    {
	      for (nd = dlibpathnode; nd != Nil; nd = nd->Cdr ())
		{
		  std::string s = "";

		  if (nd->Car ()->kind () != ATOM)
		    {
		      continue;
		    }

		  ((Atom *) (nd->Car ()))->toString (s);
		  s = s + "/" + argv[1];

		  RdFp = fopen (s.c_str (), "rb");
		  if (RdFp != NULL)
		    {
		      break;
		    }
		}
	    }
	}
      else
	{
//printf("fopen 2 argv[1] %s \n", argv[1]);
	  RdFp = fopen (argv[1], "rb");
	}

      if (RdFp == NULL)
	{
	  fprintf (stderr, "can not open File : %s \n", argv[1]);
	  return 0;
	}

      pargc = argc;
      pargv = argv;

      NwccMain ();

      fclose (RdFp);

//              fflush(stdout); PPmodule();

      exit (0);
    }
}


long
FilePointSave ()
{
  return ftell (RdFp);
}

void
FilePointResume (long tellpt)
{
  fseek (RdFp, tellpt, 0);
}

void
SkipComment (int &Char)
{
  int c;

  for (;;)
    {
      // #! comment
      if (Char == '#')
	{
	  c = fgetc (RdFp);
	  if (c == '!')
	    {
	      for (;;)
		{
		  c = fgetc (RdFp);
		  if (c == '\n')
		    {
		      break;
		    }
		}
	      Char = fgetc (RdFp);
	    }
	  else
	    {
	      ungetc (c, RdFp);
	      return;
	    }
	  // '//' and '/* */' comment
	}
      else if (Char == '/')
	{
	  c = fgetc (RdFp);
	  if (c == '/')
	    {			// comment
	      for (;;)
		{
		  c = fgetc (RdFp);
		  if (c == '\n')
		    {
		      break;
		    }
		}
	      Char = fgetc (RdFp);
	    }
	  else if (c == '*')
	    {			/* comment */
	      for (;;)
		{
		  c = fgetc (RdFp);
		  if (c == '*')
		    {		/* close comment */
		      c = fgetc (RdFp);
		      if (c == '/')
			{
			  break;
			}
		      else
			{
			  ungetc (c, RdFp);
			}
		    }
		  else if (c == '/')
		    {
		      if ((c = fgetc (RdFp)) == '*')
			{
			  ungetc ('*', RdFp);
			  Char = '/';
			  SkipComment (Char);
			}
		    }
		}
	      Char = fgetc (RdFp);
	    }
	  else
	    {
	      ungetc (c, RdFp);
	      break;
	    }
	}
      else
	{
	  return;
	}
    }
}

int
GetChar ()
{
  Char = fgetc (RdFp);

  SkipComment (Char);

  return Char;
}

void
UnGetChar (int c)
{
  ungetc (c, RdFp);
}

void
PutChar (int Char)
{
  if (fputc (Char, pr) == EOF)
    {
      ErrorStop ("can't write file ");
    }
}


void
SkipSpace ()
{
  int c;

  do
    {
      c = GetChar ();
    }
  while (pllexSpace (c));
  UnGetChar (c);

}

int
CHECK (char *Pt)
{
  long TelSav;
  int r;

  TelSav = FilePointSave ();
  r = COMP (Pt);
  FilePointResume (TelSav);
  return r;
}


int
NotSkipSpCOMP (char *Pt)
{
  long TelSav;
  int c;

  TelSav = FilePointSave ();

  c = GetChar ();
  if ((char) c != *Pt)
    {
      FilePointResume (TelSav);
      return -1;
    }
  Pt++;
  while (*Pt)
    {
      c = GetChar ();
      if ((char) Char != *Pt)
	{
	  FilePointResume (TelSav);
	  return -1;
	}
      Pt++;
    }
  return 0;
}

int
COMP (char *Pt)
{
  long TelSav;
  int r, c;

  TelSav = FilePointSave ();

  SkipSpace ();

  r = NotSkipSpCOMP (Pt);

  if (r != 0)
    {
      FilePointResume (TelSav);
      return r;
    }
  else
    {
      fseek (RdFp, ftell (RdFp) - 1, 0);
      if (!isalpha (GetChar ()))
	{
	  return 0;
	}

      c = GetChar ();
      UnGetChar (c);

      if (isalpha (c) || (c == '_'))
	{
	  FilePointResume (TelSav);
	  return -1;
	}

      return 0;
    }
}

int
LineNumber ()
{
  int LineNo = 1;
  long TelSav;
  int c;

  TelSav = FilePointSave ();
  rewind (RdFp);
  while (TelSav != FilePointSave ())
    {
      c = fgetc (RdFp);
      if (c == '\n')
	{
	  LineNo++;
	}
      if (c == EOF)
	break;
    }
  FilePointResume (TelSav);
  return LineNo;
}

static char errorbuf[1024];
int ErrorStopFlag = 0;

void
ErrorStop (char *fmt, ...)
{
  va_list argptr;
  int LineNo = 1;
  long TelSav;

  if (ErrorStopFlag)
    {
      return;
    }

  LineNo = LineNumber ();

  errorbuf[0] = '\0';

  va_start (argptr, fmt);
  vsprintf (errorbuf, fmt, argptr);
  va_end (argptr);

  if (strlen (errorbuf) == 0)
    {
      strncpy (errorbuf, "Syntax Error ", 1023);
    }

  fprintf (stderr, "# %d : error... %s\n", LineNo, errorbuf);

//      fclose(pr);
  ErrorFlg = -1;
  ErrorStopFlag = 1;

  extern jmp_buf program_jb;

  longjmp (program_jb, -1);

  exit (-1);
}

void
UNTIL (char *s)
{
  char *Pt;
  int c;
  int matchFlg = 0;
  long tellsave;

  while (matchFlg == 0)
    {
      Pt = s;
      while ((char) (c = GetChar ()) != *Pt)
	{
	  PutChar (c);
	}

      matchFlg = 1;
      Pt++;

      tellsave = FilePointSave () - 1;
      while (*Pt)
	{
	  c = GetChar ();
	  if ((char) c != *Pt)
	    {
	      FilePointResume (tellsave);
	      c = GetChar ();
	      PutChar (c);
	      matchFlg = 0;
	      break;
	    }
	  Pt++;
	}
    }
}

void
SKIP (char *s)
{
  char *Pt;
  int c;
  int matchFlg = 0;

  while (matchFlg == 0)
    {
      Pt = s;
      while ((char) (c = GetChar ()) != *Pt)
	;

      matchFlg = 1;
      Pt++;
      while (*Pt)
	{
	  c = GetChar ();
	  if ((char) c != *Pt)
	    {
	      matchFlg = 0;
	      break;
	    }
	  Pt++;
	}
    }
}


int
NotSkipSpNOT (char *s)
{
  long TelSav;
  int rc;

  TelSav = FilePointSave ();
  rc = !NotSkipSpCOMP (s);
  FilePointResume (TelSav);
  return rc;
}

int
NOT (char *s)
{
  long TelSav;
  int rc;

  TelSav = FilePointSave ();
  rc = !COMP (s);
  FilePointResume (TelSav);
  return rc;
}

int
C ()
{
  Char = GetChar ();

  return 0;
}


int
Eof ()
{
  int c;

  do
    {
      c = fgetc (RdFp);
      if (c == EOF)
	{
	  return 0;
	}
    }
  while (pllexSpace (c));
  ungetc (c, RdFp);

  return -1;
}

int
NotEof ()
{
  int c;

  do
    {
      c = fgetc (RdFp);
      if (c == EOF)
	{
	  return -1;
	}
    }
  while (pllexSpace (c));
  ungetc (c, RdFp);

  return 0;
}


int
N ()
{
  C ();

  if (!isdigit (Char))
    {
      UnGetChar (Char);
      return -1;
    }

  return 0;
}

int
A ()
{
  C ();

  if (!isalpha (Char) && (Char != '_'))
    {
      UnGetChar (Char);
      return -1;
    }

  return 0;
}

int
AN ()
{
  C ();

  switch (Char)
    {
    case '_':
      return -1;
    }

  if (!isalnum (Char))
    {
      return 0;
    }

  UnGetChar (Char);
  return -1;
}

int
SPACE ()
{
  C ();

  if (!pllexSpace (Char))
    {
      UnGetChar (Char);
      return -1;
    }

  return 0;
}

void
SetToken (int c)
{
  if (tokenPt >= TOKEN_LEN)
    {
      ErrorStop ("Too long token ");
    }

  if (c & 0xff00)
    {
      TOKEN[tokenPt] = (c >> 8);
      tokenPt++;
      TOKEN[tokenPt] = (c & 0xff);
      tokenPt++;
    }
  else
    {
      TOKEN[tokenPt] = c;
      tokenPt++;
    }
}




int
FNUM ()
{
  int dot = 0;

  SkipSpace ();

  tokenPt = 0;

  C ();
  if ((!isdigit (Char)) && (Char != '.'))
    {
      UnGetChar (Char);
      return -1;
    }
  if (Char == '.')
    {
      dot++;
    }
  SetToken (Char);

  for (;;)
    {
      C ();
      if (Char == '.')
	{
	  if (dot)
	    {
	      UnGetChar (Char);
	      return -1;
	    }
	  dot++;
	  SetToken (Char);
	}
      else if ((Char == 'e') || (Char == 'E'))
	{
	  SetToken (Char);
	  C ();
	  if ((Char == '+') || (Char == '-'))
	    {
	      SetToken (Char);
	      for (;;)
		{
		  C ();
		  if (!isdigit (Char))
		    {
		      UnGetChar (Char);
		      SetToken ('\0');
		      return 0;
		    }
		  SetToken (Char);
		}
	    }
	  else
	    {
	      UnGetChar (Char);
	      return -1;
	    }
	}
      else if (!isdigit (Char))
	{
	  UnGetChar (Char);
	  SetToken ('\0');
	  return 0;
	}
      else
	{
	  SetToken (Char);
	}
    }

  return 0;
}

int
STRINGS ()
{
  char qt;

  SkipSpace ();

  tokenPt = 0;

  C ();
  if ((Char != '\"') && (Char != '\''))
    {
      UnGetChar (Char);
      return -1;
    }
  qt = Char;
//      SetToken(Char);

  for (;;)
    {
      C ();
      if (Char == qt)
	{
//                      SetToken(Char);
	  SetToken (0);
	  return 0;
	}
      else
	{
	  SetToken (Char);
	}
    }

  SetToken (0);
  return -1;
}




int
ANY ()
{
  ErrorFlg = 0;
  MESG = NULL;
  do
    {
      long chktell1, chktell2;
      chktell1 = FilePointSave ();
      int AN ();
      ErrorFlg = AN ();
      MESG = "AN is missing ";
      if (ErrorFlg)
	{
	}
      if (!ErrorFlg)
	{
	}
      chktell2 = FilePointSave ();
      if ((chktell1 == chktell2) && !ErrorFlg)
	ErrorStop ("endless loop occured");
    }
  while (!ErrorFlg);
  ErrorFlg = 0;
  MESG = NULL;
  if (!ErrorFlg)
    {
    }
  return ErrorFlg;
}

int
WRD ()
{
  {
    long tellsave;
    tokenPt = 0;
    tellsave = FilePointSave ();
    SkipSpace ();

    ErrorFlg = 0;
    MESG = NULL;
    int AN ();
    ErrorFlg = AN ();
    MESG = "AN is missing ";
    if (ErrorFlg)
      {
	{
	  FilePointResume (tellsave);
	}
      }
    if (!ErrorFlg)
      {
	SetToken (Char);
	do
	  {
	    long chktell1, chktell2;
	    chktell1 = FilePointSave ();
	    int AN ();
	    ErrorFlg = AN ();
	    MESG = "AN is missing ";
	    if (ErrorFlg)
	      {
	      }
	    if (!ErrorFlg)
	      {
		SetToken (Char);
	      }
	    chktell2 = FilePointSave ();
	    if ((chktell1 == chktell2) && !ErrorFlg)
	      ErrorStop ("endless loop occured");
	  }
	while (!ErrorFlg);
	ErrorFlg = 0;
	MESG = NULL;
	if (ErrorFlg)
	  ErrorStop (MESG);

	SetToken ('\0');
      }

  }
  return ErrorFlg;
}

int
NUM ()
{
  {
    long tellsave;
    tokenPt = 0;
    tellsave = FilePointSave ();
    SkipSpace ();
    ErrorFlg = 0;
    MESG = NULL;
    int N ();
    ErrorFlg = N ();
    MESG = "N is missing ";
    if (ErrorFlg)
      {
	{
	  FilePointResume (tellsave);
	}
      }
    if (!ErrorFlg)
      {
	SetToken (Char);
	do
	  {
	    long chktell1, chktell2;
	    chktell1 = FilePointSave ();
	    int N ();
	    ErrorFlg = N ();
	    MESG = "N is missing ";
	    if (ErrorFlg)
	      {
	      }
	    if (!ErrorFlg)
	      {
		SetToken (Char);
	      }
	    chktell2 = FilePointSave ();
	    if ((chktell1 == chktell2) && !ErrorFlg)
	      ErrorStop ("endless loop occured");
	  }
	while (!ErrorFlg);
	ErrorFlg = 0;
	MESG = NULL;
	if (ErrorFlg)
	  ErrorStop (MESG);
	SetToken ('\0');

      }

  }
  return ErrorFlg;
}

int
ID ()
{
  {
    long tellsave;
    tokenPt = 0;
    tellsave = FilePointSave ();
    SkipSpace ();
    ErrorFlg = 0;
    MESG = NULL;
    int A ();
    ErrorFlg = A ();
    MESG = "A is missing ";
    if (ErrorFlg)
      {
	{
	  FilePointResume (tellsave);
	}
      }
    if (!ErrorFlg)
      {
	SetToken (Char);
	do
	  {
	    long chktell1, chktell2;
	    chktell1 = FilePointSave ();
	    int AN ();
	    ErrorFlg = AN ();
	    MESG = "AN is missing ";
	    if (ErrorFlg)
	      {
	      }
	    if (!ErrorFlg)
	      {
		SetToken (Char);
	      }
	    chktell2 = FilePointSave ();
	    if ((chktell1 == chktell2) && !ErrorFlg)
	      ErrorStop ("endless loop occured");
	  }
	while (!ErrorFlg);
	ErrorFlg = 0;
	MESG = NULL;
	if (ErrorFlg)
	  ErrorStop (MESG);
	SetToken ('\0');

      }

  }
  return ErrorFlg;
}
