/* 
 * © 2009 Sun Microsystems, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include "stdafx.h"


#ifdef ENABLE_PYTHON_MODULES
#include "grtpp_shell_python.h"
#include "grtpp_util.h"

// python internals
#ifdef __APPLE__
#include <Python/node.h>
#include <Python/grammar.h>
#include <Python/parsetok.h>
#include <Python/errcode.h>
#include <Python/token.h>
#else
#include <node.h>
#include <grammar.h>
#include <parsetok.h>
#include <errcode.h>
#include <token.h>
#endif




using namespace grt;



PythonShell::PythonShell(GRT *grt)
: Shell(grt), _loader(0)
{
}

void PythonShell::init()
{
  _loader= dynamic_cast<PythonModuleLoader*>(_grt->get_module_loader(LanguagePython));
  if (!_loader)
    throw std::runtime_error("Python module loader not initialized");
  
  PythonContext *pycontext= _loader->get_python_context();

  pycontext->refresh();
}


void PythonShell::print_welcome()
{
  print(strfmt("MySQL Generic Runtime Environment %s\n", GRT_VERSION));
  
  if (_disable_quit)
    print("\nType 'help' or '?' for help.\n");
  else
    print("Type 'help' or '?' for help. Type 'quit' to exit the shell.\n");
  print("Python Shell initialized.\n");
}


std::string PythonShell::get_prompt()
{
  std::string cwd= _loader->get_python_context()->get_cwd();
  
  if (_current_line.empty())
    return cwd+">>>";
  else
    return cwd+"...";
}



int PythonShell::execute_line(const std::string &linebuf)
{
  PythonContext *pycon= _loader->get_python_context();
  node *n;
  PyObject *result;
  PyObject *mainmod;
  PyObject *globals;
  
  _current_line.append(linebuf);
  
  LockPython lock(pycon);
  
  n= PyParser_SimpleParseStringFlags(_current_line.c_str(),
                                     Py_single_input,
                                     0);
  
  if (n && (linebuf[0] ==' ' || linebuf[0] =='\t'))
  {
    return 0; // continued line
  }
  
  if (!n && PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_SyntaxError))
  {
    PyObject *excep;
    PyObject *value;
    PyObject *message;
    PyObject *trace;
    PyErr_Fetch(&excep, &value, &trace);
    Py_DECREF(excep);
    if (trace)
    {
      Py_DECREF(trace);
    }
    message= PyTuple_GetItem(value, 0);
    if (strstr(PyString_AsString(message), "unexpected EOF") ||
        strncmp(PyString_AsString(message), "EOF", 3)==0)
    {
      Py_DECREF(value);
      PyErr_Clear();
      return 0; // continued line
    }
    Py_DECREF(value);
  }
  
  if (!n)
  {
    PyErr_Print();  
    
    _current_line.clear();
    
    PyErr_Clear();
    return -1;
  }
  PyNode_Free(n);
  PyErr_Clear();
  
  // command is supposedly complete, try to execute it
  
  mainmod= PyImport_AddModule("__main__");
  if (!mainmod)
  {
    return -1;
  }
  globals= PyModule_GetDict(mainmod);
  
  result= PyRun_String(_current_line.c_str(), Py_single_input, globals, globals);
  
  _current_line.clear();
  
  if (!result)
  {
    if (PyErr_Occurred())
      PyErr_Print();
    
    return -1;
  }
  else
  {    
    Py_DECREF(result);
  }
  
  return 0;
}


int PythonShell::run_file(const std::string &file_name, bool interactive)
{
  return _loader->get_python_context()->run_file(file_name, interactive);
}


std::vector<std::string> PythonShell::complete_line(const std::string &line, std::string &completed)
{
  std::vector<std::string> tokens= get_tokens_for_prefix(line);
  if (tokens.size() == 1)
  {
    completed= tokens[0];
    tokens.clear();
  }
  return tokens;
}


static void add_matching_tokens_from_list(std::vector<std::string> &tokens, PyObject *list, const char *base, const char *prefix, const char *append= "")
{
  for (Py_ssize_t i= 0; i < PyList_Size(list); i++)
  {
    const char *str= PyString_AsString(PyList_GetItem(list, i));
    if (g_str_has_prefix(str, prefix))
    {
      if (base)
        tokens.push_back(strfmt("%s.%s%s", base, str, append));
      else
        tokens.push_back(strfmt("%s%s", str, append));
    }
  }
}

std::vector<std::string> PythonShell::get_tokens_for_prefix(const std::string &prefix)
{
  std::vector<std::string> tokens;
  std::string::size_type dot= prefix.rfind('.');
  
  LockPython lock(_loader->get_python_context());
  
  if (dot == std::string::npos) // SOMET
  {
    PyObject *module= PyImport_ImportModule("keyword");
    if (module)
    {
      PyObject *dict= PyModule_GetDict(module);
      if (dict)
      {
        PyObject *list= PyDict_GetItemString(dict, "kwlist");
        if (list)
          add_matching_tokens_from_list(tokens, list, NULL, prefix.c_str(), " ");
      }
    }
    
    module= PyImport_AddModule("__main__");
    if (module)
    {
      PyObject *dict= PyModule_GetDict(module);
      if (dict)
      {
        PyObject *list= PyDict_Keys(dict);
        add_matching_tokens_from_list(tokens, list, NULL, prefix.c_str());
        Py_DECREF(list);
      }
      
      PyObject *submod= PyDict_GetItemString(dict, "__builtins__");
      if (submod)
      {
        PyObject *list= PyObject_Dir(submod);
        if (list)
        {
          add_matching_tokens_from_list(tokens, list, NULL, prefix.c_str(), "(");
          Py_DECREF(list);
        }
      }
    }
    PyErr_Clear();
  }
  else // PREFIX.SOMET
  {
    std::string base= prefix.substr(0, dot);
    std::string pref= prefix.substr(dot+1);
    
    PyObject *object= _loader->get_python_context()->eval_string(base);
    if (object)
    {
      // add results of dir()
      PyObject *list= PyObject_Dir(object);
      if (list)
      {
        add_matching_tokens_from_list(tokens, list, base.c_str(), pref.c_str());
        Py_DECREF(list);
      }
      Py_DECREF(object);
    }
    PyErr_Clear();
  }
  
  return tokens;
}


ValueRef PythonShell::get_global_var(const std::string &var_name)
{
  ValueRef value;
  
  throw std::logic_error("not implemented");
  
  return value;
}


int PythonShell::set_global_var(const std::string &var_name, const ValueRef &value)
{
  throw std::logic_error("not implemented");  
  return 1;
}


void PythonShell::show_help(const std::string &keyword)
{
  extern void myx_grt_shell_show_help(grt::GRT *grt, const char *command);
  
  myx_grt_shell_show_help(_grt, keyword.c_str());
}


#endif
