/*
   Copyright (C) 1998,1999,2000 T. Scott Dattalo

This file is part of gpsim.

gpsim 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.

gpsim 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 gpsim; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */


#include "modules.h"

#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <string>
#include <list>
#include <vector>

#include "../config.h"

#ifndef _WIN32
#include <dlfcn.h>
#if !defined(_MAX_PATH)
  #define _MAX_PATH 1024
#endif
#include <unistd.h>  // for getcwd 

#else
#include <direct.h>
#include <windows.h>
/*
 * interface is a Module class member variable in gpsim,
 * in WIN32 Platform SDK it is a macro, defined in BaseTypes.h
 * the WIN32 Platform SDK definition should be undefined
 */
#undef interface
#endif

#include "pic-processor.h"
#include "stimuli.h"
#include "stimulus_orb.h"
#include "symbol.h"
#include "xref.h"
#include "value.h"
#include "packages.h"
#include "cmd_manager.h"


int ModuleLibrary::m_iSequenceNumber = 0;
// ModuleLibrary *ModuleLibrary::m_pLibrary = new ModuleLibrary();
ModuleLibrary::FileList   ModuleLibrary::m_FileList;
ModuleLibrary::TypeList   ModuleLibrary::m_TypeList;
ModuleLibrary::ModuleList ModuleLibrary::m_ModuleList;


void          ModuleLibrary::LoadFile(const char *pFilename) 
{
  void *handle;
  const char *pszError;
  bool bReturn = false;

  string sPath(pFilename);
  FixupLibraryName(sPath);
  string sName;
  MakeCanonicalName(sPath, sName);
  if(!FileExists(sName)) {
    if ((handle = ::load_library(sPath.c_str(), &pszError)) == NULL) {
#ifdef THROW
      ostringstream stream;

      stream << "failed to open library module ";
      stream << sPath;
      stream << ": ";
      stream << pszError;
      stream << endl;

      char cw[_MAX_PATH];
      getcwd(cw, sizeof(cw));
      stream << "current working directory is ";
      stream << cw;
      stream << endl << ends;
      free_error_message(pszError);
      throw new Error(stream.str());
#endif //THROW
    }
    else {

      if(AddFile(sPath.c_str(),handle)) {
        bReturn = true;
      }
    }
  }

  if(verbose)
    DisplayFileList();
  return;
}

void          ModuleLibrary::FreeFile(const char *pFilename) {
  FileList::iterator  it;
  FileList::iterator  itEnd(m_FileList.end());
  for( it = m_FileList.begin(); it != itEnd; it++) {
    Module_Types *pLibModList = (*it)->get_mod_list();

    // Remove the library file's types
    for(int iIndex = 0; iIndex < Module_Types_Name_Count; iIndex++) {
      TypeList::iterator itTypeEnd = m_TypeList.end();
      TypeList::iterator itType = m_TypeList.FindIt(pLibModList->names[iIndex]);

      if(itType != itTypeEnd) {
        // Remove all instanciated objects of this type
        ModuleList::iterator  itObject;
        ModuleList::iterator  itObjectEnd(m_ModuleList.end());
        for( itObject = m_ModuleList.begin(); itObject != itObjectEnd; itObject++) {
          if( strcmp((*itObject)->m_pType->m_pName, (*itType)->m_pName) ) {
            m_ModuleList.erase(itObject);
            delete *itObject;
          }
        }
        // Remove the module type
        m_TypeList.erase(itType);
        delete *itType;
      }
    }
  }
}

Module *      ModuleLibrary::NewObject(const char *pTypeName, const char *pName) {
  Type *pType;
  ostringstream stream;
  if( pType = m_TypeList.Get(pTypeName)) {
    if(pName == NULL) {
      stream << pTypeName << m_iSequenceNumber << ends;
      pName = stream.str().c_str();
    }
    Module * pModule = pType->m_pConstructor(pName);
    if(pModule) {
      pModule->SetType(pType);
      m_ModuleList.push_back(pModule);
      symbol_table.add_module(pModule, pName);
	    // Tell the gui or any modules that are interfaced to gpsim
	    // that a new module has been added.
	    gi.new_module(pModule);
      return pModule;
    }
  }
  return NULL;
}

void          ModuleLibrary::Delete(Module *pObject) {
  ModuleList::iterator it;
  if( (it = ::find(m_ModuleList.begin(), m_ModuleList.end(), pObject))
      != m_ModuleList.end()) {
    m_ModuleList.erase(it);
    // JRH - there should be a module_type->delete() to call
    delete *it;
  }
}

