/*
 * MAIN program for BIST generation application
 *
 * @file main.c
 * @date 2017
 * @author Lukas Vykydal
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <tcl.h>
#include <readline.h>
#include <history.h>
#include <unistd.h>
#include <getopt.h>  //unistd.h should be sufficiant  
#include "microcode.h"
#include "lfsr.h"
#include "ncnt.h"
#include "config_lvlib.h"

Tcl_Interp *tcl;

static char* line_read = (char*) NULL;
/**
 * @brief Read one line from STDIN using readline library 
 * @return Line from STDIN
 */
char* GetLine(){
  if(line_read != NULL) {
    free(line_read);
    line_read = NULL;
  }
  
  line_read = readline(">");
  
  if(line_read && *line_read)
    add_history(line_read);
  return line_read;
}

/**
 * @brief MAIN
 */
int main(int argc, char** argv){

  //Initialize TCL
  Tcl_FindExecutable(argv[0]);
  tcl = Tcl_CreateInterp();

  if(Tcl_Init(tcl) != TCL_OK ){
    printf("Error while initialization of TCL");
    exit(1);
  }

  Microcode_TclRegister(tcl);
	LFSR_TclRegister(tcl);
	Ncnt_TclRegister(tcl);
	Config_lvlib_TclRegister(tcl);

  //Set basic variables
  // - argv0 should contain name of interpreter
  // - argc should contain count of arguments, but C counts executable name as argument
  // - argv list of all arguments except executable name
  Tcl_ObjSetVar2(tcl, Tcl_NewStringObj("::argv0", -1), NULL, Tcl_NewStringObj(argv[0], -1), 0);
  Tcl_ObjSetVar2(tcl, Tcl_NewStringObj("::argc", -1), NULL, Tcl_NewIntObj(argc-1), 0);
  Tcl_ObjSetVar2(tcl, Tcl_NewStringObj("::argv", -1), NULL, Tcl_NewStringObj("", -1), TCL_LIST_ELEMENT); //TO make sure that $argv exists
  for (int i=1; i < argc; i++){
    Tcl_ObjSetVar2(tcl, Tcl_NewStringObj("::argv", -1), NULL, Tcl_NewStringObj(argv[i], -1),
        (i != 1) ? TCL_LIST_ELEMENT | TCL_APPEND_VALUE : 0); //First argument will replace whole variable -> no dummy entry at start
  }

  // Don't report unknown options
  extern int opterr; 
  opterr = 0;

  //Load bootstrap.tcl with basic commands
	if(Tcl_EvalEx(tcl, "source bootstrap.tcl", -1, TCL_EVAL_DIRECT) != TCL_OK){
    printf("Error %d evaluation of bootstrap.tcl\n", Tcl_GetErrno());
    printf("\t %s\n", Tcl_ErrnoMsg(Tcl_GetErrno()));
    return 1;
	}

  // Parse arguments
  char opt;
  int done_file = 0;
  while((opt = getopt(argc, argv, "-d:hs")) != -1){
    switch(opt){
      case 's':
        optarg = "simple.tcl";
        //Deliberately omit break; to pass to 'd' option.
        // We force specified file to be executed
      case 'd':
        // Test if we can read file
        if(access(optarg, R_OK) == -1){
          printf("Can't open file %s\n", optarg);
          exit(1);
        }
        // Format command for Tcl to run this script
        char * cmd = malloc((strlen(optarg) + 8)*sizeof(char));
        sprintf(cmd, "source %s", optarg);
        // Run it!
    	  if(Tcl_EvalEx(tcl, cmd, -1, TCL_EVAL_DIRECT) != TCL_OK){
          printf("Error %d evaluation of %s\n", Tcl_GetErrno(), optarg);
          printf("\t %s\n", Tcl_ErrnoMsg(Tcl_GetErrno()));
			    Tcl_EvalEx(tcl, "set errorInfo?", -1, TCL_EVAL_DIRECT);
          free(cmd);
          return 1;
        }
        free(cmd);
        done_file = 1;
        break;
      case 'h':
        printf("Usage: %s [-d filename.tcl] [-h] [-s]\n", argv[0]);
        printf("\t-d filename.tcl - run TCL script after start\n");
        printf("\t-h              - display this help\n");
        printf("\t-s              - run simple project\n");
        exit(1); 
    }
  }

  if(done_file == 0){
    // Do into interactive mode
    char* line;
    while((line = GetLine()) != NULL){
      if(Tcl_EvalEx(tcl, line, strlen(line), TCL_EVAL_DIRECT) != TCL_OK && Tcl_GetErrno()) {
        printf("Error %d evaluation of TCL command \"%s\"\n", Tcl_GetErrno(), line);
        printf("\t %s\n", Tcl_ErrnoMsg(Tcl_GetErrno()));
  			Tcl_EvalEx(tcl, "set errorInfo", -1, TCL_EVAL_DIRECT);
      }
  /*    else
      {*/
        const char* result = Tcl_GetStringResult(tcl);
        if( result && *result)
          printf("%s\n", result);
  //    }
    }
  }  

  //Destroy TCL
  Tcl_Finalize();

  return 0;
}
