#include "stdio.h"
#include "stdlib.h"
#include "conio.h"
#include "string.h"
#include "ctype.h"
#include "C:\TC3\KENCALC\minicalc.h"



int consentito(int char_da_valutare, char *char_precedente, int buf)
/* dice se il carattere e' compatibile col tipo di buffer richiesto */
// se char_precedente e' '\0' si presuppone che char_da valutare
// sia il primo; assume anche che alle sucessive chiamate si faccia
// sempre riferimento allo stesso tipo di buffer
// PPPPSSSS in formula ammette la divisione per ZERO !!!!!!!!
// quindi bisogna impedire la divisione per 0 esplicita
{
   int a;
   const char *pm = "+-";
   const char *pd = "*/";
   const char *l = "aAbBcCdDeEfFgGhH";
   char tipo_c,
	tipo_cp;


   switch (buf)
   {
    case BUF_PHRASER:
    case BUF_COMMENTO:

     if (isascii(char_da_valutare))
      a = CONSENTITO;
     else
      a = NONCONSENTITO;
    break;

    case BUF_FORMULA:
      if(isdigit(char_da_valutare))
       tipo_c=CIFRA;
      else if(strchr(pm,char_da_valutare)!=NULL )
       tipo_c=PIUOMENO;
      else if(strchr(pd,char_da_valutare)!=NULL )
       tipo_c=PERODIVISO;
      else if(strchr(l,char_da_valutare)!=NULL )
       tipo_c=CARATTERE;
      else if(char_da_valutare=='.')
       tipo_c=VIRGOLA;
      else if(char_da_valutare=='(')
       tipo_c=PARENTESIA;
      else if(char_da_valutare==')')
       tipo_c=PARENTESIC;
      else return NONCONSENTITO;

      if(*char_precedente=='\0')
       {
	switch (tipo_c)
	case CIFRA:
	case PIUOMENO:
	case CARATTERE:
	case PARENTESIA:
	 return CONSENTITO;
	default:
	 return NONCONSENTITO;
       }
      else if(isdigit(*char_precedente))
       tipo_cp=CIFRA;
      else if(strchr(pm,*char_precedente)!=NULL )
       tipo_cp=PIUOMENO;
      else if(strchr(pd,*char_precedente)!=NULL )
       tipo_cp=PERODIVISO;
      else if(strchr(l,*char_precedente)!=NULL )
       tipo_cp=CARATTERE;
      else if(*char_precedente=='.')
       tipo_cp=VIRGOLA;
      else if(*char_precedente=='(')
       tipo_cp=PARENTESIA;
      else if(*char_precedente==')')
       tipo_cp=PARENTESIC;
      else return NONCONSENTITO;


  switch(tipo_cp)
  {
   case CIFRA:
	     switch(tipo_c)
	     {
	      int i;

	      case CIFRA:
	      if(isdigit(*(char_precedente-1))&&isalpha(*(char_precedente-2)))
		return NONCONSENTITO;
	       else if(isalpha(*(char_precedente-1)))
		    {
		     if((char_da_valutare>'5') || (*char_precedente>'1'))
		      a = NONCONSENTITO;
		     else
		      a = CONSENTITO;
		    }
		    else
		     a=CONSENTITO;
	      break;

	      case PARENTESIA:
	      case CARATTERE:
	      a = NONCONSENTITO;
	      break;

	      case VIRGOLA:
	       for(i=0;isdigit(*(char_precedente-i));i++);

	       if(*(char_precedente-i)=='.')
		a=NONCONSENTITO;
	       else
		a=CONSENTITO;
	      break;

	      default:
	      a = CONSENTITO;
	      break;
	     }

  break;

   case PIUOMENO:
   case PERODIVISO:

    if((*char_precedente=='/')&&(char_da_valutare=='0'))
     return NONCONSENTITO;

		   switch(tipo_c)
		   {
		    case CIFRA:
		    case PARENTESIA:
		    case CARATTERE:
		     a = CONSENTITO;
		    break;

		    default:
		     a = NONCONSENTITO;
		    break;
		   }
   break;
   case CARATTERE:
		   switch(tipo_c)
		   {
		    case CIFRA:
		     a = CONSENTITO;
		    break;

		    default:
		     a = NONCONSENTITO;
		    break;
		   }
   break;

   case VIRGOLA:
		if (tipo_c!=CIFRA)
		 a = NONCONSENTITO;
		else
		 a=CONSENTITO;
   break;

   case PARENTESIA:
		   switch(tipo_c)
		   {
		    case PARENTESIC:
		    case PERODIVISO:
		    case VIRGOLA:
		     a = NONCONSENTITO;
		    break;

		    default:
		     a = CONSENTITO;
		    break;
		   }
   break;

   case PARENTESIC:
		   switch(tipo_c)
		   {
		    case PARENTESIA:
		    case PERODIVISO:
		    case PIUOMENO:
		     a = CONSENTITO;
		    break;

		    default:
		     a = NONCONSENTITO;
		    break;
		   }
   break;

  }
 break;




    case BUF_VALORE :

     if(isdigit(char_da_valutare))
      tipo_c=CIFRA;
     else if(strchr(pm,char_da_valutare)!=NULL )
      tipo_c=PIUOMENO;
     else if(char_da_valutare=='.')
      tipo_c=VIRGOLA;
     else return NONCONSENTITO;

     if(*char_precedente=='\0')
      {
       switch (tipo_c)
       {
	case CIFRA:
	case PIUOMENO:
	 return CONSENTITO;
	default:
	 return NONCONSENTITO;
       }
      }
     else if(isdigit(*char_precedente))
      tipo_cp=CIFRA;
     else if(strchr(pm,*char_precedente)!=NULL )
      tipo_cp=PIUOMENO;
     else if(*char_precedente=='.')
      tipo_cp=VIRGOLA;
     else return NONCONSENTITO;

     switch (tipo_cp)
     {
      int i;

      case CIFRA:
		switch (tipo_c)
		{
		 case VIRGOLA:
		  for(i=0;isdigit(*(char_precedente-i));i++);

		  if(*(char_precedente-i)=='.')
		   a=NONCONSENTITO;
		  else
		   a=CONSENTITO;
		 break;

		 case CIFRA:
		  a=CONSENTITO;
		 break;

		 default:
		  a=NONCONSENTITO;
		 break;
		}
      break;

      case VIRGOLA:
       if (tipo_c!=CIFRA)
	a = NONCONSENTITO;
       else
	a=CONSENTITO;
      break;

      default:
       a=NONCONSENTITO;
      break;

     }

    break;
   }


  return a;

}