ICommandHandler * ModuleLibrary::GetCommandHandler(const char *pName) {
  File *pFile = m_FileList.Get(pName);
  if(pFile != NULL) {
    return pFile->GetCli();
  }
  return NULL;
}

void * ModuleLibrary::GetLibraryFileHandle(const char *pName) {
  File *pFile = m_FileList.Get(pName);
  if(pFile != NULL) {
    return pFile->m_pHandle;
  }
  return NULL;
}

void * ModuleLibrary::GetLibraryFunction(const char *pLibraryName,
                                         const char *pFunctionName) {
  File *pFile = m_FileList.Get(pLibraryName);
  if(pFile != NULL) {
    const char * error;
    return get_library_export(pFunctionName, pFile->m_pHandle, &error);
  }
  return NULL;
}

string        ModuleLibrary::DisplayFileList() {
  ostringstream stream;
  FileList::iterator  it;
  FileList::iterator  itEnd(m_FileList.end());
  stream << "Module Library Files\n";
  
  for( it = m_FileList.begin(); it != itEnd; it++) {
    stream << (*it)->m_pName << endl;
    Module_Types *pLibModList = (*it)->get_mod_list();
    if(pLibModList) {
      // Loop through the list and display all of the module types.
      for(Module_Types *pModTypes = pLibModList;
            pModTypes->names[0] != NULL;
            pModTypes++) {
        stream << "   " << pModTypes->names[0] << endl;
      }
    }
  }
  stream << ends;
  return string(stream.str());
}

string        ModuleLibrary::DisplayModuleTypeList() {
  ostringstream stream;
  string sDisplay;
  TypeList::iterator  it;
  TypeList::iterator  itEnd(m_TypeList.end());
  stream << "Module Types\n";
  for( it = m_TypeList.begin(); it != itEnd; it++) {
    stream << (*it)->m_pName << endl;
  }
  stream << ends;
  return string(stream.str());
}

string        ModuleLibrary::DisplayModuleList() {
  return symbol_table.DisplayType(typeid(module_symbol));
}

string ModuleLibrary::DisplayModulePins(char *pName) {
  ostringstream stream;
  Module * pMod = symbol_table.findModule(pName);
  if(pMod == NULL) {
    stream << "module `" << pName << "' wasn't found" << endl;
  }
  else {
    for(int i=1; i<=pMod->get_pin_count(); i++) {
      stream << " Pin number " << i
            << " named " << pMod->get_pin_name(i) 
	          << " is " << ( (pMod->get_pin_state(i)>0) ? "high" : "low");
      stream << endl;
    }
  }
  stream << ends;
  return string(stream.str());
}

string        ModuleLibrary::DisplayProcessorTypeList() {
  // I'm thinking of moving the processor constructor list
  // into the ModuleLibrary since ProcessorConstructors 
  // come from module library files.
  return ProcessorConstructorList::GetList()->DisplayString();
}

ModuleLibrary::FileList & ModuleLibrary::GetFileList() {
  return m_FileList;
}

ModuleLibrary::TypeList & ModuleLibrary::GetTypeList() {
  return m_TypeList;
}

#if 0
Processor *   ModuleLibrary::NewProcessorFromFile(const char *pName) {
  iReturn = CSimulationContext::GetContext()->LoadProgram(
                pName);
  return NULL;
}

Processor *   ModuleLibrary::NewProcessorFromType(const char *pType,
                                                  const char *pName) {
  return NULL;
}

void          ModuleLibrary::DeleteProcessor(Processor *) {
}
#endif

