#include <ctype.h>
#include <limits.h>
#include <stdlib.h>

#include "strings.h"

void TContainer::for_each( OPERATION_FUNCTION op )
{
  TObject* curr = first_item();
  while(curr)
  {
    op(*curr);
    curr = succ_item();
  } 
}

void TContainer::for_each_that( OPERATION_FUNCTION op, CONDITION_FUNCTION match )
{
  TObject* curr =  first_that( match );
  while( curr )
  {
    op( *curr );
    curr = succ_that( );
  } 
}

TObject* TContainer::first_that( CONDITION_FUNCTION match )
{
  _last_condition = match;
  if( _last_condition )
  {
    TObject* curr = first_item( );
    while( curr && !_last_condition( *curr ) )
      curr = succ_item( );
    return curr;      
  }
  else
    return first_item( );  
}

TObject* TContainer::last_that( CONDITION_FUNCTION match )
{
  _last_condition = match;
  if( _last_condition )
  {
    TObject* curr = last_item( );
    while( curr && !_last_condition( *curr ) )
      curr = pred_item( );
    return curr;      
  }
  else
    return last_item( );  
}

TObject* TContainer::succ_that( )
{
  if( _last_condition )
  {
    TObject* curr = succ_item( );
    while( curr && !_last_condition( *curr ) )
      curr = succ_item( );
    return curr;      
  }
  else
    return succ_item( );
}

TObject* TContainer::pred_that( )
{
  if( _last_condition )
  {
    TObject* curr = pred_item( );
    while( curr && !_last_condition( *curr ) )
      curr = pred_item( );
    return curr;      
  }
  else
    return pred_item( );
}

long TContainer::count( CONDITION_FUNCTION match )
{
  long c = 0;
  TObject* curr = first_item( );
  while( curr )
    if( match( *curr ) )
      c++;
  return c;    
}

TObject* TArray::first_item( )
{
  _scanindex = first( ); 
  return ( _scanindex == size( ) )?NULL:_data[ _scanindex ]; 
};

TObject* TArray::last_item( )
{ 
  _scanindex = last( ); 
  return ( _scanindex == -1 )?NULL:_data[ _scanindex ]; 
};

TObject* TArray::succ_item( )
{ 
  _scanindex = succ( _scanindex ); 
  return ( _scanindex == size( ))?NULL:_data[ _scanindex ]; 
};

TObject* TArray::pred_item( )
{ 
  _scanindex = pred( _scanindex ); 
  return ( _scanindex == -1)?NULL:_data[ _scanindex ]; 
};

// @doc EXTERNAL

// @mfunc Modifica la dimensione dell'array.
void TArray::resize(
  int arraysize) // @parm Indica la nuova dimensione che deve assumere l'array.

  // @comm Alloca la memoria necessaria per contentere i dati della nuova
  //       dimensione inizializzando a NULL tutti i nuovi elementi aggiunti
  //       all'array lasciando inalterati, sia in posizione che in
  //       contenuto gli elementi gia' presenti.
  //       <nl>Nel caso si cerchi di diminuire la dimensione viene dato un
  //       messaggio di errore.

{
  CHECK(arraysize > _size, "Can't reduce array size.");

  TObject** newdata = new TObject* [arraysize];
  memset(newdata, 0, arraysize*sizeof(TObject*));
  if (_data != NULL)
	{
    memcpy(newdata, _data, _size*sizeof(TObject*));
    delete _data;
	}
  _size = arraysize;
  _data = newdata;
}

// @doc EXTERNAL

// @mfunc Rimuove uno o tutti (default) gli elementi
// @rdesc Ritorna uno dei seguenti parametri:
//
// @flag TRUE | Se l'operazione e' riuscita con successo
// @flag FALSE | Se il numero di elementi e' rimasto invariato
bool TArray::destroy(
  int index, // @parm Numero dell'elemento da eliminare (default -1)
  bool pack) // @parm Se true richiama la funzione <mf TArray::pack> per compattare gli elementi dell'array (default FALSE)

  // @comm Permette la rimozione di uno (index > 0 ) o di tutti (default) gli
  //       elementi dell'array assegnandone il valore NULL.
  //       <nl>Nel caso il parametro pack sia TRUE permette di rendere contigui
  //       tutti gli elementi diversi da NULL tramite la chiamata alla funzione
  //       <mf TArray::pack>.
  
