
// Copyright (c) 1995-2003 The University of Cincinnati.
// All rights reserved.

// UC MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
// SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
// OR NON-INFRINGEMENT.  UC SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
// LICENSEE AS A RESULT OF USING, RESULT OF USING, MODIFYING OR
// DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.

// By using or copying this Software, Licensee agrees to abide by the
// intellectual property laws, and all other applicable laws of the U.S.,
// and the terms of this license.

// You may modify, distribute, and use the software contained in this
// package under the terms of the "GNU LIBRARY GENERAL PUBLIC LICENSE"
// version 2, June 1991. A copy of this license agreement can be found in
// the file "LGPL", distributed with this archive.

// You may modify, distribute, and use the software contained in this
// package under the terms of the "GNU LIBRARY GENERAL PUBLIC LICENSE"
// version 2, June 1991. A copy of this license agreement can be found in
// the file "LGPL", distributed with this archive.

// Authors: Philip A. Wilsey    philip.wilsey@ieee.org
//          Dale E. Martin	dmartin@cliftonlabs.com
//          Timothy J. McBrayer
//          Narayanan Thondugulam
//          Magnus Danielson      cfmd@swipnet.se

#include <signal.h>
#include "version.hh"
#include "savant.hh"
#include "scram.hh"
#include "IIR_DesignFile.hh"
#include "IIR_DesignFileList.hh"
#include "IIR_LibraryUnit.hh"
#include "IIR_EntityDeclaration.hh"
#include "IIR_ArchitectureDeclaration.hh"
#include "symbol_table.hh"
#include "dl_list.hh"
#include "IIR_EnumerationLiteral.hh"
#include "IIR_Identifier.hh"
#include "language_processing_control.hh"
#include <cctype>
#include <fstream>
#include <clutils/ArgumentParser.h>

// temporary elaboration info stuff
#include "elaborate_info.hh"
elaborate_info elab_info;

// to invoke the library manager
#include "library_manager.hh"

// These are global flags.
bool debug_symbol_table;
bool publish_vhdl;
bool publish_cc;
bool no_file_output;
bool print_warranty;
bool keep_design_units;
bool ignore_standard_library;
bool no_mangling;
bool verbose_output;
bool gen_cc_ref;

// for library manager
string design_library_name;

dl_list<char> lib_list;

#ifdef PROCESS_COMBINATION
bool static_elaborate;
char *entity_name;
char *arch_name;
char *config_name;
char *partition_algorithm;
int num_partitions;

// This method lowercases a null-terminated string by walking through the
// argument and calling tolower on each character.
static void
lowercase(char* string) {
  while (*string != '\0') {
    *string = tolower(*string);
    string++;
  }
}
#endif

#ifdef PROCESS_GRAPH
bool signal_graph;
#endif

// global flag for parse errors.  Parse errors include semantic errors
// like undefined symbols and such
bool parse_error = false;

// This object will record which language the analyzer should be
// configured to recognize.
language_processing_control *lang_proc = NULL;

// If the command line switch to capture comments is turned on, this flag
// will be set to true; otherwise it will be set to false. 
bool *capture_comments = false;

// Right now, the error handling in scram is more or less
// non-existant.  That is to say, if a symbol is undefined then there
// is a very good chance that after reporting the error, it will core
// dump.  The current plan is to implement error handling using C++
// exceptions.  However, until then, we'll just catch seg faults, and
// check to see if the global parse_error flag is set.  If so, we'll
// just exit - otherwise, we'll let the process core dump.
void 
catch_core_dumps( int ){
  if( parse_error == true ){
    exit(-1);
  }
  else{
    signal( SIGSEGV, SIG_DFL );  
  }
}

void 
help_func(){
    cerr << SAVANT_VERSION << "\n\n";
    cerr <<"THE UNIVERSITY OF CINCINNATI (UC) MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT\nTHE SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT\nNOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE, OR NON-INFRINGEMENT.  UC SHALL NOT BE LIABLE FOR ANY\nDAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING RESULT OF USING, MODIFYING\nOR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.\n";
    cerr << "\nReport problems, comments, etc. to \"savant@ececs.uc.edu\".\n\n";
}

#if defined(__GNUG__)
#define IOS_BIN ios::bin
#else
#define IOS_BIN ios::binary
#endif

#ifdef DEVELOPER_ASSERTIONS
#define _CC_OUT _cc_out
#else
#define _CC_OUT IIRScram::_cc_out
#endif	  

