//===-- UserInput.cpp - Interpreter Input Loop support --------------------===//
// 
//  This file implements the interpreter Input I/O loop.
//
//===----------------------------------------------------------------------===//

#include "Interpreter.h"
#include "llvm/Bytecode/Reader.h"
#include "llvm/Assembly/Writer.h"
#include "llvm/DerivedTypes.h"
#include "llvm/Transforms/Linker.h"
#include <algorithm>

enum CommandID {
  Quit, Help,                                 // Basics
  Print, Info, List, StackTrace, Up, Down,    // Inspection
  Next, Step, Run, Finish, Call,              // Control flow changes
  Break, Watch,                               // Debugging
  Load, Flush,
  TraceOpt, ProfileOpt                              // Toggle features
};

// CommandTable - Build a lookup table for the commands available to the user...
static struct CommandTableElement {
  const char *Name;
  enum CommandID CID;

  inline bool operator<(const CommandTableElement &E) const {
    return string(Name) < string(E.Name);
  }
  inline bool operator==(const string &S) const { 
    return string(Name) == S;
  }
} CommandTable[] = {
  { "quit"     , Quit       }, { "q", Quit }, { "", Quit }, // Empty str = eof
  { "help"     , Help       }, { "h", Help },

  { "print"    , Print      }, { "p", Print },
  { "list"     , List       },
  { "info"     , Info       },
  { "backtrace", StackTrace }, { "bt", StackTrace }, { "where", StackTrace },
  { "up"       , Up         },
  { "down"     , Down       },

  { "next"     , Next       }, { "n", Next },
  { "step"     , Step       }, { "s", Step },
  { "run"      , Run        },
  { "finish"   , Finish     },
  { "call"     , Call       },

  { "break"    , Break      }, { "b", Break },
  { "watch"    , Watch      },

  { "load"     , Load       },
  { "flush"    , Flush      },

  { "trace"    , TraceOpt   },
  { "profile"  , ProfileOpt },
};
static CommandTableElement *CommandTableEnd = 
   CommandTable+sizeof(CommandTable)/sizeof(CommandTable[0]);


//===----------------------------------------------------------------------===//
// handleUserInput - Enter the input loop for the interpreter.  This function
// returns when the user quits the interpreter.
//
void Interpreter::handleUserInput() {
  bool UserQuit = false;

  // Sort the table...
  sort(CommandTable, CommandTableEnd);

  // Print the instruction that we are stopped at...
  printCurrentInstruction();

  do {
    string Command;
    cout << "lli> " << flush;
    cin >> Command;

    CommandTableElement *E = find(CommandTable, CommandTableEnd, Command);

    if (E == CommandTableEnd) {
      cout << "Error: '" << Command << "' not recognized!\n";
      continue;
    }

    switch (E->CID) {
    case Quit:       UserQuit = true;   break;
    case Load:
      cin >> Command;
      loadModule(Command);
      break;
    case Flush: flushModule(); break;
    case Print:
      cin >> Command;
      print(Command);
      break;
    case Info:
      cin >> Command;
      infoValue(Command);
      break;
     
    case List:       list();            break;
    case StackTrace: printStackTrace(); break;
    case Up: 
      if (CurFrame > 0) { --CurFrame; printStackFrame(); }
      else cout << "Error: Already at root of stack!\n";
      break;
    case Down:
      if ((unsigned)CurFrame < ECStack.size()-1) {
        ++CurFrame;
        printStackFrame();
      } else
        cout << "Error: Already at bottom of stack!\n";
      break;
    case Next:       nextInstruction(); break;
    case Step:       stepInstruction(); break;
    case Run:        run();             break;
    case Finish:     finish();          break;
    case Call:
      cin >> Command;
      callMethod(Command);    // Enter the specified method
      finish();               // Run until it's complete
      break;

    case TraceOpt:
      Trace = !Trace;
      cout << "Tracing " << (Trace ? "enabled\n" : "disabled\n");
      break;

    case ProfileOpt:
      Profile = !Profile;
      cout << "Profiling " << (Trace ? "enabled\n" : "disabled\n");
      break;

    default:
      cout << "Command '" << Command << "' unimplemented!\n";
      break;
    }

  } while (!UserQuit);
}

//===----------------------------------------------------------------------===//
// loadModule - Load a new module to execute...
//
void Interpreter::loadModule(const string &Filename) {
  string ErrorMsg;
  if (CurMod && !flushModule()) return;  // Kill current execution

  CurMod = ParseBytecodeFile(Filename, &ErrorMsg);
  if (CurMod == 0) {
    cout << "Error parsing '" << Filename << "': No module loaded: "
         << ErrorMsg << "\n";
    return;
  }
  CW.setModule(CurMod);  // Update Writer

  string RuntimeLib = getCurrentExecutablePath();
  if (!RuntimeLib.empty()) RuntimeLib += "/";
  RuntimeLib += "RuntimeLib.bc";

  if (Module *SupportLib = ParseBytecodeFile(RuntimeLib, &ErrorMsg)) {
    if (LinkModules(CurMod, SupportLib, &ErrorMsg))
      cerr << "Error Linking runtime library into current module: "
           << ErrorMsg << endl;
  } else {
    cerr << "Error loading runtime library '"+RuntimeLib+"': "
         << ErrorMsg << "\n";
  }
}


