/********************************************************************
This file is part of the abs 0.907 distribution.  abs is a spreadsheet
with graphical user interface.

Copyright (C) 1998-2001  André Bertin (Andre.Bertin@ping.be) 

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version if in the same spirit as version 2.

This program 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

Concact: abs@pi.be
         http://home.pi.be/bertin/abs.shtml

*********************************************************************/































#include "cell_vb.h"
#include "formula_interpret.h"
#include "libfct.h"
#include "application.h"
#include "worksheet.h"
#include "node.h"
#include "border_vb.h"
#include "memory.h"
#include "util.h"
#include "print.h"

obj
vb_absnewcell (narg, arg)
     int narg;
     obj *arg;
{
  obj o;
  Cell *cell = NULL;
  if (arg[0].type == INTEGER && arg[1].type == INTEGER
      && arg[2].type == STRING_CONSTANT)
    {
      cell = applicationcell (obj2int (arg[0]), obj2int (arg[1]), 1);
      cell_setformula (cell, obj2string (arg[2]));
    }
  o.rec.s = (char *) cell;
  o.type = CELL;
  o.label = arrayclass[CELL].name;
  return o;
}

obj
vb_getcell (narg, arg)
     int narg;
     obj *arg;
{
  obj o;
  Cell *cell;

  o.rec.s = (char *) applicationcell (obj2int (arg[0]), obj2int (arg[1]), 1);
  cell = (Cell *) o.rec.s;

  o.type = CELL;
  o.label = arrayclass[CELL].name;
  return o;
}

obj
vb_getrange1 (narg, arg)
     int narg;
     obj *arg;
{
  obj o;


  if (narg == 1)
    {
      if (arg[0].type == RANGE)
	return arg[0];

      o.rec.s = obj2string (arg[0]);
      o.type = RANGE;		
      o.label = NULL;
      return o;
    }
  return o;
}

obj
vb_get_ActiveCell ()
{
  obj o;
  o.rec.s = (char *) ActiveCell;
  o.type = CELL;
  o.label = NULL;
  return o;
}

extern int getselection (int *i1, int *j1, int *i2, int *j2);

obj
vb_get_Selection ()
{
  obj o;
  int i1, i2, j1, j2;
  getselection (&i1, &j1, &i2, &j2);
  if (i1 == i2 && j1 == j2)
    {
      o.rec.s = (char *) applicationcell (i1, j1, 1);
      o.type = CELL;
    }
  else
    {


    }
  o.label = NULL;
  return o;
}


obj
vb_cell_setobj (narg, arg)
     int narg;
     obj *arg;
{
  obj o;
  int r, c;
  tmpRange *ran;


  if (arg[0].type == CELL)
    {
      cell_setobj ((Cell *) arg[0].rec.s, arg[1]);
      return o;
    }

  if (arg[0].type == RANGE)
    {
      ran = (tmpRange *) arg[0].rec.s;
      if (ran->wks == NULL)
	{
	  for (r = ran->r1; r <= ran->r2; r++)
	    for (c = ran->c1; c <= ran->c2; c++)
	      cell_setobj (applicationcell (r, c, 1), arg[1]);
	}
      else
	{
	  for (r = ran->r1; r <= ran->r2; r++)
	    for (c = ran->c1; c <= ran->c2; c++)
	      cell_setobj (worksheet_getcell (ran->wks, r, c, 1), arg[1]);
	}
    }


  return o;
}

obj
vb_cell_getobj (narg, arg)
     int narg;
     obj *arg;
{
  obj o;
  o = cell_getobj ((Cell *) arg[0].rec.s);
  return o;
}