bool ModuleLibrary::AddFile(const char *library_name, void *library_handle)
{
  const char * error;
  if(library_name) {
    string sName(library_name);
    MakeCanonicalName(sName, sName);
    File *ml = new File(sName.c_str(), library_handle);
    m_FileList.push_back(ml);
    ml->get_mod_list = (Module_Types_FPTR)get_library_export(
      "get_mod_list", ml->m_pHandle, &error);

    if (NULL == ml->get_mod_list) {
      cout << "WARNING: non-conforming module library\n"
           << "  gpsim libraries should have the get_mod_list() function defined\n";
      fputs(error, stderr);
      fputs ("\n", stderr);
      free_error_message(error);
    } else {

      // Get a pointer to the list of modules that this library file supports.
      Module_Types *pLibModList = ml->get_mod_list();

      if(pLibModList) {
        // Loop through the list and display all of the module types.
        for(Module_Types *pModTypes = pLibModList;
              pModTypes->names[0] != NULL;
              pModTypes++) {
          for(int iIndex = 0; iIndex < Module_Types_Name_Count; iIndex++) {
            char *pModName = pModTypes->names[iIndex];
            if(pModName != NULL && ! m_TypeList.Exists(pModName)) {
              m_TypeList.Add(
                new Type(pModName, pModTypes->module_constructor));
            }          
          }
        }
      }
        // If the module has an "initialize" function, then call it now.
      typedef  void * (*void_FPTR)(void);
      void * (*initialize)(void) = (void_FPTR)get_library_export(
        "initialize", ml->m_pHandle, NULL);
      if(initialize)
        initialize();

      ICommandHandler * pCliHandler = ml->GetCli();
      if (pCliHandler != NULL)
        CCommandManager::GetManager().Register(pCliHandler);
    }
    return true;
  } else {
    string sMsg("AddLibrary() called with null pointer");
    throw new Error(sMsg);
  }
  return false;
}

void ModuleLibrary::MakeCanonicalName(string &sPath, string &sName) {
#ifdef _WIN32
  sName = sPath;
#else
  GetFileName(sPath, sName);
#endif
}

bool ModuleLibrary::FileExists(const string &sName) {
  return m_FileList.Exists(sName.c_str());
#if 0
  FileList::iterator it;
  FileList::iterator itEnd = m_FileList.end();
  for(it = m_FileList.begin(); it != itEnd; it++) {
    if(sName.compare((*it)->pName) == 0) {
      return true;
    }
  }
  return false;
#endif
}


ICommandHandler *ModuleLibrary::File::GetCli() {

  PFNGETCOMMANDHANDLER pGetCli = (PFNGETCOMMANDHANDLER)get_library_export(
    GPSIM_GETCOMMANDHANDLER, m_pHandle, NULL);
  if (pGetCli != NULL)
    return pGetCli();
  return NULL;
}

Module *ModuleLibrary::TypeList::NewObject(const char *pName) {
  Type *pType = Get(pName);
  if(pType != NULL) {
    return pType->m_pConstructor(pName);
  }
  return NULL;
}





/*****************************************************************************
 *
 * Module.cc
 *
 * Here's where much of the infrastructure of gpsim is defined.
 *
 * A Module is define to be something that gpsim knows how to simulate.
 * When gpsim was originally designed, a module was simple a pic processor.
 * This concept was expanded to accomodate devices like LEDs, switches,
 * LCDs and so on. 
 */

Module::Module(const char *_name, const char *desc)
  : gpsimObject(_name, desc)
{

  package = 0;
  interface = 0;

  // Derived modules should assign more reasonable values for this.
  version = 0;

  xref = new XrefObject;

  simulation_mode = eSM_STOPPED;
  widget = 0;
}

Module::~Module(void)
{

  symbol_table.remove_module(this);

//  instantiated_modules_list.remove(this);

  delete package;
  delete xref;
}

void Module::reset(RESET_TYPE r)
{
  cout << " resetting module " << name() << endl;
}

//-------------------------------------------------------------------
//-------------------------------------------------------------------

string  Module::DisplayAttributes(bool show_values)
{
  ostringstream stream;
  list <Value *> :: iterator attribute_iterator;

  for (attribute_iterator = attributes.begin();  
       attribute_iterator != attributes.end(); 
       attribute_iterator++) {

    Value *locattr = *attribute_iterator;

    stream << locattr->name();
    if(show_values)
      stream << " = " << locattr->toString();
    stream << endl;
  }
  stream << ends;
  return string(stream.str());
}


//-------------------------------------------------------------------
//-------------------------------------------------------------------

Value *Module::get_attribute(char *attribute_name, bool bWarnIfNotFound)
{
  if(!attribute_name)
    return 0;

  string full_name = name() + "." + attribute_name;
  list <Value *> :: iterator attribute_iterator;

  for (attribute_iterator = attributes.begin();  
       attribute_iterator != attributes.end(); 
       attribute_iterator++) {

    Value *locattr = *attribute_iterator;

    if (locattr->name() == full_name)
      return locattr;
  }

  if (bWarnIfNotFound)
    cout << "Could not find attribute named " << attribute_name  << '\n';

  return 0;
}