//===----------------------------------------------------------------------===//
// flushModule - Return true if the current program has been unloaded.
//
bool Interpreter::flushModule() {
  if (CurMod == 0) {
    cout << "Error flushing: No module loaded!\n";
    return false;
  }

  if (!ECStack.empty()) {
    // TODO: if use is not sure, return false
    cout << "Killing current execution!\n";
    ECStack.clear();
    CurFrame = -1;
  }

  CW.setModule(0);
  delete CurMod;
  CurMod = 0;
  ExitCode = 0;
  return true;
}

//===----------------------------------------------------------------------===//
// setBreakpoint - Enable a breakpoint at the specified location
//
void Interpreter::setBreakpoint(const string &Name) {
  Value *PickedVal = ChooseOneOption(Name, LookupMatchingNames(Name));
  // TODO: Set a breakpoint on PickedVal
}

//===----------------------------------------------------------------------===//
// callMethod - Enter the specified method...
//
bool Interpreter::callMethod(const string &Name) {
  vector<Value*> Options = LookupMatchingNames(Name);

  for (unsigned i = 0; i < Options.size(); ++i) { // Remove nonmethod matches...
    if (!isa<Method>(Options[i])) {
      Options.erase(Options.begin()+i);
      --i;
    }
  }

  Value *PickedMeth = ChooseOneOption(Name, Options);
  if (PickedMeth == 0)
    return true;

  Method *M = cast<Method>(PickedMeth);

  vector<GenericValue> Args;
  // TODO, get args from user...

  callMethod(M, Args);  // Start executing it...

  // Reset the current frame location to the top of stack
  CurFrame = ECStack.size()-1;

  return false;
}

static void *CreateArgv(const vector<string> &InputArgv) {
  // Pointers are 64 bits...
  uint64_t *Result = new PointerTy[InputArgv.size()+1];

  for (unsigned i = 0; i < InputArgv.size(); ++i) {
    unsigned Size = InputArgv[i].size()+1;
    char *Dest = new char[Size];
    copy(InputArgv[i].begin(), InputArgv[i].end(), Dest);
    Dest[Size-1] = 0;
    Result[i] = (PointerTy)Dest;
  }

  Result[InputArgv.size()] = 0;
  return Result;
}


// callMainMethod - This is a nasty gross hack that will dissapear when
// callMethod can parse command line options and stuff for us.
//
bool Interpreter::callMainMethod(const string &Name,
                                 const vector<string> &InputArgv) {
  vector<Value*> Options = LookupMatchingNames(Name);

  for (unsigned i = 0; i < Options.size(); ++i) { // Remove nonmethod matches...
    if (!isa<Method>(Options[i])) {
      Options.erase(Options.begin()+i);
      --i;
    }
  }

  Value *PickedMeth = ChooseOneOption(Name, Options);
  if (PickedMeth == 0)
    return true;

  Method *M = cast<Method>(PickedMeth);
  const MethodType *MT = M->getMethodType();

  vector<GenericValue> Args;
  switch (MT->getParamTypes().size()) {
  default:
    cout << "Unknown number of arguments to synthesize for '" << Name << "'!\n";
    return true;
  case 2: {
    PointerType *SPP = PointerType::get(PointerType::get(Type::SByteTy));
    if (MT->getParamTypes()[1] != SPP) {
      CW << "Second argument of '" << Name << "' should have type: '"
         << SPP << "'!\n";
      return true;
    }

    GenericValue GV; GV.PointerVal = (uint64_t)CreateArgv(InputArgv);
    Args.push_back(GV);
  }
    // fallthrough
  case 1:
    if (!MT->getParamTypes()[0]->isIntegral()) {
      cout << "First argument of '" << Name << "' should be integral!\n";
      return true;
    } else {
      GenericValue GV; GV.UIntVal = InputArgv.size();
      Args.insert(Args.begin(), GV);
    }
    // fallthrough
  case 0:
    break;
  }

  callMethod(M, Args);  // Start executing it...

  // Reset the current frame location to the top of stack
  CurFrame = ECStack.size()-1;

  return false;
}



void Interpreter::list() {
  if (ECStack.empty())
    cout << "Error: No program executing!\n";
  else
    CW << ECStack[CurFrame].CurMethod;   // Just print the method out...
}

void Interpreter::printStackTrace() {
  if (ECStack.empty()) cout << "No program executing!\n";

  for (unsigned i = 0; i < ECStack.size(); ++i) {
    printStackFrame((int)i);
  }
}