#include <textset.h>

///////////////////////////////////////////////////////////
// TText_recordset
///////////////////////////////////////////////////////////

bool TText_recordset::destroy(TRecnotype r) 
{ 
  const bool ok = _rec.destroy(r, true);
  if (ok && (_curr == r || _curr > _rec.items()))
    _curr = -1;
  return ok;
}

const TRecordset_column_info& TText_recordset::column_info(const char* column) const
{
  return column_info(find_column(column));
}

TRecnotype TText_recordset::new_rec(const char* rec)
{
  _curr = _rec.add(rec);
  const int l = row(_curr).size(); // Forse sarebbe piu' sicuro len()
  if (l > _info._width)
    _info._width = l;
  return _curr;
}

TRecnotype TText_recordset::insert_rec(TRecnotype r, const char* buf)
{
  TRecnotype pos = new_rec(buf);
  if ((r >= 0) && (r != pos))
  {
    TObject* last = _rec.remove(pos);
    _rec.insert(last, r);
    _curr = pos = r;
  }
  return pos;
}

TToken_string& TText_recordset::row(TRecnotype n) const
{
  if (n < 0)
    n = current_row();
  if (n < 0 || n >= items())
    n = ((TText_recordset*)this)->new_rec();  // Clausola di salvaguardia
  return (TToken_string&)_rec.row(n);
}

TRecnotype TText_recordset::items() const 
{ 
  TRecnotype n = _rec.items();
  if (n == 0 && _query.full() && _query.find("CREATE ") < 0)
  {
    ((TText_recordset*)this)->exec(_query);
    n = _rec.items();
  }
  return n; 
}

bool TText_recordset::move_to(TRecnotype pos)
{
  const bool ok = pos >= 0 && pos < items();

  _curr = pos;
	return ok;
}

void TText_recordset::requery()
{
}

bool TText_recordset::load_file(const TFilename& n)
{
  TString str(4096);
  char* buf = str.get_buffer();
  ifstream f(n);
  while (f && !f.eof())
  {
    f.getline(buf, str.size());
    new_rec(buf);
  }
  return _rec.items() > 0;
}

int TText_recordset::find_column(const char* column) const
{
  int n = -1;
  if (column && *column && *column != '#')
  {
    if (real::is_natural(column))
      n = atoi(column);
    if (strlen(column) > 2)
      n = TRecordset::find_column(column);
    else
    {
			n = 0;
      for (const char* c = column; isalpha(*c); c++)
      {
        n *= 26;
        n += toupper(*c)-'@';
      }
      n--;
    }
  }
  return n;
}

const TVariant& TText_recordset::get(unsigned int column) const
{
  const TRecnotype n = current_row();
  if (column == 0 && n >= 0 && n < items())
  {
    TVariant& var = get_tmp_var();
    var.set(((TText_recordset*)this)->row(n));
    return var;
  }
  return NULL_VARIANT;
}

bool TText_recordset::set(unsigned int column, const TVariant& var)
{
  const bool ok = column == 0;
  if (ok)
    row() = var.as_string();
  return ok;
}

bool TText_recordset::set(const char* column, const TVariant& var)
{
  return set(find_column(column), var);
}

TQuery_type TText_recordset::parse_query(const char* query, TFilename& table)
{
  TQuery_type qt = _qt_none;
  
  TString q(query); q.trim(); 
  const int ct = q.find("CREATE TABLE ");
  if (ct >= 0)
  {
    const TToken_string t(q.mid(ct), ' ');
    t.get(2, table); // Furbastro: prendo la terza parola della query
    table.trim();
    if (table.not_empty())
      qt = _qt_create;
  }
  else
  {
    const int se = q.find("SELECT ");
    if (se >= 0)
    {
      int n = q.find("FROM ", se);
      table = q.mid(n+5);
      n = table.find("WHERE");
      if (n > 0)
        table.cut(n-1);
      n = table.find(',');
      if (n > 0)
        table.cut(n);
    }
    else
    {
      table = query;
    }
    table.trim();
    if (table.not_empty())
      qt = _qt_select;
  }
  return qt;
}

bool TText_recordset::exec(const char* query)
{
  TFilename name;
  const TQuery_type qt = parse_query(query, name);
  if (qt != _qt_none)
  {
    _query = query; _query.trim();
    if (qt == _qt_select)
    {
      destroy();
      load_file(name);
    }
  }
  return qt != _qt_none;
}

bool TText_recordset::save_as_text(const char* path)
{
  bool valid = path && *path;
  if (valid)
  {
    ofstream out(path);
    if (out.good())
    {
      for (bool ok = move_first(); ok; ok = move_next())
        out << row() << endl;
    }
    else
      valid = false;
  }
  return valid;
}