obj
vb_cell_setvalue (narg, arg)
     int narg;
     obj *arg;
{
  obj o;
  int r, c;
  tmpRange *ran;
  obj *cellORrange = (obj *) arg[0].rec.s;


  if (cellORrange->type == CELL)
    {
      cell_setobj ((Cell *) cellORrange->rec.s, arg[1]);
      return o;
    }

  if (cellORrange->type == RANGE)
    {
      ran = (tmpRange *) cellORrange->rec.s;
      if (ran->wks == NULL)
	{
	  for (r = ran->r1; r <= ran->r2; r++)
	    for (c = ran->c1; c <= ran->c2; c++)
	      cell_setobj (applicationcell (r, c, 1), arg[1]);
	}
      else
	{
	  for (r = ran->r1; r <= ran->r2; r++)
	    for (c = ran->c1; c <= ran->c2; c++)
	      cell_setobj (worksheet_getcell (ran->wks, r, c, 1), arg[1]);
	}
    }




  return o;
}

obj
vb_cell_getvalue (narg, arg)
     int narg;
     obj *arg;
{
  obj o;
  obj *cell = (obj *) arg[0].rec.s;

  o = cell_getobj ((Cell *) cell->rec.s);

  return o;
}

obj
vb_cell_setformula (narg, arg)
     int narg;
     obj *arg;
{
  obj o;
  obj *cell = (obj *) arg[0].rec.s;
  
  if (arg[1].type == STRING_CONSTANT)
    {				
      cell_setformula ((Cell *) cell->rec.s, obj2string (arg[1]));
    }
  return o;
}

obj
vb_cell_getformula (narg, arg)
     int narg;
     obj *arg;
{
  obj o;
  o.rec.d = cell_getvalue ((Cell *) arg[0].rec.s);
  o.type = DOUBLE;
  return o;

}

obj
vb_cell_sethalign (narg, arg)
     int narg;
     obj *arg;
{
  obj o;
  obj *cell = (obj *) arg[0].rec.s;


  cell_setjust ((Cell *) cell->rec.s, obj2int (arg[1]));
  return o;
}

obj
vb_cell_gethalign (narg, arg)
     int narg;
     obj *arg;
{
  obj o;
  o.rec.d = cell_getvalue ((Cell *) arg[0].rec.s);
  o.type = DOUBLE;
  return o;

}

obj
vb_cell_setnumform (narg, arg)
     int narg;
     obj *arg;
{
  obj o;
  obj *cell = (obj *) arg[0].rec.s;


  cell_setnumberformat ((Cell *) cell->rec.s, obj2string (arg[1]));
  return o;
}

obj
vb_cell_getnumform (narg, arg)
     int narg;
     obj *arg;
{
  obj o;
  o.rec.d = cell_getvalue ((Cell *) arg[0].rec.s);
  o.type = DOUBLE;
  return o;

}

obj
vb_cell_setinterior (narg, arg)
     int narg;
     obj *arg;
{
  obj o;
  obj *cell = (obj *) arg[0].rec.s;
  cell_setbg ((Cell *) cell->rec.s, obj2int (arg[1]));
  return o;
}

obj
vb_cell_getinterior (narg, arg)
     int narg;
     obj *arg;
{
  obj o;
  o.rec.d = cell_getvalue ((Cell *) arg[0].rec.s);
  o.type = DOUBLE;
  return o;

}

static Border bord;		

obj
vb_cell_getborder (narg, arg)
     int narg;
     obj *arg;
{
  obj o;


  bord.which = obj2int (arg[1]);	
  bord.object = arg[0];
  
  o.type = BORDER;
  o.label = NULL;
  o.rec.s = (char *) &bord;
  return o;
}

obj
vb_cell_getborders (narg, arg)
     int narg;
     obj *arg;
{
  obj o;
  bord.which = 0;
  bord.object = arg[0];
  o.type = BORDER;
  o.label = NULL;
  o.rec.s = (char *) &bord;
  return o;
}

obj
vb_cell_setborders (narg, arg)
     int narg;
     obj *arg;
{
  obj o;
  bord.which = 0;
  bord.object = arg[0];
  o.type = BORDER;
  o.label = NULL;
  o.rec.s = (char *) &bord;
  return o;
}




