/*{{{}}}*/
/*{{{  #includes*/
#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>

#include "cat.h"
#include "eval.h"
#include "func.h"
#include "misc.h"
#include "parser.h"
#include "scanner.h"
#include "sheet.h"
/*}}}  */

/*{{{  @*/
static Token at(int argc, const Token argv[])
{
  /*{{{  variables*/
  Token result;
  /*}}}  */

  /*{{{  asserts*/
  assert(argv!=(Token*)0);
  /*}}}  */
  if (argc==1 && argv[0].type==LOCATION)
  /*{{{  return value at location pointed to by argument*/
  return (getvalue(upd_sheet,argv[0].u.location[0],argv[0].u.location[1],argv[0].u.location[2]));
  /*}}}  */
  else if (argc==2 && argv[0].type==INT && argv[1].type==INT)
  /*{{{  return value at x,y on current z*/
  return (getvalue(upd_sheet,argv[0].u.integer,argv[1].u.integer,upd_z));
  /*}}}  */
  else if (argc==3 && argv[0].type==INT && argv[1].type==INT && argv[2].type==INT)
  /*{{{  return value at x,y,z*/
  return (getvalue(upd_sheet,argv[0].u.integer,argv[1].u.integer,argv[2].u.integer));  
  /*}}}  */
  else
  /*{{{  return error*/
  {
    result.type=EEK;
    result.u.err=strcpy(malloc(strlen(EEKAT)+1),EEKAT);
    return result;
  }
  /*}}}  */
}
/*}}}  */
/*{{{  &*/
static Token adr(int argc, const Token argv[])
{
  /*{{{  variables*/
  Token result;
  /*}}}  */

  /*{{{  asserts*/
  assert(argv!=(Token*)0);
  /*}}}  */
  if (argc==3 && argv[0].type==INT && argv[1].type==INT && argv[2].type==INT)
  /*{{{  result is location of the given position*/
  {
    result.type=LOCATION;
    result.u.location[0]=argv[0].u.integer;
    result.u.location[1]=argv[1].u.integer;
    result.u.location[2]=argv[2].u.integer;
  }
  /*}}}  */
  else if (argc==2 && argv[0].type==INT && argv[1].type==INT)
  /*{{{  result is location of the given position in the current z layer*/
  {
    result.type=LOCATION;
    result.u.location[0]=argv[0].u.integer;
    result.u.location[1]=argv[1].u.integer;
    result.u.location[2]=upd_z;
  }
  /*}}}  */
  else
  /*{{{  result is type error*/
  {
    result.type=EEK;
    result.u.err=strcpy(malloc(strlen(EEKPTR)+1),EEKPTR);
  }
  /*}}}  */
  return result;
}
/*}}}  */
/*{{{  error*/
static Token error(int argc, const Token argv[])
{
  /*{{{  variables*/
  Token result;
  /*}}}  */

  /*{{{  asserts*/
  assert(argv!=(Token*)0);
  /*}}}  */
  result.type=EEK;
  if (argc!=1 || argv[0].type!=STRING)
  /*{{{  result is type error*/
  result.u.err=strcpy(malloc(strlen(EEKERROR)+1),EEKERROR);
  /*}}}  */
  else
  /*{{{  result is user defined error*/
  result.u.err=strcpy(malloc(strlen(argv[0].u.string)+1),argv[0].u.string);
  /*}}}  */
  return result;
}
/*}}}  */
/*{{{  here.x*/
static Token here_x(int argc, const Token argv[])
{
  /*{{{  variables*/
  Token result;
  /*}}}  */

  if (argc==0)
  /*{{{  result is currently updated x position*/
  {
    result.type=INT;
    result.u.integer=upd_x;
  }
  /*}}}  */
  else if (argc==1 && argv[0].type==LOCATION)
  /*{{{  return x component of location*/
  {
    result.type=INT;
    result.u.integer=argv[0].u.location[0];
  }
  /*}}}  */
  else
  /*{{{  here.x type error*/
  {
    result.type=EEK;
    result.u.err=strcpy(malloc(strlen(HEREXTYPE)+1),HEREXTYPE);
  }
  /*}}}  */
  return result;
}
/*}}}  */
/*{{{  here.y*/
static Token here_y(int argc, const Token argv[])
{
  /*{{{  variables*/
  Token result;
  /*}}}  */

  if (argc==0)
  /*{{{  result is currently updated y position*/
  {
    result.type=INT;
    result.u.integer=upd_y;
  }
  /*}}}  */
  else if (argc==1 && argv[0].type==LOCATION)
  /*{{{  return y component of location*/
  {
    result.type=INT;
    result.u.integer=argv[0].u.location[1];
  }
  /*}}}  */
  else
  /*{{{  here.y type error*/
  {
    result.type=EEK;
    result.u.err=strcpy(malloc(strlen(HEREYTYPE)+1),HEREYTYPE);
  }
  /*}}}  */
  return result;
}
/*}}}  */
/*{{{  here.z*/
static Token here_z(int argc, const Token argv[])
{
  /*{{{  variables*/
  Token result;
  /*}}}  */

  if (argc==0)
  /*{{{  result is currently updated z position*/
  {
    result.type=INT;
    result.u.integer=upd_z;
  }
  /*}}}  */
  else if (argc==1 && argv[0].type==LOCATION)
  /*{{{  return z component of location*/
  {
    result.type=INT;
    result.u.integer=argv[0].u.location[2];
  }
  /*}}}  */
  else
  /*{{{  result is here.z type error*/
  {
    result.type=EEK;
    result.u.err=strcpy(malloc(strlen(HEREZTYPE)+1),HEREZTYPE);
  }
  /*}}}  */
  return result;
}
/*}}}  */
/*{{{  ctos*/
static Token ctos(int argc, const Token argv[])
{
  /*{{{  variables*/
  Token result;
  char *buf;
  size_t size;
  int x,y,z;
  /*}}}  */

  if (argc==1 && argv[0].type==LOCATION)
  /*{{{  x,y,z is given location*/
  {
    x=argv[0].u.location[0];
    y=argv[0].u.location[0];
    z=argv[0].u.location[0];
  }
  /*}}}  */
  else if (argc==2 && argv[0].type==INT && argv[1].type==INT)
  /*{{{  x,y are given, z is z of currently updated cell*/
  {
    x=argv[0].u.integer;
    y=argv[0].u.integer;
    z=upd_z;
  }
  /*}}}  */
  else if (argc==3 && argv[0].type==INT && argv[1].type==INT && argv[2].type==INT)
  /*{{{  x,y,z are given*/
  {
    x=argv[0].u.integer;
    y=argv[0].u.integer;
    z=argv[2].u.integer;
  }
  /*}}}  */
  else
  /*{{{  return ctos type error  */
  {
    result.type=EEK;
    result.u.err=strcpy(malloc(strlen(CTOSTYPE)+1),CTOSTYPE);
    return result;
  }
  /*}}}  */
  /*{{{  convert cell to string  */
  buf=(char*)0;
  size=0;
  do
  {
    if (buf!=(char*)0) free(buf);
    size+=16;
    buf=malloc(size);
    printvalue(buf,size,0,0,getscientific(upd_sheet,x,y,z),getprecision(upd_sheet,x,y,z),upd_sheet,x,y,z);
  } while (strlen(buf)==size-1);
  result.type=STRING;
  result.u.string=buf;
  /*}}}  */
  return result;
}
/*}}}  */
/*{{{  sum*/
static Token sum(int argc, const Token argv[])
{
  /*{{{  variables*/
  Token result;
  /*}}}  */

  if (argc==2 && argv[0].type==LOCATION && argv[1].type==LOCATION)
  /*{{{  result is sum*/
  {
    /*{{{  variables*/
    int x,y,z;
    int x1,y1,z1;
    int x2,y2,z2;
    Token tmp;
    /*}}}  */

    x1=argv[0].u.location[0]; x2=argv[1].u.location[0]; order(&x1,&x2);
    y1=argv[0].u.location[1]; y2=argv[1].u.location[1]; order(&y1,&y2);
    z1=argv[0].u.location[2]; z2=argv[1].u.location[2]; order(&z1,&z2);
    result.type=EMPTY;
    for (x=x1; x<=x2; ++x)
    for (y=y1; y<=y2; ++y)
    for (z=z1; z<=z2; ++z)
    {
      Token t;

      tmp=tadd(result,t=getvalue(upd_sheet,x,y,z));
      tfree(&t);
      tfree(&result);
      result=tmp;
      if (result.type==EEK) return result;
    }
  }
  /*}}}  */
  else
  /*{{{  result is sum type error*/
  {
    result.type=EEK;
    result.u.err=strcpy(malloc(strlen(SUMTYPE)+1),SUMTYPE);
  }
  /*}}}  */
  return result;
}
/*}}}  */
/*{{{  n*/
static Token n(int argc, const Token argv[])
{
  /*{{{  variables*/
  Token result;
  /*}}}  */

  if (argc==2 && argv[0].type==LOCATION && argv[1].type==LOCATION)
  /*{{{  result is number of elements*/
  {
    /*{{{  variables*/
    int x,y,z;
    int x1,y1,z1;
    int x2,y2,z2;
    Token tmp;
    int n;
    /*}}}  */

    x1=argv[0].u.location[0]; x2=argv[1].u.location[0]; order(&x1,&x2);
    y1=argv[0].u.location[1]; y2=argv[1].u.location[1]; order(&y1,&y2);
    z1=argv[0].u.location[2]; z2=argv[1].u.location[2]; order(&z1,&z2);
    n=0;
    for (x=x1; x<=x2; ++x)
    for (y=y1; y<=y2; ++y)
    for (z=z1; z<=z2; ++z)
    {
      tmp=getvalue(upd_sheet,x,y,z);
      if (tmp.type!=EMPTY) ++n;
      tfree(&tmp);
    }
    result.type=INT;
    result.u.integer=n;
  }
  /*}}}  */
  else
  /*{{{  result is n type error*/
  {
    result.type=EEK;
    result.u.err=strcpy(malloc(strlen(NTYPE)+1),NTYPE);
  }
  /*}}}  */
  return result;
}
/*}}}  */
/*{{{  int*/
static Token int_func(int argc, const Token argv[])
{
  /*{{{  variables*/
  Token result;
  /*}}}  */

  if (argc==1 && argv[0].type==FLOAT)
  /*{{{  result is integer with cutoff fractional part*/
  {
    result.type=INT;
    result.u.integer=(long)(argv[0].u.flt);
  }
  /*}}}  */
  else
  /*{{{  result is int type error*/
  {
    result.type=EEK;
    result.u.err=strcpy(malloc(strlen(INTTYPE)+1),INTTYPE);
  }
  /*}}}  */
  return result;
}
/*}}}  */
/*{{{  frac*/
static Token frac(int argc, const Token argv[])
{
  /*{{{  variables*/
  Token result;
  double foo;
  /*}}}  */

  if (argc==1 && argv[0].type==FLOAT)
  /*{{{  result is fractional part*/
  {
    result.type=FLOAT;
    result.u.flt=modf(argv[0].u.flt,&foo);
  }
  /*}}}  */
  else
  /*{{{  result is frac type error*/
  {
    result.type=EEK;
    result.u.err=strcpy(malloc(strlen(FRACTYPE)+1),FRACTYPE);
  }
  /*}}}  */
  return result;
}
/*}}}  */
/*{{{  len*/
static Token len(int argc, const Token argv[])
{
  /*{{{  variables*/
  Token result;
  /*}}}  */

  if (argc==1 && argv[0].type==STRING)
  /*{{{  result is length*/
  {
    result.type=INT;
    result.u.integer=strlen(argv[0].u.string);
  }
  /*}}}  */
  else
  /*{{{  result is frac type error*/
  {
    result.type=EEK;
    result.u.err=strmalloc(LENTYPE);
  }
  /*}}}  */
  return result;
}
/*}}}  */
/*{{{  min*/
static Token min(int argc, const Token argv[])
{
  /*{{{  variables*/
  Token result;
  /*}}}  */

  if (argc==2 && argv[0].type==LOCATION && argv[1].type==LOCATION)
  /*{{{  result is min*/
  {
    /*{{{  variables*/
    int x,y,z;
    int x1,y1,z1;
    int x2,y2,z2;
    Token tmp;
    /*}}}  */

    x1=argv[0].u.location[0]; x2=argv[1].u.location[0]; order(&x1,&x2);
    y1=argv[0].u.location[1]; y2=argv[1].u.location[1]; order(&y1,&y2);
    z1=argv[0].u.location[2]; z2=argv[1].u.location[2]; order(&z1,&z2);
    result=getvalue(upd_sheet,x1,y1,z1);
    for (x=x1; x<=x2; ++x)
    for (y=y1; y<=y2; ++y)
    for (z=z1; z<=z2; ++z)
    {
      Token t;

      tmp=tle(result,t=getvalue(upd_sheet,x,y,z));
      if (tmp.type==INT && tmp.u.integer==0)
      {
        tfree(&result);
        result=t;
      }
      else tfree(&t);
      tfree(&tmp);
      if (result.type==EEK) return result;
    }
    return result;
  }
  /*}}}  */
  else
  /*{{{  result is min type error*/
  {
    result.type=EEK;
    result.u.err=strmalloc(MINTYPE);
    return result;
  }
  /*}}}  */
}
/*}}}  */
/*{{{  abs*/
static Token doabs(int argc, const Token argv[])
{
  /*{{{  variables*/
  Token result;
  /*}}}  */

  if (argc==1 && argv[0].type==FLOAT)
  /*{{{  result is absolute floating point number*/
  {
    result.type=FLOAT;
    result.u.flt=fabs(argv[0].u.flt);
  }
  /*}}}  */
  else if (argc==1 && argv[0].type==INT)
  /*{{{  result is absolute integer number*/
  {
    result.type=INT;
    result.u.integer=(argv[0].u.integer<0 ? -argv[0].u.integer : argv[0].u.integer);
  }
  /*}}}  */
  else
  /*{{{  result is abs type error*/
  {
    result.type=EEK;
    result.u.err=strcpy(malloc(strlen(ABSTYPE)+1),ABSTYPE);
  }
  /*}}}  */
  return result;
}
/*}}}  */

/*{{{  table of functions*/
Tfunc tfunc[]=
{
  { "@", at },
  { "&", adr },
  { "error", error },
  { "here.x", here_x },
  { "here.y", here_y },
  { "here.z", here_z },
  { "ctos", ctos },
  { "sum", sum },
  { "n", n },
  { "int", int_func },
  { "frac", frac },
  { "len", len },
  { "min", min },
  { "abs", doabs },
  { (const char*)0, (Token (*)(int, const Token[]))0 }
};
/*}}}  */