{
  const int old = _items;

  if (index < 0)
  {
    for (int i = last(); i >= 0; i--) if (_data[i] != NULL)
    {
      delete _data[i];
      _data[i] = NULL;
    }
    _items = _next = 0;
  }
  else
  {
    TObject* o = remove(index, pack);
    if (o) delete o;
  }

  return _items < old;
}


TArray::TArray(int arraysize)   
      : _size(0), _items(0), _next(0), _data(NULL)
{
  if (arraysize) 
    resize(arraysize);
}

TArray::TArray() 
      : _size(0), _items(0), _next(0), _data(NULL)
{
}

TArray::TArray(const TArray& a) 
      : _size(0), _items(0), _next(0), _data(NULL)
{
  (*this) = a;
}              

TArray& TArray::operator= (const TArray& a)
{
  destroy();
  if (size() < a.size()) 
    resize(a.size());
  for (int i = a.size()-1; i >= 0; i--) 
  {
    const TObject* o = a.objptr(i);
    if (o != NULL) 
    {
      _data[i] = o->dup();
#ifdef DBG
      const long vir_tab1 = *((long*)o);
      const long vir_tab2 = *((long*)_data[i]);
      if (vir_tab1 != vir_tab2)
        NFCHECK("Duplicazione di elementi di array pericolosa");
#endif    
    }
  }  
  _items = ( int )a.items();
  _next = ( int )a._next;  
  
  return *this;
}              

TArray::~TArray()
{
  if (ok())
  {
    destroy();
    delete _data;
  }
}

const char* TArray::class_name() const
{
  return "Array";
}


word TArray::class_id() const
{
  return CLASS_ARRAY;
}

// @doc EXTERNAL

// @mfunc Stampa un array
void TArray::print_on(
  ostream& out) const // @parm indica l'output sul quale reindirizzare la stampa (funzione standard del C++)

  // @comm Permette di stampare sull'output passato come parametro il contenuto
  //       dell'array, preceduto dal numero dell'elemetno a cui si riferisce.
  //       <nl>Nel caso vi siano degli elementi vuoti (valore = NULL) viene
  //       stampato il valore (null).

{
  for (int i = 0; i < size(); i++)
  {
    out.width(4);
    out << i << ' ';
    out.width();
    if (_data[i] != NULL) out << *_data[i];
    else out << "(null)";
    out << endl;
  }
}
    
// @doc EXTERNAL
    
// @mfunc Controlla se si tratta di un oggetto valido
//
// @rdesc Ritorna uno dei seguenti parametri:
// @flag TRUE | Se l'array contiene degli elementi
// @flag FALSE | Se l'array e' vuoto
bool TArray::ok() const

// @comm Controlla se la dimensione dell'array e' uguale a 0 e se esistono
//       degli elementi diversi da NULL

{
  return(size() != 0 && (_data != NULL));
}

#ifdef DBG
TObject& TArray::operator[] (int index) const
{
  TObject* o = objptr(index);
  if (o == NULL)
  {
    NFCHECK("Can't access NULL array item %d of %d", index, _size);
    const int f = last();
    if (f >= 0) 
    {
      o = objptr(f)->dup();
      ((TArray*)this)->add(o, index);
    } 
    CHECK(o, "Unrecoverable array access error");
  }
  return *o;
}
#endif

// @doc EXTERNAL