bool TText_recordset::save_as(const char* path, TRecordsetExportFormat fmt)
{
  if (fmt == fmt_text)
  {
    TFilename n = path;
    if (n.blank())
      parse_query(_query, n);
    if (n.full())
      return save_as_text(n);
  }
  return TRecordset::save_as(path, fmt);
}

void TText_recordset::sort(COMPARE_FUNCTION f)
{
  if (f == NULL)
    _rec.sort(true);      // Ordina alfabeticamante
  else
    _rec.TArray::sort(f); // Usa la funzione definita dall'utente
}

TText_recordset::TText_recordset(const char* query) : _query(query), _curr(-1)
{ 
  _info._name = "A";
  _info._pos = _info._width = 0;
  _info._type = _alfafld;
}

///////////////////////////////////////////////////////////
// TCSV_recordset
///////////////////////////////////////////////////////////

TQuery_type TCSV_recordset::parse_query(const char* query, TFilename& table)
{
  TString q(query);

  if (q.starts_with("CSV(", true))
  {
    switch (q[4])
    { 
    case '\'':
    case '"' : 
      if (q[5] == '\\' && q[6] == 't')
        _separator = '\t'; 
      else
        _separator = q[5]; 
      break;
    default  :
      if (q[4] != ')')
        _separator = q[4];
      else
        _separator = ',';
      break;
    }

    q = q.after(')');
    q.trim();
  }
  return TText_recordset::parse_query(q, table);
}


unsigned int TCSV_recordset::columns() const
{
  unsigned int c = _trc.items();
  if (c == 0)
  {
    const TRecnotype i = items();  //rilegge fai di testo e tenta di crearne il tracciato _trc
    c = _trc.items();
    if (c == 0 && i > 0)
      c = ((TCSV_recordset*)this)->build_trc();
  }
  return c;
}

const TRecordset_column_info& TCSV_recordset::column_info(unsigned int column) const
{
  if (column >= 0 && column < columns())
  {
    TRecordset_column_info* ci = (TRecordset_column_info*)_trc.objptr(column);
    if (ci != NULL)
      return *ci;
  }
  return TText_recordset::column_info(column);
}

TRecnotype TCSV_recordset::new_rec(const char* rec)
{
  const TRecnotype n = TText_recordset::new_rec(rec);
  if (n >= 0)
    row(n).separator(_separator);
  return n;
}

void TCSV_recordset::create_column(const char* name, TFieldtypes type)
{
  TRecordset_column_info * info = new TRecordset_column_info;

	info->_name = name;
	info->_type = type;
  info->_pos  = _trc.add(info);
}

unsigned int TCSV_recordset::build_trc()
{
  _trc.destroy();
  int cols = 1;
  int i;
  for (i = items()-1; i >= 0; i--)
  {
    const TToken_string& r = row(i);
    const int c = r.items();
    if (c > cols)
      cols = c;
  }

  for (i = 0; i < cols; i++)
    _trc.add(new TRecordset_column_info);

  TString val;
  for (i = items()-1; i >= 0; i--)
  {
    const TToken_string& r = row(i);
    for (int c = r.items()-1; c >= 0; c--)
    {
      r.get(c, val);
      if (val.full())
      {
        TRecordset_column_info& ci = *(TRecordset_column_info*)_trc.objptr(c);
        const int l = val.len();
        if (l > ci._width)
        {
          ci._width = l;
          if (l == 8 || l == 10 && TDate::isdate(val))
            ci._type = _datefld;
          else
          {
            if (!real::is_null(val))
            {
              if (real::is_natural(val))
                ci._type = _intfld;
              else
                ci._type = _realfld;
            }
          }
        }
      }
    }
  }

  return _trc.items();
}


bool TCSV_recordset::load_file(const TFilename& n)
{
  if (!TText_recordset::load_file(n))
    return false;
  if (_trc.empty())
    build_trc();
  return columns() > 0;
}

bool TCSV_recordset::set(unsigned int column, const TVariant& var)
{
  TToken_string& r = row();
  r.add(var.as_string(), column);
  return true;
}

const TVariant& TCSV_recordset::get(unsigned int column) const
{
  const TRecnotype n = current_row();
  if (n >= 0 && n < items())
  {
    const char* val = row(n).get(column);
    if (val != NULL)
    {
      TVariant& var = get_tmp_var();
      var.set(val);
      return var;
    }
  }
  return NULL_VARIANT;
}