int iscommand(int c)
/* restituisce TRUE se c e' un comando altrimenti FALSE */
{
  switch (c)
  {
     case F2           :
     case F3           :
     case TAB          :
     case F10          :
     case TASTOCANC    :
     case TASTOHOME    :
     case TASTOFINE    :
     case TASTOSU      :
     case TASTOGIU     :
     case TASTODESTRA  :
     case TASTOSINISTRA:
     case INVIO        :
     case ESC          :
       return TRUE;
     default:
       return FALSE;
  }
}

int valuta(char *string, int buffer_type, int col, int rig)
// da inserire tutti i controlli di ciclicita'
{
  PTRLISTAPUNTATORI puntatoreacoordinate;
  PTRFORMULA puntatoreaformula;
  FLOATINGPOINT risultato;
  int err,a;
  char *endptr;

  switch (buffer_type)
  {
   case BUF_COMMENTO:
    err = inserisci_commento(col, rig, string);
   break;

   case BUF_VALORE:
//    prossimo_carattere(string ,SPAZIONONAMMESSO, INIZIALIZZA);
//    err = inserisci_valore(col, rig, valore());
    err = inserisci_valore(col, rig, (FLOATINGPOINT)strtod(string, &endptr));
    if (err != NOERROR) return err;
    err = ricalcola_celle_riferite(col,rig);

   break;


    case BUF_FORMULA:

// cerca ciclicita
     err = ricorsiva_ciclicita(col,rig,string);
      if (err != NOERROR) return err;

// inserisce la formula come testo e come albero
     err = inserisci_testo_formula(col, rig, string);
      if (err != NOERROR) return err;

// inserisce la formula come albero dopo averla convertita
     puntatoreaformula = converti_formula(string);
      if (puntatoreaformula == NULL)
      {
       cancella_testo_formula(col,rig);
       return OUTOFMEM;
      }
      else
       inserisci_formula(col, rig, puntatoreaformula);

// inserisce i riferimenti a ritroso in tutte le celle riferite

     prossimo_carattere(string , INIZIALIZZA);
     a = vai_fino_a_lettera();

     while (a!=BUFLIMITE)
     {
      PTRLISTAPUNTATORI mod_temp;

      puntatoreacoordinate = leggi_coordinate();
      mod_temp = crea_modulo_lista(col,rig);
       if(mod_temp == NULL)
	{
	 prossimo_carattere(string , INIZIALIZZA);
	 a = vai_fino_a_lettera();
	 cancella_riferimenti_formula(col,rig);
	 cancella_testo_formula(col,rig);
	 cancella_formula(col,rig);
	 return OUTOFMEM;
	}
       else
	{
	 err = inserisci_riferimento
	      (
		get_col_riferimento(puntatoreacoordinate),
		get_rig_riferimento(puntatoreacoordinate),
		mod_temp
	      );
	 a = vai_fino_a_lettera();
	}
     }

// calcola il risultato
     if ((err=calcolabile(string))==NOERROR)
     {
      int errval;
      risultato = calcola_formula(puntatoreaformula);
      errval = inserisci_valore(col, rig, risultato);
      if (errval != NOERROR)
      {
       cancella_riferimenti_formula(col,rig);
       cancella_testo_formula(col,rig);
       cancella_formula(col,rig);
       cancella_valore(col,rig);
       return err;
      }
// ricalcola le celle riferite

      err = ricalcola_celle_riferite(col,rig);
      if (err != NOERROR)
      {
       cancella_riferimenti_formula(col,rig);
       cancella_testo_formula(col,rig);
       cancella_formula(col,rig);
       cancella_valore(col,rig);
       cancella_valori_riferiti(col,rig);
       return err;
      }
      return err;
     }
     else
     return err;

//   break;

 }
  return NOERROR;
}