// @mfunc Aggiunge un oggetto ad un array.
//
// @rdesc La funzione ritorna la posizione nella quale e' stato aggiunto
//        l'oggetto.
int TArray::add(
  TObject *object, // @parm Oggetto da aggiungere nell'array
  int index)       // @parm Posizione in cui aggiungere l'oggetto
  // @parm TObject | &object | Oggetto da aggiungere nell'array. L'oggetto viene duplicato

  // @syntax add(TObject* object,int index);
  // @syntax add(TObject& object,int index);
  //
  // @comm Nel caso venga passata una posizione minore di 0 l'elemento viene aggiunto
  //       in coda all'array, diversamente viene l'elemento presente viene
  //       sostitito con l'oggetto passato nei prametri.
  //       <nl>Se e' stato passato un indice minore della dimensione dell'array,
  //       viene aumentata automaticamente la dimensione dell'array stesso tramite
  //       la chiamata alla funzione <mf TArray::resize>.
  //       <nl><nl>ATTENZIONE: Nel caso si passi l'oggetto per indirizzo deve
  //       essere definita la funzione <mf TObject::dup>.
  //
  // @xref <mf TArray::insert>
{
  if (index < 0) 
    for (index = _next; index < size() && _data[index]; index++);
  if (index >= size()) 
  {
    long newsize = 3L * index / 2 + 1;
    if (newsize > INT_MAX)
      newsize = INT_MAX;
    resize((int)newsize);
  }  

  if (object != _data[index]) // Evita ricopiatura su se stesso
  {
    if (_data[index] != NULL)
    {
      delete _data[index];
      _items--;
    }

    _data[index] = object;
    if (object != NULL) 
    {
      _items++;
      _next = index+1;
    }  
    else
    { 
      if (index < _next)
        _next = index;
    }
  }
  return index;
}

// @doc EXTERNAL

// @mfunc Inserisce un elemento dell'array nella posizione index
//
// @rdesc La funzione ritorna la posizione nella quale e' stato inserito l'oggetto.
int TArray::insert(
  TObject *object, // @parm Oggetto da inserire nell'array
  int index,       // @parm Posizione in cui inserire l'oggetto (puo' essere -1)
  bool force)      // @parm Forza l'inserimento anche se la posizione e' vuota
  // @parm TObject | &object | Oggetto da inserire nell'array. L'oggetto viene duplicato

  // @syntax insert(TObject* object, int index);
  // @syntax insert(TObject& object, int index);
  //
  // @comm Nel caso l'elemento della posizione passata sia diverso da NULL vengono
  //       spostati di una posizione tutti gli elementi presenti nell'array.
  //       La dimensione dell'array viene automaticamente aumentata nel caso
  //       la stessa non sia sufficiente a contenere il nuovo oggetto.
  //       <nl><nl>ATTENZIONE: Nel caso si passi l'oggetto per indirizzo deve
  //       essere definita la funzione <mf TObject::dup>.
  //
  // @xref <mf TArray::add>
{
  const int s = size();                                  
  if ((index >= 0 && index < s) && (force || objptr(index)))
  {         
    if (_data[s - 1])  // Se l'ultimo non e' vuoto ...
      add(NULL, s);    // ... aggiungi un elemento in fondo
    for (int i = size()-1; i > index; i--)   // size va aggiornato perche potrebbe essere cambiato
      _data[i] = _data[i - 1];
    _data[index] = NULL;
  }
  return add(object, index);
}

int TArray::add(const TObject& object, int index)
{
  TObject* objptr = object.dup();
  return add(objptr, index);
}

int TArray::insert(const TObject& object, int index, bool force)
{
  TObject* objptr = object.dup();
  return insert(objptr, index, force);
}

// @doc EXTERNAL

// @mfunc Elimina l'elemento nella posizione index dell'array
//
// @rdesc Ritorna l'elemento dell'array eliminato senza farne la delete
TObject* TArray::remove(
  int index,   // @parm Indica la posizione dell'elemento da eliminare
  bool dopack) // @parm Indica se si vuole richiamare la funzione
  //       <mf TArray::pack> (default FALSE) per spostare gli elementi successivi dell'array
{     
  CHECKD(index >= 0, "Can't remove array item ", index);

  TObject* o = objptr(index);
  if (o)
  {
    _data[index] = NULL;
    _items--;
    if (index < _next)
      _next = index;
  }
  if (dopack && index < size()) 
  {                                 
    const int last = size()-1;
    for (int i = index ; i < last; i++)
      _data[i] = _data[i+1];
    _data[last] = NULL;  
  }
  return o;
}

