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

#include <array.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 INTERNAL

// @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];

  int i = 0;
  if (_data != NULL)
    for (i = 0; i < size(); i++) newdata[i] = _data[i];

  while (i < arraysize) newdata[i++] = NULL;

  if (_data != NULL) delete _data;

  _size = arraysize;
  _data = newdata;
}

// @doc EXTERNAL
//
// @mfunc Rimuove uno o tutti (default) gli elementi
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>.
  //
  // @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
{
  const int old = _items;

  if (index < 0)
  {
    for (int i = size(); 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();
  }  
  _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;
}

// @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;
  }
}

// @mfunc Controlla se si tratta di un oggetto valido
bool TArray::ok() const

// @rdesc Ritorna uno dei seguenti parametri:
//
// @flag TRUE | Se l'array contiene degli elementi
// @flag FALSE | Se l'array e' vuoto
//
// @comm Controlla se la dimensione dell'array e' uguale a 0 e se esistono
//       degli elementi diversi da NULL

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

// @mfunc Aggiunge un oggetto ad un array.
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>.
  //
  // @rdesc La funzione ritorna la posizione nella quale e' stato aggiunto
  //        l'oggetto.
  //
  // @xref <mf TArray::insert>
{
  if (index < 0) for (index = _next; index < size() && _data[index]; index++);
  
  if (index >= size()) resize(int(3L*index/2) + 1);

  if (_data[index] != NULL)
  {
#ifdef DBG
    if (object == _data[index])
    {
      error_box("Iu ar traing tu overrait en arrei obgiect: %d", index);
      return index;
    }
#endif
    delete _data[index];
    _items--;
  }

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

// @mfunc Inserisce un elemento dell'array nella posizione index
int TArray::insert(
  TObject *object, // @parm Oggetto da inserire nell'array
  int index)       // @parm Posizione in cui inserire l'oggetto
  // @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>.
  //
  // @rdesc La funzione ritorna la posizione nella quale e' stato inserito l'oggetto.
  //
  // @xref <mf TArray::add>
{
  if (objptr(index))
  {
    if (items() == size()) add(NULL);
    for (int i = size()-1; i >= index; i--)
      _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)
{
  TObject* objptr = object.dup();
  return insert(objptr, index);
}

// @mfunc Elimina l'elemento nella posizione index dell'array
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

  // @rdesc Ritorna l'elemento dell'array eliminato senza farne la delete
{     
  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;
}

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);
}

// @mfunc Cerca il prossimo oggetto dell'array
int TArray::succ(int i) const
// @rdesc Ritorna l'indice del prossimo elemento trovato o size() se non esiste
{               
  CHECKD(i >= -1, "Bad array index ", i);
  for (i++; i < size(); i++)
    if (_data[i]) break;
  return i;  
}

// @mfunc Cerca il precedente oggetto dell'array
int TArray::pred(int i) const
// @rdesc Ritorna l'indice dell'elemento precedente o -1 se non esiste
{
  CHECKD(i <= size(), "Bad array index ", i);
  for (i--; i >= 0; i--)
    if (_data[i]) break;
  return i;  
}

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

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

// @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;
}

// @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);
}

// @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);
}

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

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

// @doc INTERNAL
//
// @mfunc Copia nell'array l'elemento passato come parametro
void TBit_array::copy(
  const TBit_array& ba) // @parm Oggetto da copiare nell'array

  // @comm Sostituisce all'elemento corrente dell'array l'elemento 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 INTERNAL
//
// @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)
{
  CHECK(_size >= ba._size, "TBit_array |=: right operand too big");

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

  return *this;
}

// Certified 99%
void TBit_array::set(long 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
long TBit_array::ones() const

// @rdesc Numero di 1 trovati.
//
// @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
long TBit_array::last_one() const

// @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
//
// @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%
// @mfunc Ritorna la posizione del primo 1 nell'array
long TBit_array::first_one() const

// @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
//
// @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 INTERNAL
//
// @mfunc Controlla se si tratta di un oggetto valido
bool TBit_array::ok() const

// @rdesc Ritorna uno dei seguenti parametri:
//
// @flag TRUE | Se l'array contiene degli elementi
// @flag FALSE | Se l'array e' vuoto
//
// @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 INTERNAL
//
// @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;  
}


