/****************************************************************************
    Copyright (C) 1987-2005 by Jeffery P. Hansen

    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 of the License, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
****************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdarg.h>
#include <string.h>
#include "gsim.h"
#include "ycmalloc.h"

extern SHash module_table;

int yyparse();
void BeginVR();

#ifndef yyrestart
void yyrestart(FILE*);
#endif

char *topModule = 0;
int do_prompt = 0;
int do_commandEcho = 0;

int errCount = 0;

int total_area = 0;
int total_staticPower = 0;

char *base_directory = 0;

/*
 * These undoable object functions are only used in the main tkgate code, but
 * since they are used in the common hash table code we provide stubs here for
 * the hash table code to use.
 */
void *ob_malloc(int s,char *x) { return malloc(s); }
void *ob_calloc(int n,int s,char *x) { return calloc(n,s); }
void ob_free(void *p) { free(p); }
char *ob_strdup(char *s) { return strdup(s); }
void ob_set_type(void *o,char *n) {}
void ob_touch(void *o) {}

void sendMsg(char *fmt,...)
{
  va_list ap;
  char buf[1024],*p;
  int c,l;

  va_start(ap,fmt);
  vsprintf(buf,fmt,ap);
  va_end(ap);

  l = strlen(buf);
  strcpy(buf+l,"\n");
  l++;

  p = buf;
  while (l > 0) {
    c = write(1,p,l);
    if (c > 0) {
      p += c;
      l -= c;
    }
    if (l != 0) {
      fd_set ws;

      FD_ZERO(&ws);
      FD_SET(1,&ws);
      select(2,0,&ws,0,0);
    }
  }

}

void errorGate(char *gat,char *fmt,...)
{
  char buf[STRMAX],*p;
  va_list ap;

  errCount++;
  va_start(ap,fmt);
  p = buf + sprintf(buf,"error gate %s ",gat);
  vsprintf(p,fmt,ap);
  va_end(ap);

  sendMsg("%s",buf);
}

void errorNet(char *net,char *fmt,...)
{
  char buf[STRMAX],*p;
  va_list ap;

  errCount++;
  va_start(ap,fmt);
  p = buf + sprintf(buf,"error net %s ",net);
  vsprintf(p,fmt,ap);
  va_end(ap);

  sendMsg("%s",buf);
}

void errorFile(char *fmt,...)
{
  char buf[STRMAX],*p;
  va_list ap;

  errCount++;
  va_start(ap,fmt);
  p = buf + sprintf(buf,"error file ");
  vsprintf(p,fmt,ap);
  va_end(ap);

  sendMsg("%s",buf);
}

void error(char *fmt,...)
{
  char buf[STRMAX],*p;
  va_list ap;

  errCount++;
  va_start(ap,fmt);
  p = buf + sprintf(buf,"simerror ");
  vsprintf(p,fmt,ap);
  va_end(ap);

  sendMsg("%s",buf);
}

/*
 * Open a file in the context of the current circuit.
 * If the first character of 'name' is '/', then the
 * file is opened "as is".  Otherwise the following
 * path is searched:
 *
 *    current directory
 *    The base_directory spoecified by -B option
 *    user's home directory (value of HOME)
 */
FILE *openInPath(const char *name)
{
  char buf[STRMAX];
  FILE *f;
  char *r;

  if ((f = fopen(name,"r"))) return f;
  if (*name == '/') return 0;

  if ((r = base_directory)) {
    strcpy(buf,r);
    if ((r = strrchr(buf,'/'))) {
      if (r != buf) {
	strcpy(r+1,name);
	if ((f = fopen(buf,"r"))) return f;
      }
    }
  }

  if ((r = getenv("HOME"))) {
    strcpy(buf,r);
    strcat(buf,"/");
    strcat(buf,name);
    if ((f = fopen(buf,"r"))) return f;
  }

  return 0;
}

void waitForExit()
{
  char buf[STRMAX];

  sendMsg("error_exit");

  for (;;) {
    if (!fgets(buf,STRMAX,stdout)) break;
    if (strncmp(buf,"exit",4) == 0)
      break;
  }
  exit(0);
}