TObject* TArray::remove_item(bool pack ) 
{
  return remove(_scanindex,pack);
}

void TArray::swap(int i1, int i2)
{
  TObject* o1 = remove(i1, FALSE);
  TObject* o2 = remove(i2, FALSE);
  if (o1) add(o1, i2);
  if (o2) add(o2, i1);
}

// @doc EXTERNAL

// @mfunc Cerca il prossimo oggetto dell'array
//
// @rdesc Ritorna l'indice del prossimo elemento trovato o size() se non esiste
int TArray::succ(int i) const

{               
  CHECKD(i >= -1, "Bad array index ", i);
  for (i++; i < size(); i++)
    if (_data[i]) break;
  return i;  
}

// @doc EXTERNAL

// @mfunc Cerca il precedente oggetto dell'array
//
// @rdesc Ritorna l'indice dell'elemento precedente o -1 se non esiste
int TArray::pred(int i) const

{
  CHECKD(i <= size(), "Bad array index ", i);
  for (i--; i >= 0; i--)
    if (_data[i]) break;
  return i;  
}

// @doc EXTERNAL

// @mfunc Cerca il primo oggetto dell'array
//
// @rdesc Ritorna l'indice del primo elemento trovato o size() se l'array e' vuoto
int TArray::first() const

{
  return succ(-1);
}

// @doc EXTERNAL

// @mfunc Cerca l'ultimo oggetto dell'array
//
// @rdesc Ritorna l'indice dell'ultimo elemento trovato o -1 se l'array e' vuoto
int TArray::last() const

{
  return pred(size());
}

// @doc EXTERNAL

// @mfunc Rende contigui tutti gli elementi non nulli
void TArray::pack()

  // @comm Riordina gli elementi dell'array facendo che tra di loro non ve ne
  //       siano con valore uguale a NULL.
{
  int next = 0;
  for (int i = 0; i < size(); i++)
  {
    if (_data[i] != NULL)
    {
      if (next < i)
      {
        _data[next] = _data[i];
        _data[i] = NULL;
      }
      next++;
    }
  }
  _next = next;
}

// @doc INTERNAL

// @func Funzione per permettere il confonto tra 2 oggetti.
//
// @rdesc Ritorna i seguenti valori
//
// @flag <gt>0 | se <p this> <gt> <p s>
// @flag 0 | se <p this> == <p s>
// @flag <lt>0 | se <p this> <lt> <p s>
// @flag UNDEFINED | se l'ordine non e' definito  
static int sortable_compare(
  const TObject** o1, // @parm Primo oggetto da confrontare
  const TObject** o2) // @parm Secondo oggetto da confrontare

  // @comm E' utilizzata dalla funzione <mf TArray::sort> come default per
  //       stabilire il criteri di ordinamento degli oggetti passati.
{                                             
  if (o1 == o2)
    return 0; // qsort a volte confronta un oggetto con se stesso: per cui ne approfitto!

  const TSortable* s1 = (const TSortable*)*o1;
  const TSortable* s2 = (const TSortable*)*o2;
  return s1->compare(*s2);
}

// @doc EXTERNAL

// @mfunc Ordina i TObject secondo il criterio definito in <t COMPARE_FUNCTION>
void TArray::sort(
  COMPARE_FUNCTION compare) // @parm Funzione indicante il criterio di ordinamento (default TSortable)

  // @comm Nel caso non venga passata nessuna funzione che permetta di conforntare
  //       i due oggetti viene utilizzato il criterio <c Tsortable>
{
  typedef int (*qsortfunc)(const void*, const void*);
  if (compare == NULL) compare = sortable_compare;

  pack();
  qsort(_data, items(), sizeof(TObject*), (qsortfunc)compare);
}

///////////////////////////////////////////////////////////
// TPointer_array
///////////////////////////////////////////////////////////