obj
vb_cell_assign (narg, arg)
     int narg;
     obj *arg;
{
  obj o;
  if (arg[0].type != CELL)
    return o;

  if (arg[1].type == CELL)
    {
      obj val = cell_getobj ((Cell *) arg[1].rec.s);
      cell_setobj ((Cell *) arg[0].rec.s, val);
    }
  else
    {
      cell_setobj ((Cell *) arg[0].rec.s, arg[1]);
    }

  return o;
}

obj
vb_cell_print (narg, arg)
     int narg;
     obj *arg;
{
  obj o = arg[0];
  if (arg[0].type == CELL)
    {
      printparam.print2file = 1;
      printparam.language = "ps";
      printparam.print2file = PORTRAIT;
      printparam.center = FLUSH_TOP_LEFT;
      printparam.magnification = 1.0;
      printparam.copies = 1;
      printparam.papertype = "A4";
      printparam.xshift = 0;
      printparam.yshift = 0;
      printparam.multiplepage = 0;
      printparam.filename = "output.ps";
      printfile (printparam);
      return o;
    }
  o.type = INTEGER;
  return o;
}

extern int setselection (int i1, int j1, int i2, int j2);

obj
vb_cell_select (narg, arg)
     int narg;
     obj *arg;
{
  obj o = arg[0];
  if (arg[0].type == CELL)
    {
      Cell *cell = (Cell *) arg[0].rec.s;
      setselection (cell->r, cell->c, cell->r, cell->c);
      return o;
    }
  if (arg[0].type == RANGE)
    {
      char *valuerange = arg[0].rec.s;
      char *i1j1;
      char *i2j2;
      char *separ;
      int r1, c1, r2, c2;
      if (valuerange == NULL)
	return o;
      separ = strchr (valuerange, ':');
      if (separ == NULL)
	return o;
      i1j1 = valuerange;
      i2j2 = separ + 1;
      separ[0] = '\0';
      alphatonum (i1j1, &r1, &c1);
      alphatonum (i2j2, &r2, &c2);
      setselection (r1, c1, r2, c2);
    }
  return o;
}





static int rrr, ccc;
static nodeType *wksname = NULL;
static int dr = 0;
static int dc = 0;
static int sr = -1;
static int sc = -1;

int
get_id2row ()
{
  return rrr;
}
int
get_id2col ()
{
  return ccc;
}

int
setdi (int i)
{
  dr = i;
  return 0;
}
int
setdj (int j)
{
  dc = j;
  return 0;
}
int
setsi (int i)
{
  sr = i;
  return 0;
}
int
setsj (int j)
{
  sc = j;
  return 0;
}

char *
newformula (str)
     char *str;
{				
				
				
				
				
  char tok;
  char token[32];
  char newstr[512];
  int letter, number;
  int lval, nval, v;
  int found;
  int i, j, k, spc, newj;
  int dollet, dolnum;

  j = 0;
  newj = 0;
  while (j < strlen (str))
    {
      found = 0;
      dolnum = dollet = 0;
      lval = nval = 0;

      
      if (str[j] == '\"')
	{
	  do
	    {
	      newstr[newj] = str[j];
	      j++;
	      newj++;
	    }
	  while (str[j] != '\"' && j < strlen (str));

	  if (str[j] == '\"')
	    {
	      newstr[newj] = str[j];
	      j++;
	      newj++;
	    }
	}

      i = j;
      tok = str[i];

      if (tok == '$')
	{
	  dollet = 1;
	  i++;
	}

      letter = 0;
      do
	{
	  tok = str[i];
	  v = isletter (tok);
	  if (v)
	    {
	      lval = lval * 26 + v;
	      letter = 1;
	      i++;
	    }
	  else
	    {
	      letter = 0;
	    }
	}
      while (letter);

      if (lval > 0)
	{			

	  
	  
	  if (tok == '$')
	    {
	      dolnum = 1;
	      i++;
	    }

	  do
	    {
	      tok = str[i];
	      v = isnumber (tok);
	      if (v >= 0)
		{
		  nval = nval * 10 + v;
		  number = 1;
		  i++;
		}
	      else
		{
		  number = 0;
		}
	    }
	  while (number);

	  if (nval > 0)
	    {			

	      
	      
	      spc = 0;
	      while (tok == ' ')
		{
		  i++;
		  spc++;
		  tok = str[i];
		}

	      if (tok != '!' && tok != '(')
		{
		  
		  found = 1;
		  if (!dollet && lval > sc)
		    lval += dc;
		  if (!dolnum && nval > sr)
		    nval += dr;

		  if (nval < 1)
		    nval = 1;
		  if (lval < 1)
		    lval = 1;
		  if (nval > ActiveWorksheet->nblin - 1)
		    nval = ActiveWorksheet->nblin - 1;
		  if (lval > ActiveWorksheet->nbcol - 1)
		    nval = ActiveWorksheet->nbcol - 1;

		  numtoalpha2 (token, nval, lval, dolnum, dollet);
		  for (k = 0; k < strlen (token); k++)
		    {
		      newstr[newj] = token[k];
		      newj++;

		    }
		  for (k = 0; k < spc; k++)
		    {
		      newstr[newj] = ' ';
		      newj++;
		    }
		  j = i;
		}

	    }			

	}			

      if (!found)
	{
	  for (k = j; k <= i; k++)
	    {
	      newstr[newj] = str[k];
	      newj++;
	    }
	  j = i + 1;
	}
    }
  newstr[newj] = '\0';

  str = (char *) absrealloc (str, strlen (newstr) + 5, "newformula");
  strcpy (str, newstr);


  return str;

}


