/*****
 * plain.asy
 * Andy Hammerlindl and John Bowman 2004/08/19
 *
 * A package for general purpose drawing, with automatic sizing of pictures.
 *
 *****/

access settings;

if(settings.command != "") {
  string s=settings.command;
  settings.command="";
  settings.multipleView=settings.batchView=settings.interactiveView;
  _eval(s+";",false,true);
  exit();
}

include plain_constants;

access version;             
if(version.VERSION != VERSION) {
  warning("version","using possibly incompatible version "+
          version.VERSION+" of plain.asy"+'\n');
  nowarn("version");
}
   
include plain_strings;
include plain_pens;
include plain_paths;
include plain_filldraw;
include plain_margins;
include plain_picture;
include plain_Label;
include plain_shipout;
include plain_arcs;
include plain_boxes;
include plain_markers;
include plain_arrows;
include plain_debugger;

typedef void exitfcn();

bool needshipout() {
  return !shipped && !currentpicture.empty();
}

void updatefunction()
{
  if(!currentpicture.uptodate) shipout();
}

void exitfunction()
{
  if(needshipout()) shipout();
}

atupdate(updatefunction);
atexit(exitfunction);

// A restore thunk is a function, that when called, restores the graphics state
// to what it was when the restore thunk was created.
typedef void restoreThunk();
typedef restoreThunk saveFunction();
saveFunction[] saveFunctions={};

// When save is called, this will be redefined to do the corresponding restore.
void restore()
{
  warning("nomatchingsave","restore called with no matching save");
}

void addSaveFunction(saveFunction s)
{
  saveFunctions.push(s);
}

restoreThunk buildRestoreThunk()
{
  // Call the save functions in reverse order, storing their restore thunks.
  restoreThunk[] thunks={};
  for (int i=saveFunctions.length-1; i >= 0; --i)
    thunks.push(saveFunctions[i]());

  return new void() {
    // Call the restore thunks in an order matching the saves.
    for (int i=thunks.length-1; i >= 0; --i)
      thunks[i]();
  };
}

// Add the default save function.
addSaveFunction(new restoreThunk () {
    pen defaultpen=defaultpen();
    pen p=currentpen;
    picture pic=currentpicture.copy();
    restoreThunk r=restore;
    return new void() {
      defaultpen(defaultpen);
      currentpen=p;
      currentpicture=pic;
      currentpicture.uptodate=false;
      restore=r;
    };
  });

// Save the current state, so that restore will put things back in that state.
restoreThunk save() 
{
  return restore=buildRestoreThunk();
}

void restoredefaults()
{
  warning("nomatchingsavedefaults",
          "restoredefaults called with no matching savedefaults");
}

restoreThunk buildRestoreDefaults()
{
  pen defaultpen=defaultpen();
  exitfcn atupdate=atupdate();
  exitfcn atexit=atexit();
  restoreThunk r=restoredefaults;
  return new void() {
    defaultpen(defaultpen);
    atupdate(atupdate);
    atexit(atexit);
    restoredefaults=r;
  };
}

// Save the current state, so that restore will put things back in that state.
restoreThunk savedefaults() 
{
  return restoredefaults=buildRestoreDefaults();
}

void initdefaults()
{
  savedefaults();
  resetdefaultpen();
  atupdate(null);
  atexit(null);
}

// Return the sequence n,...m
int[] sequence(int n, int m)
{
  return sequence(new int(int x){return x;},m-n+1)+n;
}

int[] reverse(int n) {return sequence(new int(int x){return n-1-x;},n);}
bool[] reverse(bool[] a) {return a[reverse(a.length)];}
int[] reverse(int[] a) {return a[reverse(a.length)];}
real[] reverse(real[] a) {return a[reverse(a.length)];}
pair[] reverse(pair[] a) {return a[reverse(a.length)];}
triple[] reverse(triple[] a) {return a[reverse(a.length)];}
string[] reverse(string[] a) {return a[reverse(a.length)];}

// Return a uniform partition dividing [a,b] into n subintervals.
real[] uniform(real a, real b, int n)
{
  if(n <= 0) return new real[];
  return a+(b-a)/n*sequence(n+1);
}

void eval(string s, bool embedded=false)
{
  if(!embedded) initdefaults();
  _eval(s+";",embedded);
  if(!embedded) restoredefaults();
}

void eval(code s, bool embedded=false)
{
  if(!embedded) initdefaults();
  _eval(s,embedded);
  if(!embedded) restoredefaults();
}

// Evaluate user command line option.
void usersetting()
{
  eval(settings.user,true);
}

string stripsuffix(string f, string suffix=".asy") 
{
  int n=rfind(f,suffix);
  if(n != -1) f=erase(f,n,-1);
  return f;
}

// Conditionally process each file name in array s in a new environment.
void asy(string format, bool overwrite=false ... string[] s)
{
  for(string f : s) {
    f=stripsuffix(f);
    string suffix="."+format;
    string fsuffix=f+suffix;
    if(overwrite || error(input(fsuffix,check=false))) {
      string outformat=settings.outformat;
      bool interactiveView=settings.interactiveView;
      bool batchView=settings.batchView;
      settings.outformat=format;
      settings.interactiveView=false;
      settings.batchView=false;
      string outname=outname();
      delete(outname+"_"+".aux");
      eval("import \""+f+"\" as dummy");
      rename(stripsuffix(outname)+suffix,fsuffix);
      settings.outformat=outformat;
      settings.interactiveView=interactiveView;
      settings.batchView=batchView;
    }
  }
}

void beep()
{
  write('\7',flush);
}

struct processtime {
  real user;
  real system;
}

struct cputime {
  processtime parent;
  processtime child;
  processtime change;
}

cputime cputime() 
{
  static processtime last;
  real [] a=_cputime();
  cputime cputime;
  cputime.parent.user=a[0];
  cputime.parent.system=a[1];
  cputime.child.user=a[2];
  cputime.child.system=a[3];
  real user=a[0]+a[2];
  real system=a[1]+a[3];
  cputime.change.user=user-last.user;
  cputime.change.system=system-last.system;
  last.user=user;
  last.system=system;
  return cputime;
}

string cputimeformat="%#.2f";

void write(file file, string s="", cputime c, string format=cputimeformat,
           suffix suffix=none)
{
  write(file,s,
        format(format,c.change.user)+"u "+
        format(format,c.change.system)+"s "+
        format(format,c.parent.user+c.child.user)+"U "+
        format(format,c.parent.system+c.child.system)+"S ",suffix);
}

void write(string s="", cputime c, string format=cputimeformat,
           suffix suffix=endl)
{
  write(stdout,s,c,format,suffix);
}

if(settings.autoimport != "") {
  string s=settings.autoimport;
  settings.autoimport="";
  eval("import \""+s+"\" as dummy",true);
  shipped=false;
  atupdate(updatefunction);
  atexit(exitfunction);
  settings.autoimport=s;
}

cputime();