// @doc EXTERNAL

// @mfunc Rimuove uno o tutti (default) gli elementi
// @rdesc Ritorna uno dei seguenti parametri:
//
// @flag TRUE | Se l'operazione e' riuscita con successo
// @flag FALSE | Se il numero di elementi e' rimasto invariato
bool TPointer_array::destroy(
  int index, // @parm Numero dell'elemento da eliminare (default -1)
  bool pack) // @parm Se true richiama la funzione <mf TArray::pack> per compattare gli elementi dell'array (default FALSE)

  // @comm Permette la rimozione di uno (index > 0 ) o di tutti (default) gli
  //       elementi dell'array assegnandone il valore NULL.
  //       <nl>Nel caso il parametro pack sia TRUE permette di rendere contigui
  //       tutti gli elementi diversi da NULL tramite la chiamata alla funzione
  //       <mf TArray::pack>.
  
{            
  if (data())
  {
    if (index < 0)
      memset(data(), 0, size() * sizeof(TObject*));
    else
      remove(index);
  }
  return TArray::destroy(index, pack);
}

void TPointer_array::copy(const TArray& a)
{
  destroy();
  if (size() < a.size()) 
    resize(a.size());
  
//  for (int i = a.size()-1; i >= 0; i--)
//    add(a.objptr(i), i);
  memcpy(data(), a.data(), a.size() * sizeof(TObject*));
  _items = a._items;
  _next = a._next;
}              

int TPointer_array::add(TObject* object, int index)
{        
  if (index >= 0)
    remove(index);
  return TArray::add(object, index);
}

int TPointer_array::add(const TObject& object, int index)
{                     
  return add(&(TObject&)object, index);
}

int TPointer_array::insert(const TObject& object, int index, bool force)
{
  return TArray::insert(&(TObject&)object, index, force);
}

///////////////////////////////////////////////////////////
// TBit_array
///////////////////////////////////////////////////////////

TBit_array::TBit_array(long size) 
          : _bit(NULL), _size(0)
{
  if (size)
    resize(index(size));
}

// @doc EXTERNAL

// @mfunc Copia l'array passato come parametro
void TBit_array::copy(
  const TBit_array& ba) // @parm Array da copiare

  // @comm Copia il contenuto dell'array passato come parametro.
{
  if (_bit)
  {
    delete _bit;
    _bit = NULL;
    _size = 0;
  }
  resize(ba._size-1);
  memcpy(_bit, ba._bit, _size);
}

TBit_array::TBit_array(const TBit_array& ba) : _bit(NULL), _size(0)
{       copy(ba); }

TBit_array& TBit_array::operator=(const TBit_array& ba)
{
  copy(ba);
  return *this;
}


TBit_array::~TBit_array()
{
  if (_bit)
    delete _bit;
}

// Certified 100%
void TBit_array::set()
{
  if (_bit) 
    memset(_bit, 0xFF, _size);
}

// Certified 100%
void TBit_array::reset()
{
  if (_bit) 
    memset(_bit, 0x0, _size);
}

// Certified 99%
// @doc EXTERNAL
//
// @mfunc Modifica la dimensione dell'array.
void TBit_array::resize(
  word size) // @parm Indica la nuova dimensione che deve assumere l'array.

  // @comm Alloca la memoria necessaria per contentere i dati della nuova
  //       dimensione inizializzando a NULL tutti i nuovi elementi aggiunti
  //       all'array lasciando inalterati, sia in posizione che in
  //       contenuto gli elementi gia' presenti.
{
  word oldsize = _size;
  byte* oldbit = _bit;

  _size = size+1;
  _bit = new byte[_size];
  reset();

  if (oldsize)
  {
    memcpy(_bit, oldbit, oldsize);
    delete oldbit;
  }
}

bool TBit_array::operator[] (long n) const
{
  const word i = index(n);
  if (i >= _size) return FALSE;
  return (_bit[i] & mask(n)) != 0;
}