//-------------------------------------------------------------------
//-------------------------------------------------------------------
void Module::initializeAttributes()
{
  // Create position attribute place holders if we're not using the gui
  if(!get_interface().bUsingGUI()) {
    add_attribute(new Float("xpos",80.0));
    add_attribute(new Float("ypos",80.0));
  }
}
//-------------------------------------------------------------------
//-------------------------------------------------------------------
void Module::add_attribute(Value *new_attribute)
{

  attributes.push_back(new_attribute);

  symbol_table.add(new attribute_symbol(this,new_attribute));

  if(verbose)
    cout << "add_attribute  name = " << new_attribute->name() << '\n';

}

//-------------------------------------------------------------------
//-------------------------------------------------------------------
void Module::create_pkg(unsigned int number_of_pins)
{
  if(package)
    delete package;

  package = new Package(number_of_pins);

}

void Module::SetType(ModuleLibrary::Type *pType) {
  m_pType = pType;
}

//-------------------------------------------------------------------
//-------------------------------------------------------------------
void Module::assign_pin(unsigned int pin_number, IOPIN *pin)
{
  if(package)
    package->assign_pin(pin_number, pin);

}

//-------------------------------------------------------------------
//-------------------------------------------------------------------
int Module::get_pin_count(void)
{
  if(package)
    return package->get_pin_count();

  return 0;

}
//-------------------------------------------------------------------
//-------------------------------------------------------------------
string &Module::get_pin_name(unsigned int pin_number)
{
  static string invalid("");
  if(package)
    return package->get_pin_name(pin_number);
  return invalid;  //FIXME

}
//-------------------------------------------------------------------
//-------------------------------------------------------------------
int Module::get_pin_state(unsigned int pin_number)
{
  if(package)
    return package->get_pin_state(pin_number);

  return 0;
}
//-------------------------------------------------------------------
//-------------------------------------------------------------------
IOPIN *Module::get_pin(unsigned int pin_number)
{
  if(package)
    return package->get_pin(pin_number);

  return 0;
}

//-------------------------------------------------------------------
// Module Scripts
//
// Module command line scripts are named scripts created by symbol 
// files. For example, with PIC cod files, it's possible to
// create assertions and simulation commands using the '.assert'
// and '.sim' directives. These commands are ASCII strings that
// are collected together. 
//

//-------------------------------------------------------------------
// Module::add_command
//
// Add a command line command to a Module Script.
//-------------------------------------------------------------------
void Module::add_command(string &script_name, string &command)
{
  ModuleScript *script = m_scripts[script_name];
  if (!script) {
    script = new ModuleScript(script_name);
    m_scripts[script_name] = script;
  }

  script->add_command(command);
}

//-------------------------------------------------------------------
// Module::run_script - execute a gpsim command line script
// 
//-------------------------------------------------------------------
void Module::run_script(string &script_name)
{
  ModuleScript *script = m_scripts[script_name];
  if (script) {
    ICommandHandler *pCli = CCommandManager::GetManager().find("gpsimCLI");
    if(pCli) {
      script->run(pCli);
    }
  }
}
//-------------------------------------------------------------------
//-------------------------------------------------------------------
Module::ModuleScript::ModuleScript(string &name_)
  : name(name_)
{
}
//-------------------------------------------------------------------
//-------------------------------------------------------------------
Module::ModuleScript::~ModuleScript()
{
}
//-------------------------------------------------------------------
//-------------------------------------------------------------------
void Module::ModuleScript::add_command(string &command)
{
  string *new_command = new string(command);
  m_commands.push_back(new_command);
}

//-------------------------------------------------------------------
//-------------------------------------------------------------------
void Module::ModuleScript::run(ICommandHandler *pCommandHandler)
{
  if (!pCommandHandler)
    return;

  pCommandHandler->ExecuteScript(m_commands,0);
}

//-------------------------------------------------------------------
//-------------------------------------------------------------------
void Module::ModuleScript::concatenate(ModuleScript *pOtherScript)
{
  if (!pOtherScript)
    return;

  list <string *> :: iterator command_iterator;

  for (command_iterator = pOtherScript->m_commands.begin();
       command_iterator != pOtherScript->m_commands.end(); 
       ++command_iterator)
    m_commands.push_back(*command_iterator);
}


syntax highlighted by Code2HTML, v. 0.9.1