TCSV_recordset::TCSV_recordset(const char* query) 
              : TText_recordset(query), _separator(',')
{ 
  TFilename n;
  if (parse_query(query, n) == _qt_select && n.exist())
    load_file(n);
}

///////////////////////////////////////////////////////////
// TAS400_recordset
///////////////////////////////////////////////////////////

bool TAS400_recordset::load_file(const TFilename& n)
{
  TString row(record_length(), ' ');
  ifstream f(n, ios::binary);
  char* buf = row.get_buffer();
  TRecnotype r = -1;
  while (f)
  {
    *buf = '\0';
    f.read(buf, record_length());
    if (*buf)
      r = new_rec(buf);
    else
      break;
  }
  return r >= 0;
}

TQuery_type TAS400_recordset::parse_query(const char* query, TFilename& table)
{
  TString q(query);
  if (q.find("AS400(") >= 0)
  {
    const int da = q.find('(');
    const int al = q.find(')', da);
    TToken_string k(q.sub(da+1, al), ',');
    _info._width = k.get_int(0);
    _key._width = k.get_int();
    _key._pos = k.get_int();
    q = q.mid(al+1);
    q.trim();
  }
  return TText_recordset::parse_query(q, table);
}

bool TAS400_recordset::set_field(const TAS400_column_info& fi, const TVariant& var)
{
  TToken_string& r = row();
  switch (fi._type)
  {
  case _intfld:
  case _longfld:
    {
      TString80 str;
      if (fi._width > 8)
        str = var.as_real().string(fi._width, 0, ' ');
      else
        str.format("%*ld", fi._width, var.as_int());
      r.overwrite(str, fi._pos);
    }
    break;
  case _intzerofld:
  case _longzerofld:
    {
      TString80 str; 
      if (fi._width > 8)
        str = var.as_real().string(fi._width, 0, '0');
      else
        str.format("%0*ld", fi._width, var.as_int());
      r.overwrite(str, fi._pos);
    }
    break;
  case _realfld:
    {
      const char* str = var.as_real().string(fi._width);
      r.overwrite(str, fi._pos);
    }
    break;
  case _datefld:
    if (fi._width == 8)
    {
      TString8 str; str << var.as_date().date2ansi();
      r.overwrite(str, fi._pos, fi._width);
      break;
    }
    // fall down to default
  default:
    r.overwrite(var.as_string(), fi._pos, fi._width);
    break;
  }
  bool ok = true;
  if (fi._required && var.is_empty())
    ok = false;
  return ok;
}

TAS400_column_info* TAS400_recordset::parse_field(const char* column, int& c, bool create)
{
  CHECK(column && *column > ' ', "NULL field name");
  TAS400_column_info* ci = NULL;
  
  TString16 trc;
  TString80 fld(column);
  const int dot = fld.find('.');
  if (dot > 0)
  {
    trc = fld.left(dot);
    fld.ltrim(dot+1);
  }
  else
    trc = rec_type();

  TArray* info = (TArray*)_trc.objptr(trc);
  if (info == NULL && create)
  {
    info = new TArray;
    _trc.add(trc, info);
  }
  
  if (info != NULL)
  {
    if (create)
    {
      ci = new TAS400_column_info;
      ci->_name = fld;
      if (info->items() > 0)
      {
        const TAS400_column_info& last = *(const TAS400_column_info*)info->objptr(info->last());
        ci->_pos = last._pos+last._width;
      }
      c = info->add(ci);
    }
    else
    {
      for (c = info->last(); c >= 0; c--)
      {
        TAS400_column_info* aci = (TAS400_column_info*)info->objptr(c);
        if (aci->_name == fld)
        {
          ci = aci;
          break;
        }
      }
    }
  }  
  return ci;
}

unsigned int TAS400_recordset::columns() const
{
  const TArray* info = (const TArray*)_trc.objptr(rec_type());
  if (info)
    return info->items();
  return TText_recordset::columns();
}

const TRecordset_column_info& TAS400_recordset::column_info(unsigned int c) const
{
  const TArray* info = (const TArray*)_trc.objptr(rec_type());
  if (info)
  {
    TRecordset_column_info* i = (TRecordset_column_info*)info->objptr(c);
    if (i != NULL)
      return *i;
  }
  return TText_recordset::column_info(c);
}


int TAS400_recordset::find_column(const char* column) const
{
  int c = -1;
  ((TAS400_recordset*)this)->parse_field(column, c,  false);
  return c;
}

const TRecordset_column_info& TAS400_recordset::column_info(const char* column) const
{
  int c = -1;
  TRecordset_column_info* ci = ((TAS400_recordset*)this)->parse_field(column, c,  false);
  if (ci != NULL)
  {
    return *ci;
  }
  return TText_recordset::column_info(column);
}