static void usage()
{
  errorFile("Usage: gsim [-t name][file]");
  if (errCount) {
    waitForExit();
  }
}


int VerilogLoad(char *name)
{
  FILE *f;

  if (!(f = fopen(name,"r"))) {
    return -1;
  }

  ycFileName = name;
  ycLineNumber = 1;
  yc_setup();
  BeginVR();
  yyrestart(f);
  yc_pushpool();
  yyparse();
  yc_poppool();
  fclose(f);

  return 0;
}

void InitTables()
{
  SHash_init(&module_table);

  init_gates();
}


int main(int argc,char *argv[])
{
  extern char *optarg;
  extern int optind;
#if OPTRESET
  extern int optreset;
#endif
  int c;
  int do_print = 0;
  int do_expand = 0;
  int do_test = 0;
  int do_analysisMode = 0;
  char *delay_file = 0;
  char *load_file = 0;
  char *p;

  sendMsg("comment GSim V2.2 - Digital Circuit Simulator");
  sendMsg("comment %s",TKGATE_COPYRIGHT);

  while (argc > 0) {
    while ((c = getopt(argc,argv,"acepPt:lB:D:")) != EOF) {
      switch (c) {
      case 'a' :
	do_analysisMode = 1;
	break;
      case 'l' :
	do_test = 1;
	break;
      case 'P' :
	do_prompt = 1;
	break;
      case 'p' :
	do_print = 1;
	break;
      case 'e' :
	do_expand = 1;
	break;
      case 'c' :
	do_commandEcho = 1;
	break;
      case 'B' :
	base_directory = optarg;
	break;
      case 't' :
	topModule = optarg; 
	break;
      case 'D' :
	delay_file = optarg; 
	break;
      default :
	usage();
	break;
      }
    }
    argc -= optind;
    argv += optind;
#if OPTRESET
    optreset = 1;
#endif
    optind = 0;
    if (argc > 0) {
      load_file = argv[0];
      argc--;
      argv++;
    }
  }

  if (do_test) {
    char buf[1024];

    while (fgets(buf,1024,stdin)) {
      if (do_commandEcho) 
	printf(">>> %s",buf);
      testLogic(buf);
    }
    exit(0);
  }

  if (!load_file) {
    errorFile("No circuit.");
    waitForExit();
  }

  if (!delay_file) {
    errorFile("No delay file.");
    waitForExit();
  }

  for (p = strtok(delay_file,":");p;p = strtok(0,":")) {
    if (GDelayDef_readFile(p) < 0)
      errorFile("Can not open delay file '%s'.",delay_file);
    if (errCount) waitForExit();
  }

  InitTables();

  if (VerilogLoad(load_file) < 0) {
    errorFile("Can not open circuit file '%s'.",load_file);
    waitForExit();
  }

  if (errCount) {
    waitForExit();
  }

    

  if (!topModule) {
    errorFile("No top-level module.");
    exit(0);
  }


  if (do_print) {
    if (do_expand) {
      SModule *M = (SModule*) SHash_find(&module_table,topModule);
      if (!M) {
	errorFile("No module to print.");
	exit(0);
      }
      SModule_expand(M);
      SModule_finalize(M);
      SModule_print(M,stdout);
    } else {
      VerilogDump();
    }
  } else {
    EvQueue Q;
    SModule *M;

    M = (SModule*) SHash_find(&module_table,topModule);
    if (!M) {
      errorFile("No module to simulate.");
      exit(0);
    }
    SModule_expand(M);
    SModule_finalize(M);
    SModule_check(M);
    if (errCount) {
      waitForExit();
    } else {
      sendMsg("stats area=%d static_power=%d",total_area,total_staticPower);
      sendMsg("ok");
      EvQueue_init(&Q,M);
      if (!do_analysisMode)
	SModule_init(M,&Q);
      EvQueue_mainEventLoop(&Q);
    }
  }
  return 0;
}