int
check_if_id_is_cell (o)
     obj o;
{
  int v;
  int i;
  char tok;
  char *str = o.label;
  int dollet = 0;
  int dolnum = 0;
  int letter, number;
  int nval = 0;
  int lval = 0;


  i = 0;
  tok = str[i];
  if (tok == '$')
    {
      dollet = 1;
      i++;
    }
  letter = 0;
  do
    {
      tok = str[i];
      v = isletter (tok);
      if (v)
	{
	  lval = lval * 26 + v;
	  letter = 1;
	  i++;
	}
      else
	{
	  letter = 0;
	}
    }
  while (letter);

  if (lval > 0 && lval <= NB_COLUMN)
    {				

      
      
      if (tok == '$')
	{
	  dolnum = 1;
	  i++;
	}

      do
	{
	  tok = str[i];
	  v = isnumber (tok);
	  if (v >= 0)
	    {
	      nval = nval * 10 + v;
	      number = 1;
	      i++;
	    }
	  else
	    {
	      number = 0;
	    }
	}
      while (number);

      if (nval > 0)
	{			

	  if (!dolnum)
	    rrr = nval + dr;
	  else
	    rrr = nval;
	  if (!dollet)
	    ccc = lval + dc;
	  else
	    ccc = lval;
	  return 1;
	}			
    }				

  return 0;

}

static int rangeopr = 0;
static nodeType *sheet1 = NULL;
static int r1 = -1;
static int c1 = -1;
static nodeType *sheet2 = NULL;
static int r2 = -1;
static int c2 = -1;