int valuta_tipo(char *dati)
/* ritorna il tipo di dato presente in dati */
{

  int r;
  char *x;
  x=dati;


    for (x=dati;
	   (r=consentito((int)*x, x-1, BUF_VALORE))&&((*(x+1))!='\0');
	     x++);
     x++;
     if ((*x == '\0')&&(r==CONSENTITO))
      return BUF_VALORE;



    for(x=dati;
	   (r=consentito((int)*x, x-1, BUF_FORMULA))&&((*(x+1))!='\0');
	     x++);
     x++;
     if ((*x == '\0')&&(corretta(dati)==NOERROR)&&(r==CONSENTITO))
      return BUF_FORMULA;

   return BUF_COMMENTO;

}


int buffer_libero(int col, int rig, int buf)
{
 int r;

 switch (buf)
  {
   case BUF_COMMENTO:
    if (get_dati_buffer(col, rig, BUF_COMMENTO)==NULL)
      r = CONSENTITO;
     else
      r = NONCONSENTITO;
   break;

   case BUF_VALORE:
   case BUF_FORMULA:
    if((get_dati_buffer(col, rig, BUF_VALORE)==NULL) &&
       (get_dati_buffer(col, rig, BUF_FORMULA)==NULL)  )
      r = CONSENTITO;
     else
      r = NONCONSENTITO;
   break;

   case BUF_PHRASER:
    if(((get_dati_buffer(col, rig, BUF_COMMENTO)!=NULL) &&
	(get_dati_buffer(col, rig, BUF_VALORE)!=NULL)) ||
       ((get_dati_buffer(col, rig, BUF_COMMENTO)!=NULL) &&
	(get_dati_buffer(col, rig, BUF_FORMULA)!=NULL)))
      r = NONCONSENTITO;
     else
      r = CONSENTITO;
   break;
  }

 return r;

}



int cerca_ciclicita(PTRLISTAPUNTATORI ptr_in_lista, char *form)
{
  PTRLISTAPUNTATORI ptracoordinate;
  int gcrpac,grrpac,gcrpil,grrpil;
  int a;

     prossimo_carattere(form , INIZIALIZZA);
     if((vai_fino_a_lettera())!=BUFLIMITE)
     {
      do
      {
	ptracoordinate = leggi_coordinate();

	gcrpac = get_col_riferimento(ptracoordinate);
	grrpac = get_rig_riferimento(ptracoordinate);
	gcrpil = get_col_riferimento(ptr_in_lista);
	grrpil = get_rig_riferimento(ptr_in_lista);
	a = vai_fino_a_lettera();

      }
      while(!((a==BUFLIMITE)||((gcrpac == gcrpil)&&(grrpac == grrpil))));

      if ((gcrpac == gcrpil) && (grrpac == grrpil))
      return CICLICITA;
     }

  return NOERROR;

}



int ricorsiva_ciclicita(int colonna, int riga, char *stringa)
{
  int err;
  PTRLISTAPUNTATORI puntatoreinlista, ptrlistacellainiziale;

  ptrlistacellainiziale = crea_modulo_lista(colonna, riga);
  if((cerca_ciclicita(ptrlistacellainiziale, stringa) == NOERROR))
   {
     puntatoreinlista = get_testa_riferimento(colonna, riga);

    if (puntatoreinlista !=NULL)
    {
     do
     {
       err =  cerca_ciclicita (puntatoreinlista, stringa);

       if (err == NOERROR)
       {
	  err = ricorsiva_ciclicita(get_col_riferimento(puntatoreinlista),
				  get_rig_riferimento(puntatoreinlista),
				  stringa);
	   if (err == NOERROR)
	     puntatoreinlista = puntatoreinlista->prossimo_ptr;

       }
     }
     while ((err==NOERROR) && (puntatoreinlista != NULL));

    }
    else err = NOERROR;

   }
   else
    err = CICLICITA;

  free(ptrlistacellainiziale);
  return err;

}

