/*  _______________________________________________________________________
 
    Surfpack: A Software Library of Multidimensional Surface Fitting Methods
    Copyright (c) 2006, Sandia National Laboratories.
    This software is distributed under the GNU Lesser General Public License.
    For more information, see the README file in the top Surfpack directory.
    _______________________________________________________________________ */


#include "SurfpackParser.h"
#include "surfparse.h"
#include "FlexWrapper.h"

/// This function will be generated by bison
extern int yyparse();

std::ostringstream SurfpackParser::cmdstream;

//
std::string SurfpackParser::argname;
std::string SurfpackParser::argval;
ParamMap SurfpackParser::params;
std::vector<Command> SurfpackParser::comms;

SurfpackParser& SurfpackParser::instance()
{
  static SurfpackParser sp;
  return sp;
}

FlexWrapper& SurfpackParser::globalLexer()
{
  return *global_lexer;
}

int SurfpackParser::yyparse(const std::string* input_string, 
  const std::string* output_string)
{
  // Sets the input file/stream for the lexical analyzer
  global_lexer->setParseStreams(input_string,output_string);
  // Calls the code generated by bison, which in turn calls
  // The code generated by flex.
  return ::yyparse();
}

SurfpackParser::SurfpackParser() 
{
  global_lexer = new FlexWrapper;
  currentTuple = new Tuple;
  init();
}

void SurfpackParser::init()
{
  commands.clear();
  currentArgList = 0;
  currentArgIndex = -1;
  currentTupleIndex = -1;
  cmdstream.str("");
}

SurfpackParser::~SurfpackParser()
{
  delete global_lexer;
  delete currentTuple;
  global_lexer = 0;
}

void SurfpackParser::storeCommandString()
{
  if (commands.size() > 0) {
    int loc;
    std::string newcommand = cmdstream.str();
    if (newcommand.find("/*") == 0) {
      newcommand.erase(0,2);
    }
    if ((loc = newcommand.find("*/")) != std::string::npos) {
      newcommand.erase(loc,2);
    }
    if (newcommand.find("!") == 0) {
       newcommand.erase(0,1);
    }
    commands[commands.size()-1].cmdstring = newcommand;
    cmdstream.str("");
  }
}

std::vector<ParsedCommand>& SurfpackParser::commandList()
{
  return commands;
}

void SurfpackParser::addCommandName()
{
  // simplified
  comms.push_back(Command());
  comms.back().first = std::string(global_lexer->currentToken());
  params.clear();
  // end simplified

  ParsedCommand pc;
  commands.push_back(pc);
  commands[commands.size()-1].name = std::string(global_lexer->currentToken());
  pushNewArgList();
}

void SurfpackParser::addArg()
{
  argval = std::string(global_lexer->currentToken());
  int pos;
  while ( (pos = argval.find('\'')) != std::string::npos) {
    argval.erase(pos,pos+1);
  }
}

void SurfpackParser::appendArg()
{
  params.insert(ModelParam(argname,argval));
}

void SurfpackParser::printComms()
{
  for (unsigned i = 0; i < comms.size(); i++) {
    std::cout << comms[i].first << std::endl;
    for (ParamMap::iterator itr = comms[i].second.begin();
	itr != comms[i].second.end(); itr++) {
      std::cout << "     " << itr->first << ": " << itr->second << std::endl;
    }
  }
}

void SurfpackParser::addArgName()
{
  argname = std::string(global_lexer->currentToken());
  argval = "";
  if (currentArgList == 0) {
    std::cerr << "currentArgList is NULL; cannot assign name" << std::endl;
  } else {
    Arg newArg;
    currentArgList->push_back(newArg);
    currentArgIndex++;
    (*currentArgList)[currentArgIndex].name = std::string(global_lexer->currentToken());
  }
}

void SurfpackParser::addArgValIdent()
{
  if (currentArgIndex == -1) {
    std::cerr << "currentArgIndex = -1; cannot assign Identifier" << std::endl;
  } else {
    (*currentArgList)[currentArgIndex].setRVal
      (new RvalIdentifier(std::string(global_lexer->currentToken())));
  }
}

void SurfpackParser::addArgValInt()
{
  if (currentArgIndex == -1) {
    std::cerr << "currentArgIndex = -1; cannot assign Integer" << std::endl;
  } else {
    (*currentArgList)
      [currentArgIndex].setRVal(new RvalInteger(atoi(global_lexer->currentToken())));
  }
}

void SurfpackParser::addArgValString()
{
  if (currentArgIndex == -1) {
    std::cerr << "currentArgIndex = -1; cannot assign String" << std::endl;
  } else {
    std::string currentToken = std::string(global_lexer->currentToken());
    // The token contains leading and trailing apostrophes; remove them.
    int pos;
    while ( (pos = currentToken.find('\'')) != std::string::npos) {
      currentToken.erase(pos,pos+1);
    }
    (*currentArgList)
      [currentArgIndex].setRVal(new RvalStringLiteral(currentToken));
    //std::cout << "Stripped std::string: " << currentToken << std::endl;
  }
}

void SurfpackParser::addArgValReal()
{
  if (currentArgIndex == -1) {
    std::cerr << "currentArgIndex = -1; cannot assign Real" << std::endl;
  } else {
    (*currentArgList)
      [currentArgIndex].setRVal(new RvalReal(atof(global_lexer->currentToken())));
  }
}

void SurfpackParser::pushNewArgList()
{
  arglistStack.push(ArgList());
  currentArgList = &(arglistStack.top());
  currentArgIndex = -1;
}
void SurfpackParser::addArgValArgList()
{
  if (currentArgIndex == -1) {
    std::cerr << "currentArgIndex = -1; cannot assign ArgList" << std::endl;
  } else {
    ArgList temp = arglistStack.top();
    popArgList();
    (*currentArgList)
      [currentArgIndex].setRVal(new RvalArgList(temp));
              
  }
}