obj
modcelltree (nodeType ** p)
{
  int i, nops;
  obj o;
  nodeType *wks;
  if (!*p)
    return o;



  switch ((*p)->type)
    {
    case typeCon:
      {
	return o;
      }
    case typeId:
      {
	if (check_if_id_is_cell ((*p)->id.id))
	  {			
	    obj r, c;
	    nodeType *argu;
	    c.type = r.type = INTEGER;
	    c.label = r.label = NULL;
	    r.rec.i = get_id2row ();
	    c.rec.i = get_id2col ();
	    if (rangeopr == 1)
	      {
		r1 = get_id2row ();
		c1 = get_id2col ();
		sheet1 = wksname;
		
	      }
	    if (rangeopr == 2)
	      {
		r2 = get_id2row ();
		c2 = get_id2col ();
		sheet2 = wksname;
		
	      }



	    if (ActiveWorksheet != NULL && wksname == NULL)
	      wks = id (createidentifier (ActiveWorksheet->Name));
	    else
	      wks = wksname;


	    if (wks == NULL)
	      {
		cell1isfcell2 (ActiveCell, applicationcell (r.rec.i, c.rec.i, 1));

		(*p)->id.id.label = "cells";
		(*p)->id.id.type = BUILTINFUNCTION;
		(*p)->id.id.rec.i = 0;
		
		
		
		
		argu = opr (ARG, "Arg", 1, con (r));	
		argu = opr (ARG, "Arg", 2, argu, con (c));	
	      }
	    else
	      {
		cell1isfcell2 (ActiveCell, applicationwkscell (wks->id.id.label, r.rec.i, c.rec.i, 1));

		(*p)->id.id.label = "wkscells";
		(*p)->id.id.type = BUILTINFUNCTION;
		(*p)->id.id.rec.i = 1;
		
		
		
		
		argu = opr (ARG, "Arg", 1, wks);	
		argu = opr (ARG, "Arg", 2, argu, con (r));	
		argu = opr (ARG, "Arg", 2, argu, con (c));	
	      }

	    (*p) = opr (BUILTINFUNCTION, "call", 2, *p, argu);
	    
	    
	    
	    return o;
	  }
	return o;
      }
    case typeOpr1:
      {
	o = modcelltree (&((*p)->opr1.op1));
	return o;
      }
    case typeOpr2:
      {
	o = modcelltree (&((*p)->opr2.op1));
	o = modcelltree (&((*p)->opr2.op2));
	return o;
      }
    case typeOpr:
      switch ((*p)->opr.oper.rec.i)
	{
	case '!':
	  {			
	    wksname = ((*p)->opr.op[0]);

	    
	    modcelltree (&(*p)->opr.op[1]);
	    
	    *p = (*p)->opr.op[1];
	    wksname = NULL;
	    return o;
	  }
	case ':':
	  {
	    int rr, cc;
	    obj tmp;
	    nodeType *argu;
	    nodeType *ranfct = id (o);
	    tmpRange *ran;
	    ranfct->id.id.label = "range_old";
	    ranfct->id.id.type = BUILTINFUNCTION;
	    ranfct->id.id.rec.i = 2;
	    rangeopr = 1;	
	    modcelltree (&(*p)->opr.op[0]);	
	    rangeopr = 2;
	    modcelltree (&(*p)->opr.op[1]);
	    rangeopr = 0;
	    

	    if (r1 < 0 || r2 < 0 || c1 < 0 || c2 < 0)
	      return o;
	    if (r2 < r1)
	      {
		rr = r2;
		r2 = r1;
		r1 = rr;
	      }
	    if (c2 < c1)
	      {
		cc = c2;
		c2 = c1;
		c1 = cc;
	      }
	    for (rr = r1; rr <= r2; rr++)
	      for (cc = c1; cc <= c2; cc++)
		if (sheet1 != NULL)
		  cell1isfcell2 (ActiveCell, applicationwkscell (sheet1->id.id.label, rr, cc, 1));
		else
		  cell1isfcell2 (ActiveCell, applicationcell (rr, cc, 1));


	    ran = (tmpRange *) absmalloc (sizeof (tmpRange), "cell_range:tmpRange");
	    if (sheet1 != NULL)
	      ran->wks = (Worksheet *) workbook_getsheet (ActiveWorkbook, sheet1->id.id.label);
	    else
	      ran->wks = ActiveCell->worksheet;		
	    ran->r1 = r1;
	    ran->c1 = c1;
	    ran->r2 = r2;
	    ran->c2 = c2;
	    tmp.type = RANGE;
	    tmp.rec.s = (char *) ran;
	    tmp.label = NULL;
	    argu = opr (ARG, "Arg", 1, con (tmp));



	    (*p) = opr (BUILTINFUNCTION, "call", 2, ranfct, argu);
	    sheet1 = NULL;
	    sheet2 = NULL;
	    r1 = c1 = r2 = c2 = -1;
	    return o;
	  }
	default:
	  {
	    nops = (*p)->opr.nops;
	    for (i = 0; i < nops; i++)
	      {
		
		o = modcelltree (&((*p)->opr.op[i]));
		
	      }
	  }
	  return o;
	}

    default:
      break;
    }
  return o;
}