int calcolabile(char *i)
{
  PTRLISTAPUNTATORI ptra;
  int err;
  FLOATINGPOINT *valor;
  FLOATINGPOINT maxval_calcolabile;
  char *maxval_in_char="999999999999999";
  char *endptr;

  maxval_calcolabile=strtod(maxval_in_char,endptr);

     prossimo_carattere(i , INIZIALIZZA);
     if((vai_fino_a_lettera())!=BUFLIMITE)
     {
      do
      {
       ptra = leggi_coordinate();
       valor = (FLOATINGPOINT *)get_dati_buffer(get_col_riferimento(ptra),
						get_rig_riferimento(ptra),
						BUF_VALORE );

       err = vai_fino_a_lettera();

      } // sedici cifre sono le cifre significative
     while(!((err==BUFLIMITE)||(valor==NULL)||(*valor>maxval_calcolabile)));

       if (valor == NULL)
	return VALOREMANCANTE;
       else if(*valor>maxval_calcolabile)
	return NUMBERTOOBIG;
     }

     prossimo_carattere(i , INIZIALIZZA);
     if((vai_a_divisione_con_rif())!=BUFLIMITE)
     {
      do
      {
       ptra = leggi_coordinate();
       valor = (FLOATINGPOINT *)get_dati_buffer(get_col_riferimento(ptra),
						get_rig_riferimento(ptra),
						BUF_VALORE );

       err = vai_a_divisione_con_rif();

      }
      while(!((err==BUFLIMITE)||(*valor==0)));

       if (*valor == 0)
	return DIVISIONEPERZERO;
     }

  return NOERROR;

}


int ricalcola_celle_riferite(int colonna, int riga)
{
  PTRLISTAPUNTATORI puntatoreinlista;
  int c,r;
  int err;

  puntatoreinlista = get_testa_riferimento(colonna, riga);

      if (puntatoreinlista !=NULL)
      {
       do
       {
	c = get_col_riferimento(puntatoreinlista);
	r = get_rig_riferimento(puntatoreinlista);
	err=calcolabile((char *)get_dati_buffer(c,r, BUF_FORMULA ));

     if(err==NOERROR)
	  {
	   err = inserisci_valore(c, r,(calcola_formula(get_albero(c,r))));
	   if (err!=NOERROR)
	    return err;
	   else
	   {
	    scrivi_cella(c, r);
	   }
	  }
     else
	  {
	   switch (err)
	    {
	      case NUMBERTOOBIG:
	       riquadra_cella(c+1,r+1, 4); //EGA_RED
	      break;

	      case DIVISIONEPERZERO:
	       riquadra_cella(c+1,r+1, 62);// EGA_YELLOW
	      break;

	      case VALOREMANCANTE:
	       riquadra_cella(c+1,r+1, 63);// EGA_WHITE
	      break;
	    }
	  }
	 ricalcola_celle_riferite(c,r);
	 puntatoreinlista = puntatoreinlista->prossimo_ptr;

       }
       while (puntatoreinlista != NULL);

      }
  return NOERROR;
}

int corretta (char *formula_da_valutare)
// controlla che le parentesi siano ammissibili e che l'espressione
// sia finita.. sempre che vi sia un'espressione..altrimenti NOERROR
{
 int aperta=0,
     chiusa=0,
     almeno_un_operatore=FALSE;

 if(*(formula_da_valutare)=='\0')
  return NOERROR;

 for( ; *formula_da_valutare!='\0'; formula_da_valutare++)
 {
  switch (*formula_da_valutare)
  {
   case '(':
    aperta++;
   break;

   case ')':
    if (aperta>chiusa)
     chiusa++;
    else
     return PARENTESISBAGLIATE;
   break;

   case '+':
   case '*':
   case '-':
   case '/':
    almeno_un_operatore=TRUE;
   break;

   default:
   break;

  }

 }

 if (aperta!=chiusa)
  return PARENTESISBAGLIATE;
 else if (( isdigit(*(formula_da_valutare-1))||
	   (*(formula_da_valutare-1)==')')     )  &&
	   (almeno_un_operatore==TRUE)
	 )
       return NOERROR;
      else
       return ESPRESSIONENONCOMPLETA;
}









