/*****
 * runsystem.in
 *
 * Runtime functions for system operations.
 *
 *****/

callable* => voidFunction()
callableBp* => breakpointFunction()
runnable* => primCode()

#include "asyprocess.h"
#include "stack.h"
#include "locate.h"

using namespace camp;
using namespace settings;
using vm::bpinfo;
using vm::bplist;
using vm::getPos;
using vm::Default;
using vm::nullfunc;
using vm::item;
using absyntax::runnable;

typedef callable callableBp;

namespace run {
extern string emptystring;
}

function *voidFunction()
{
  return new function(primVoid());
}

function *breakpointFunction()
{
  return new function(primString(),primString(),primInt(),primInt(),
                      primCode());
}

void clear(string file, Int line, bool warn=false)
{
  bpinfo bp(file,line);
  for(mem::list<bpinfo>::iterator p=bplist.begin(); p != bplist.end(); ++p) {
    if(*p == bp) {
      cout << "cleared breakpoint at " << file << ": " << line << endl;
      bplist.remove(bp);
      return;
    }
  }
  if(warn)
    cout << "No such breakpoint at "  << file << ": " << line << endl;
}

namespace run {
void breakpoint(stack *Stack, runnable *r)
{
  callable *atBreakpointFunction=processData().atBreakpointFunction;
  if(atBreakpointFunction &&
     !nullfunc::instance()->compare(atBreakpointFunction)) {
    position curPos=getPos();
    Stack->push<string>(curPos.filename());
    Stack->push<Int>((Int) curPos.Line());
    Stack->push<Int>((Int) curPos.Column());
    Stack->push(r ? r : vm::Default);
    atBreakpointFunction->call(Stack); // returns a string
  } else Stack->push<string>("");
}
}

string convertname(string name, const string& format)
{
  if(name.empty())
    return buildname(outname(),format,"");
  name=outpath(name);
  return format.empty() ? name : format+":"+name;
}

namespace run {
void purge(Int divisor=0)
{
#ifdef USEGC
  if(divisor > 0) GC_set_free_space_divisor((GC_word) divisor);
  GC_gcollect();
#endif
}

void updateFunction(stack *Stack)
{
  callable *atUpdateFunction=processData().atUpdateFunction;
  if(atUpdateFunction && !nullfunc::instance()->compare(atUpdateFunction))
    atUpdateFunction->call(Stack);
}

void exitFunction(stack *Stack)
{
  callable *atExitFunction=processData().atExitFunction;
  if(atExitFunction && !nullfunc::instance()->compare(atExitFunction))
    atExitFunction->call(Stack);
}
}

// Autogenerated routines:

string outname()
{
  return outname();
}

void atupdate(callable *f)
{
  processData().atUpdateFunction=f;
}

callable *atupdate()
{
  return processData().atUpdateFunction;
}

void atexit(callable *f)
{
  processData().atExitFunction=f;
}

callable *atexit()
{
  return processData().atExitFunction;
}

void atbreakpoint(callableBp *f)
{
  processData().atBreakpointFunction=f;
}

void breakpoint(runnable *s=NULL)
{
  breakpoint(Stack,s);
}

string locatefile(string file, bool full=true)
{
  return locateFile(file,full);
}

void stop(string file, Int line, runnable *s=NULL)
{
  file=locateFile(file);
  clear(file,line);
  cout << "setting breakpoint at " << file << ": " << line << endl;
  bplist.push_back(bpinfo(file,line,s));
}

void breakpoints()
{
  for(mem::list<bpinfo>::iterator p=bplist.begin(); p != bplist.end(); ++p)
    cout << p->f.name() << ": " << p->f.line() << endl;
}

void clear(string file, Int line)
{
  file=locateFile(file);
  clear(file,line,true);
}

void clear()
{
  bplist.clear();
}

void warn(string s)
{
  Warn(s);
}

void nowarn(string s)
{
  noWarn(s);
}

void warning(string s, string t, bool position=false)
{
  if(settings::warn(s)) {
    em.warning(position ? getPos() : nullPos,s);
    em << t;
    em.sync(true);
  }
}

// Strip directory from string
string stripdirectory(string *s)
{
  return stripDir(*s);
}

// Strip directory from string
string stripfile(string *s)
{
  return stripFile(*s);
}

// Strip file extension from string
string stripextension(string *s)
{
  return stripExt(*s);
}

// Call ImageMagick convert.
Int convert(string args=emptystring, string file=emptystring,
            string format=emptystring)
{
  string name=convertname(file,format);
  mem::vector<string> cmd;
  string s=getSetting<string>("convert");
  cmd.push_back(s);
  push_split(cmd,args);
  for(size_t i=1; i<cmd.size(); ++i) {
    checkread(cmd[i]);
    checkwrite(cmd[i]);
  }
  cmd.push_back(name);
  bool quiet=verbose <= 1;

  char *oldPath=NULL;
  string dir=stripFile(outname());
  if(!dir.empty()) {
    oldPath=getPath();
    setPath(dir.c_str());
  }
  Int ret=System(cmd,quiet ? 1 : 0,true,"convert",
                 "your ImageMagick convert utility");
  if(oldPath != NULL)
    setPath(oldPath);

  if(ret == 0 && verbose > 0)
    cout << "Wrote " << (file.empty() ? name : file) << endl;

  return ret;
}

// Call ImageMagick animate.
Int animate(string args=emptystring, string file=emptystring,
            string format=emptystring)
{
  string name=convertname(file,format);
  if(view()) {
    mem::vector<string> cmd;
    string s=getSetting<string>("animate");
    cmd.push_back(s);
    if(s == "magick")
      cmd.push_back("animate");
    push_split(cmd,args);
    for(size_t i=1; i<cmd.size(); ++i) {
      checkread(cmd[i]);
      checkwrite(cmd[i]);
    }
    cmd.push_back(name);
    return System(cmd,0,false,"animate","your animated GIF viewer");
  }
  return 0;
}

void purge(Int divisor=0)
{
  purge(divisor);
}