int
cell_is_constant (Cell * cell)
{
  nodeType *p;

  if (cell == NULL)
    return 0;
  if (cell->tree == NULL)
    return 0;
  p = cell->tree;
  switch (p->type)
    {
    case typeCon:
      {
	if (p->con.value.type == INTEGER)
	  {
	    cell->val = p->con.value;
	    return 1;
	  }
	if (p->con.value.type == DOUBLE)
	  {
	    cell->val = p->con.value;
	    return 1;
	  }
      }
    case typeOpr1:
      {				
	if (p->opr1.op1->type == typeCon)
	  {
	    if (p->con.value.type == INTEGER)
	      {
		cell->val = p->con.value;
		cell->val.rec.i = -cell->val.rec.i;
		return 1;
	      }
	    if (p->con.value.type == DOUBLE)
	      {
		cell->val = p->con.value;
		cell->val.rec.d = -cell->val.rec.d;
		return 1;
	      }
	  }
      }
    default:
      return 0;
    }
  return 0;

}


int
setcelltree (tree)
     nodeType *tree;
{
  modcelltree (&tree);

  ActiveCell->tree = tree;
  if (dr || dc)
    ActiveCell->formula = newformula (ActiveCell->formula);
  
  return 0;
}

int
execcelltree (cell)
     Cell *cell;
{
  obj o2;
  
  
  o2 = exform (cell->tree);



  if (o2.type == IDENTIFIER)
    o2 = id2val (o2);

  if (o2.type == CELL)
    cell_setresval (cell, ((Cell *) o2.rec.s)->val);
  else
    cell_setresval (cell, o2);
  freenocstobj (o2);

  return 0;
}



static double *arraydouble = NULL;
static int dimarraydouble = 0;

double *
getarraydouble ()
{
  return arraydouble;
}
int 
freearraydouble ()
{
  if (arraydouble != NULL)
    absfree (arraydouble, "freearraydouble");
  arraydouble = NULL;
  return 0;
}

int 
sortarraydouble ()
{
  int i, k;			
  if (arraydouble == NULL)
    {
      return -1;
    }
  for (i = 0; i < dimarraydouble; i++)
    {
      for (k = i; k < dimarraydouble; k++)
	if (arraydouble[k] < arraydouble[i])
	  {
	    double l = arraydouble[k];
	    arraydouble[k] = arraydouble[i];
	    arraydouble[i] = l;
	  }
    }
  return 0;
}


int
arg2arraydouble (int narg, obj * arg)
{
  int dim = 0;
  int pos = 0;
  int i, r, c;

  for (i = 0; i < narg; i++)
    {
      if (arg[i].type == RANGE)
	{
	  tmpRange *ran = (tmpRange *) arg[i].rec.s;
	  dim += (ran->r2 - ran->r1 + 1) * (ran->c2 - ran->c1 + 1);
	  if (dim != 0);
	  arraydouble = (double *) absrealloc (arraydouble, dim * sizeof (double), "arg2arraydouble");
	  if (ran->wks == NULL)
	    {
	      for (r = ran->r1; r <= ran->r2; r++)
		for (c = ran->c1; c <= ran->c2; c++)
		  {
		    arraydouble[pos] = get_value (r, c);
		    pos++;
		  }
	    }
	  else
	    {
	      for (r = ran->r1; r <= ran->r2; r++)
		for (c = ran->c1; c <= ran->c2; c++)
		  {
		    arraydouble[pos] = get_value_wks (ran->wks, r, c);
		    pos++;
		  }
	    }
	}
      else
	{
	  dim++;
	  arraydouble = (double *) absrealloc (arraydouble, dim * sizeof (double), "arg2arraydouble");
	  arraydouble[pos] = obj2double (arg[i]);
	  pos++;
	}
    }
  dimarraydouble = dim;
  return dim;
}