const TVariant& TAS400_recordset::get_field(const TAS400_column_info& ci) const
{
  const TRecnotype n = current_row();
  if (n >= 0 && n < items())
  {
    const TString& str = row(n).mid(ci._pos, ci._width);
    TVariant& var = get_tmp_var();
    var.set(str); var.convert_to(ci._type);
    return var;
  }
  return ci._default;
}

const TVariant& TAS400_recordset::get(unsigned int column) const
{
  const TArray* info = (const TArray*)_trc.objptr(rec_type());
  if (info)
  {
    const TAS400_column_info* ci = (const TAS400_column_info*)info->objptr(column);
    if (ci != NULL)
      return get_field(*ci);
  }
  return TText_recordset::get(column);
}

const TVariant& TAS400_recordset::get(const char* column) const
{
	if (column && *column != '#')
  {
		int c = -1;
		if (column[1] != '\0')
		{
			const TAS400_column_info* ci = ((TAS400_recordset*)this)->parse_field(column, c,  false);
			if (ci != NULL)
				return get_field(*ci);
		}
		c = TText_recordset::find_column(column);
		if (c >= 0)
			return TText_recordset::get(c);
	}
	return TRecordset::get(column);
}

TRecnotype TAS400_recordset::new_rec(const char* trc)
{
	CHECK(record_length() > 0, "Lunghezza record nulla");

	if (trc && (int)strlen(trc) == record_length())
		return TText_recordset::new_rec(trc);
	else
	{
	  const TString str(record_length(), ' ');
		const TRecnotype n = TText_recordset::new_rec(str);

		if (trc && (int)strlen(trc) == key_length())
		{
			TArray* info = (TArray*)_trc.objptr(trc);
			if (info != NULL) // Se il tracciato e' noto, riempio di zeri i campi opportuni
			{
				FOR_EACH_ARRAY_ITEM_BACK((*info), i, obj)
				{
					const TAS400_column_info& ci = *(const TAS400_column_info*)obj;
					if (ci._default.is_null())
					{
						if (ci._type == _intzerofld || ci._type == _longzerofld)
							set_field(ci, NULL_VARIANT);
					}
					else
						set_field(ci, ci._default);
				}
			}
			row(n).overwrite(trc, key_position(), key_length());
		}
	  return n;
	}
}

const TString& TAS400_recordset::rec_type(TRecnotype r) const
{
  if (key_length() > 0)
  {
    const TToken_string& riga = row(r);
    return riga.mid(key_position(), key_length());
  }
  return EMPTY_STRING;
}

bool TAS400_recordset::create_field(const char* nam, int pos, int len, 
                                    TFieldtypes typ, bool req, const TVariant& def)
{
  CHECK(nam && *nam > ' ', "Null field name");
  
  int c = -1;
  TAS400_column_info* ci = parse_field(nam, c, true);
  const bool ok = ci != NULL;
  if (ok)
  {
    if (pos >= 0) // Se l'utente fissa una posizione impostala, altrimenti tieni quella automatica
      ci->_pos    = pos;
    ci->_width    = len;
    ci->_type     = typ;
    ci->_required = req;
    ci->_default  = def;
    
    CHECKS(ci->_pos >= 0 && ci->_pos < record_length(), "Invalid position for field ", nam);
    CHECKS(ci->_width > 0 && ci->_pos+ci->_width <= record_length(), "Invalid lenght for field ", nam);
  }
  return ok;
}

bool TAS400_recordset::set(const char* column, const TVariant& var)
{
  int c = -1;
  TAS400_column_info* ci = parse_field(column, c,  false);
  bool ok = ci != NULL;
  if (ok)
    ok = set_field(*ci, var);
  return ok;
}

bool TAS400_recordset::set(unsigned int column, const TVariant& var)
{
  const TAS400_column_info& ci = (const TAS400_column_info&)column_info(column);
  return set_field(ci, var);
}

bool TAS400_recordset::save_as_text(const char* path)
{
  bool valid = path && *path;
  if (valid)
  {
    ofstream out(path, ios::binary);
    if (out.good())
    {
      for (bool ok = move_first(); ok; ok = move_next())
        out << row();  // NON mettere << endl
    }
    else
      valid = false;
  }
  return valid;
}

TAS400_recordset::TAS400_recordset(const char* query)
                : TText_recordset(query)
{ 
  TFilename n;
  if (parse_query(query, n) == _qt_select && n.exist())
    load_file(n);
}