/*****
 * runhistory.in
 *
 * Runtime functions for history operations.
 *
 *****/

pair     => primPair()
picture* => primPicture()
stringarray* => stringArray()


#include "array.h"
#include "mathop.h"

using namespace camp;
using namespace settings;
using namespace vm;
using namespace run;

typedef array stringarray;
using types::stringArray;

#if defined(HAVE_LIBREADLINE) && defined(HAVE_LIBCURSES)
#include <readline/readline.h>
#include <readline/history.h>

struct historyState {
  bool store;
  HISTORY_STATE state;
};
  
typedef mem::map<CONST string, historyState> historyMap_t;
historyMap_t historyMap;
static HISTORY_STATE history_save;

// Store a deep copy of the current readline history in dest.
void store_history(HISTORY_STATE *dest)
{
  HISTORY_STATE *src=history_get_history_state();
  if(src) {
    *dest=*src;
    for(Int i=0; i < src->length; ++i)
      dest->entries[i]=src->entries[i];
    free(src);
  }
}

stringarray* get_history(Int n) 
{
  int N=intcast(n);
  if(N <= 0) N=history_length;
  else N=Min(N,history_length);
  array *a=new array((size_t) N);
  int offset=history_length-N+1;
  for(int i=0; i < N; ++i) {
    HIST_ENTRY *last=history_get(offset+i);
    string s=last ? last->line : "";
    (*a)[i]=s;
  }     
  return a;
}

string historyfilename(const string &name) 
{
  return historyname+"_"+name;
}
#endif

namespace run {

extern string emptystring;

#if defined(HAVE_LIBREADLINE) && defined(HAVE_LIBCURSES)
int readline_startup_hook()
{
#ifdef __CYGWIN__
  rl_set_key("\\M-[3~",rl_delete,rl_get_keymap());
  rl_set_key("\\M-[2~",rl_overwrite_mode,rl_get_keymap());
#endif    
  return 0;
}

void init_readline(bool tabcompletion) 
{
  static bool first=true;
  if(first) {
    first=false;
#ifdef __CYGWIN__
    rl_startup_hook=readline_startup_hook;
#endif    
  }
  rl_bind_key('\t',tabcompletion ? rl_complete : rl_insert);
}
#endif

void cleanup()
{
#if defined(HAVE_LIBREADLINE) && defined(HAVE_LIBCURSES)
  store_history(&history_save);
  int nlines=intcast(getSetting<Int>("historylines"));
  for(historyMap_t::iterator h=historyMap.begin(); h != historyMap.end(); 
      ++h) {
    history_set_history_state(&h->second.state);
    stifle_history(nlines);
    if(h->second.store) write_history(historyfilename(h->first).c_str());
  }
  history_set_history_state(&history_save);
#endif
}
}

// Autogenerated routines:


// Return the last n lines of the history named name.
stringarray* history(string name, Int n=1)
{
#if defined(HAVE_LIBREADLINE) && defined(HAVE_LIBCURSES)
  bool newhistory=historyMap.find(name) == historyMap.end();
  
  string filename;
  
  if(newhistory) {
    filename=historyfilename(name);
    std::ifstream exists(filename.c_str());
    if(!exists) return new array(0);
  }

  store_history(&history_save);
  HISTORY_STATE& history=historyMap[name].state;
  history_set_history_state(&history);
  
  if(newhistory)
    read_history(filename.c_str());

  array *a=get_history(n);
  
  store_history(&history);
  history_set_history_state(&history_save);
  
  return a;
#else
  unused(&n);
  return new array(0);
#endif
}

// Return the last n lines of the interactive history.
stringarray* history(Int n=0)
{
#if defined(HAVE_LIBREADLINE) && defined(HAVE_LIBCURSES)
  return get_history(n);
#else
  unused(&n);
  return new array(0);
#endif
}

// Prompt for a string using prompt, the GNU readline library, and a
// local history named name.
string readline(string prompt=emptystring, string name=emptystring,
                bool tabcompletion=false)
{
  if(!(isatty(STDIN_FILENO) || getSetting<bool>("interactive")))
    return emptystring;
#if defined(HAVE_LIBREADLINE) && defined(HAVE_LIBCURSES)
  init_readline(tabcompletion);
  
  store_history(&history_save);
  bool newhistory=historyMap.find(name) == historyMap.end();
  historyState& h=historyMap[name];
  HISTORY_STATE& history=h.state;
  history_set_history_state(&history);

  if(newhistory)
    read_history(historyfilename(name).c_str());
  
  static char *line=NULL;
  /* Return the memory to the free pool
     if the buffer has already been allocated. */
  if(line) {
    free(line);
    line=NULL;
  }
     
  /* Get a line from the user. */
  line=readline(prompt.c_str());
     
  if(!line) cout << endl;
  
  history_set_history_state(&history_save);

  return line ? string(line) : emptystring;
#else
  cout << prompt;
  string s;
  getline(cin,s);
  unused(&tabcompletion); // Avoid unused variable warning message.
  return s;
#endif
}

// Save a string in a local history named name.
// If store=true, store the local history in the file historyfilename(name).
void saveline(string name, string value, bool store=true)
{
#if defined(HAVE_LIBREADLINE) && defined(HAVE_LIBCURSES)
  store_history(&history_save);
  bool newhistory=historyMap.find(name) == historyMap.end();
  historyState& h=historyMap[name];
  h.store=store;
  HISTORY_STATE& history=h.state;
  history_set_history_state(&history);

  if(newhistory)
    read_history(historyfilename(name).c_str());
  
  if(value != "") {
    add_history(value.c_str());
    if(store) {
      std::ofstream hout(historyfilename(name).c_str(),std::ios::app);
      hout << value << endl;
    }
  }
  
  store_history(&history);
  history_set_history_state(&history_save);
#else
  unused(&store);
#endif   
}