void SurfpackParser::popArgList()
{
  assert(!arglistStack.empty());
  arglistStack.pop();
  if (arglistStack.empty()) {
    currentArgList = 0;
    currentArgIndex = -1;
  } else {
    currentArgIndex = arglistStack.top().size()-1;
    currentArgList = &(arglistStack.top());
  }
}

void SurfpackParser::addArgListToCommand()
{
  comms.back().second = params;
  commands[commands.size()-1].arglist = *currentArgList;
  popArgList();
}

void SurfpackParser::addTupleVal()
{
  // Simplified parser info
  argval += " ";
  argval += std::string(global_lexer->currentToken());
  // end simplified parser info

  if (currentArgIndex == -1) {
    std::cerr << "currentArgIndex = -1; cannot addTupleVal" << std::endl;
  } else {
    //double val = atof(global_lexer->currentToken());
    std::string val = std::string(global_lexer->currentToken());
    currentTuple->push_back(val);
    //(*currentArgList)[currentArgIndex].getRVal()->getTuple().push_back(val);
  }
}

void SurfpackParser::addArgValTuple()
{
  if (currentArgIndex == -1) {
    std::cerr << "currentArgIndex = -1; cannot addTuple" << std::endl;
  } else {
    (*currentArgList)[currentArgIndex].setRVal(new RvalTuple(*currentTuple));
  }
}

void SurfpackParser::newTuple()
{
  currentTuple->clear();
}

std::string SurfpackParser::parseIdentifier(const std::string& argname,
  const ArgList& arglist, bool throwExIfAbsent)
{
  for (unsigned i = 0; i < arglist.size(); i++) {
    if (arglist[i].name == argname) {
      return arglist[i].getRVal()->getIdentifier();
    }
  }
  if (throwExIfAbsent) {
    std::ostringstream os;
    os << "Required parameter " << argname << " is not specified." 
	<< std::endl;
    throw os.str();
  }
  return std::string("");
}

std::string SurfpackParser::parseStringLiteral(const std::string& argname,
  const ArgList& arglist, bool throwExIfAbsent)
{
  for (unsigned i = 0; i < arglist.size(); i++) {
    if (arglist[i].name == argname) {
      return arglist[i].getRVal()->getStringLiteral();
    }
  }
  if (throwExIfAbsent) throw std::string("parseStringLiteral");
  return std::string("");
}

int SurfpackParser::parseInteger(const std::string& argname,
  const ArgList& arglist, bool& valid, bool throwExIfAbsent)
{
  valid = false;
  for (unsigned i = 0; i < arglist.size(); i++) {
    if (arglist[i].name == argname) {
      valid = true;
      return arglist[i].getRVal()->getInteger();
    }
  }
  if (throwExIfAbsent) throw std::string("parseInteger");
  return -1;
}

std::vector<double> SurfpackParser::parseTuple(const std::string& argname,
  const ArgList& arglist, bool throwExIfAbsent)
{
  std::vector<double> result;
  for (unsigned i = 0; i < arglist.size(); i++) {
    if (arglist[i].name == argname) {
      RvalTuple::asVectorDouble(result,arglist[i].getRVal()->getTuple());
    }
  }
  if (result.empty() && throwExIfAbsent) throw std::string("parseTuple");
  return result;
}

std::vector<unsigned> SurfpackParser::parseUnsignedTuple(const std::string& argname,
  const ArgList& arglist, bool throwExIfAbsent)
{
  std::vector<double> dresult;
  std::vector<unsigned> result;
  for (unsigned i = 0; i < arglist.size(); i++) {
    if (arglist[i].name == argname) {
      RvalTuple::asVectorDouble(dresult,arglist[i].getRVal()->getTuple());
    }
  }
  for (unsigned i = 0; i < dresult.size(); i++) result.push_back((unsigned)dresult[i]);
  if (result.empty() && throwExIfAbsent) throw std::string("parseUnsignedTuple");
  return result;
}

std::vector<std::string> 
SurfpackParser::parseStringTuple(const std::string& argname,
  const ArgList& arglist, bool throwExIfAbsent)
{
  std::vector<std::string> result;
  for (unsigned i = 0; i < arglist.size(); i++) {
    if (arglist[i].name == argname) {
      RvalTuple::asVectorString(result,arglist[i].getRVal()->getTuple());
    }
  }
  if (result.empty() && throwExIfAbsent) throw std::string("parseStringTuple");
  return result;
}


std::vector<std::string> SurfpackParser::parseMultiString(
  const std::string& argname, const ArgList& arglist, bool throwExIfAbsent)
{
  std::vector<std::string> result;
  for (unsigned i = 0; i < arglist.size(); i++) {
    if (arglist[i].name == "test_function") {
      result.push_back(arglist[i].getRVal()->getIdentifier());
    }
  }
  if (throwExIfAbsent && result.empty()) {
    throw std::string("parseMultiString");
  }
  return result;
}

void SurfpackParser::shellCommand()
{
  ParsedCommand pc(true); // ctor arg specifies that it's a shell command
  commands.push_back(pc);
  storeCommandString();
}

ParsedCommand::ParsedCommand() : shellCommand(false)
{

}

ParsedCommand::ParsedCommand(bool shell_command) 
  : shellCommand(shell_command)
{

}

bool ParsedCommand::isShellCommand() const 
{ 
  return shellCommand; 
}

extern "C" void appendToken(const char* token)
{
  SurfpackParser::cmdstream << token;
}