int 
main (int argc, char *argv[]) {
  signal( SIGSEGV, catch_core_dumps );

  // here are defaults for global argument variables
  debug_symbol_table      = false;
  publish_vhdl            = false;
  publish_cc              = false;
  no_file_output          = false;
  print_warranty          = false;
  keep_design_units       = false;
  ignore_standard_library = false;
  no_mangling             = false;
  verbose_output          = false;
  gen_cc_ref              = false;

  // true if command line argument --version is present
  bool print_version      = false;

  // these variables are used by the arg_parser to record command line
  // arguments.  DO NOT reference them, use the lang_processing object
  // to discover information for language processing
  bool vhdl_93  = false;
  bool vhdl_ams = false;
  bool vhdl_2001 = false;
  // Again, DO NOT reference the above named variables for anything more
  // than configuring the language_processing_control object.
  
  static ArgumentParser::ArgRecord arg_list[] = {
    {"--debug-symbol-table","print out debugging info relating to symbol table", &debug_symbol_table, ArgumentParser::BOOLEAN},
    {"--debug-gen-cc-ref","make code gen. and VHDL line references in c++ code", &gen_cc_ref, ArgumentParser::BOOLEAN},  
    {"--design-library-name", "design library name", &design_library_name, ArgumentParser::STRING},
    {"--capture-comments","capture comments and store them in the design file IIR node", &capture_comments, ArgumentParser::BOOLEAN},
    {"--publish-vhdl","publish VHDL", &publish_vhdl, ArgumentParser::BOOLEAN},
    {"--publish-cc","publish c++", &publish_cc, ArgumentParser::BOOLEAN},
    {"--no-file-output", "send publish_cc output to stdout instead of files", &no_file_output, ArgumentParser::BOOLEAN},
    {"--warranty-info", "print information about (lack of) warranty", &print_warranty, ArgumentParser::BOOLEAN},
    {"--vhdl-93", "setup the analyzer to process the VHDL 93 language standard (default)", &vhdl_93, ArgumentParser::BOOLEAN},
    {"--vhdl-ams", "setup the analyzer to process the VHDL AMS language standard", &vhdl_ams, ArgumentParser::BOOLEAN},
    {"--vhdl-2001", "setup the analyzer to process the VHDL 2001 language standard", &vhdl_2001, ArgumentParser::BOOLEAN},
    {"--version", "print version number and exit.", &print_version, ArgumentParser::BOOLEAN },
    {"--verbose", "verbose output", &verbose_output, ArgumentParser::BOOLEAN },
    {"","", 0}
  };

  ArgumentParser ap( arg_list ); // , help_func );
  vector<string> argVec = ArgumentParser::vectorifyArguments( argc, argv, true );
  ap.checkArgs( argVec );

  if (print_version) {
    cerr << SAVANT_VERSION_NUMBER << "\n";
    exit(-1);
  }

  if( argc <= 1 ){
//     ap.print_usage( argv[0] );
    exit( -1 );
  }

  // Invoke the language processing object.  If no other languages are
  // selected, then recognize VHDL 93.
  if (vhdl_ams) {
    lang_proc = new language_processing_control(language_processing_control::VHDL_AMS);
  } else if (vhdl_2001) {
    lang_proc = new language_processing_control(language_processing_control::VHDL_2001);
  } else {
    lang_proc = new language_processing_control(language_processing_control::VHDL_93);
  }

  if (lib_list.num_elements() != 0) {
     cerr << "Libraries specified: ";
     char* lib = lib_list.first();
     while (lib != NULL) {
        cerr << lib << " ";
        lib = lib_list.successor(lib);
     }
     cerr << "\n";
  }
    
  if( print_warranty == true ){
    help_func();
    exit( -1 );
  }

  IIR_DesignFileList *iir_design_files_processed = NULL;    
  string work_lib_name = "work";
  if( design_library_name != "" ){
    work_lib_name = design_library_name;
  }  
  scram parser( true, work_lib_name );

  if(argc > 1) {
    iir_design_files_processed = parser.parse_files( argVec );  

    if( parse_error == FALSE ){
      cerr << "Parse complete - no errors." << endl;
    }
    else{
      exit( -1 );
    }
  }
  
  if( iir_design_files_processed != NULL ){
    // Now we'll walk the list, and publish if we're supposed to.
    // stream for the publishing vhdl
    IIR_DesignFile *to_publish = iir_design_files_processed->first();
    while( to_publish != NULL ){
#ifdef PROCESS_COMBINATION
      IIR_ArchitectureDeclaration *elaborated_design = NULL;
      if( static_elaborate == true ) {
	cerr << "Starting elaboration of specified design...\n";
	elaborated_design = (IIR_ArchitectureDeclaration*)to_publish->_static_elaborate_design();
	if (elaborated_design == NULL) {
	  cerr << "Specified design not found in " 
	       << (to_publish->get_file_name()) << "\n";
	  to_publish = iir_design_files_processed->successor( to_publish );
	  continue;
	}
	ASSERT(elaborated_design->get_kind() == IIR_ARCHITECTURE_DECLARATION);
	cerr << "Elaboration complete.\n";

	elaborated_design->_combine();
	cerr << "Process combination complete.\n";
      }
#endif
       if (publish_vhdl == true) {
	 to_publish->_publish_vhdl( cout );
	 cout.flush();
       }
	
      if (publish_cc == true) {
	cerr << "Starting C++ code generation..." << endl;
	if( iir_design_files_processed->successor( to_publish ) == NULL ){
	  to_publish->_publish_cc( true );
	}
	else{
	  to_publish->_publish_cc( false );
	}
	cerr << "Code generation finished." << endl;
      }

      to_publish = iir_design_files_processed->successor( to_publish );
    }
  }
  return 0;
}