// Certified 99%
TBit_array& TBit_array::operator|= (const TBit_array& ba)
{
  if (_size < ba._size)
    resize(ba._size);

  for (word i = 0; i < _size; i++)
    _bit[i] |= ba._bit[i];

  return *this;
}

// Certified 99%
void TBit_array::set(long n)
{
  CHECKD(n >= 0, "Can't set a negative bit ", n);
	const word i = index(n);
  if (i >= _size)
    resize(i);
  _bit[i] |= mask(n);
}

// Certified 99%
void TBit_array::reset(long n)
{
  const word i = index(n);
  if (i < _size)
    _bit[i] &= ~mask(n);
}

// Certified 99%
void TBit_array::not(long n)
{
  const word i = index(n);
  if (i >= _size) resize(i);
  _bit[i] ^= mask(n);
}

// Certified 90%
// @doc EXTERNAL

// @mfunc Ritorna il numero di 1 presenti nell'array
//
// @rdesc Numero di 1 trovati.
long TBit_array::ones() const

// @comm Passa tutto l'array di bit e conta quanti degli elementi presenti sono
//       settati ad 1.
//
// @xref <mf TBit_array::last_one> <mf TBit_array::first_one>
{
  long one = 0;
  for (word i = 0; i < _size; i++)
  {
    const byte b = _bit[i];
    if (b)
    {
      for (byte m = 1; m; m <<= 1)
        if (b & m) one++;
    }
  }
  return one;
}

// Certified 90%
// @doc EXTERNAL

// @mfunc Ritorna la posizione dell'ultimo 1 nell'array
//
// @rdesc Ritorna i seguenti valori:
// @flag >0 | Posizione nella quale si trova l'ultimo 1
// @flag -1 | Se non sono presenti bit settati ad 1

long TBit_array::last_one() const

// @comm Cerca all'interno dell'array di bit la posizione dell'ultimo bit
//       settato ad 1
//
// @xref <mf TBit_array::first_one> <mf TBit_array::ones>
{
  for (word i = _size; i--;)
  {
    const byte b = _bit[i];
    if (b)
    {
      for (byte j = 8; j--;)
        if ((1<<j) & b) return (long(i)<<3) + j;
    }
  }
  return -1;
}

// Certified 90%
// @doc EXTERNAL

// @mfunc Ritorna la posizione del primo 1 nell'array
//
// @rdesc Ritorna i seguenti valori:
//
// @flag >0 | Posizione nella quale si trova il primo 1
// @flag -1 | Se non sono presenti bit settati ad 1

long TBit_array::first_one() const

// @comm Cerca all'interno dell'array di bit la posizione del primo bit
//       settato ad 1
//
// @xref <mf TBit_array::last_one> <mf TBit_array::ones>
{
  for (word i = 0; i < _size; i++)
  {
    const byte b = _bit[i];
    if (b)
    {
      for (byte j = 0; j < 8; j++)
        if ((1<<j) & b) return (long(i)<<3)+j;
    }
  }
  return -1;
}

// @doc EXTERNAL

// @mfunc Controlla se si tratta di un oggetto valido
//
// @rdesc Ritorna uno dei seguenti parametri:
//
// @flag TRUE | Se l'array contiene degli elementi
// @flag FALSE | Se l'array e' vuoto
bool TBit_array::ok() const

// @comm Controlla se la dimensione dell'array e' maggiore di 0 e se esistono
//       degli elementi diversi da NULL
{
  return _bit != NULL && _size > 0;
}


void TBit_array::set(const char* numbers)
{
  TToken_string s(numbers, ' ');
  for (const char* n = s.get(0); n; n = s.get())
    if (isdigit(*n)) set(atol(n));
}

// @doc EXTERNAL
//
// @mfunc Stampa un array
void TBit_array::print_on(
  ostream& out) const // @parm Indica l'output sul quale reindirizzare la stampa (funzione standard del C++)

  // @comm Stampa tutti gli indici degli elementi diversi da 0
{
  const long last = _size<<3;
  for (long i = 1; i < last; i++)
    if (operator[](i)) out << ' ' << i;  
}