
#include <QtCore/QString.h>
#include <QtCore/QTextStream.h>

#include "x_at_index.h"
#include "x_at_reclist.h"

#include "base/config.h"
#include "base/exception.h"
#include "index/indexlist.h"

#include "indexiter.h"

#include "x_at_error.h"

// ================================== INDEX ===================

AT_Index::AT_Index
  (int) :
    x_Index(0)
{
}

AT_Index::AT_Index
  (AT_Read_Rand_File *indexFile,
   uint8 indexType,
   uint64 indexOffset,
   uint32 indexSize,
   uint32 headerSize,
   uint   featureBits,
   uint32 dbRecords) 
try :
  x_Index(new Xpace::Index(Xpace::Configuration(), indexFile->x_File, indexOffset, dbRecords)),
  db_records(dbRecords)
{
}
catch (Xpace::Exception& ex)
{
  throw AT_Constructor_Err(translateException(ex));
}


// static
AT_Index AT_Index::null(0);

bool AT_Index::operator!
  ()
  const
{
  return !x_Index || !*x_Index;
}

AT_Index::AT_Index
  (const AT_Index& rhs) 
try :
  x_Index(new Xpace::Index(*rhs.x_Index)),
  db_records(rhs.db_records)
{
}
catch (Xpace::Exception& ex)
{
  throw AT_Constructor_Err(translateException(ex));
};

AT_Index& AT_Index::operator=
  (const AT_Index& rhs)
{
  try
  {
    x_Index = new Xpace::Index(*rhs.x_Index);
  }
  catch (Xpace::Exception& ex)
  {
    throw AT_Constructor_Err(translateException(ex));
  }

  db_records = rhs.db_records;
  return *this;
}

AT_Index::~AT_Index
  ()
{
  delete x_Index;
}

uint AT_Index::getRecordCount
  ()
  const
{
  return db_records;
}

bool AT_Index::isNumeric
  ()
  const
{
  return !!x_Index->getCollator()->getFixedSize();
}

uint32 AT_Index::getTermCount
  ()
  const
{
  return uint32(x_Index->end().diff(x_Index->begin()));
}

bool AT_Index::hasCounts
  ()
  const
{
  return false;
}

const AT_Charset* AT_Index::getCharset
  ()
  const
{
  return 0;
}

AT_Record_List AT_Index::search
  (AT_Num term)
  const
{
  try
  {
    bool found;
    Xpace::Index::Iter iter(x_Index->begin(term, &found));
    if (found)
      return AT_Record_List(iter.getRefList(), db_records);
    return AT_Record_List(db_records);
  }
  catch (Xpace::Exception& ex)
  {
    throw AT_Xpace_Err(ex);
  }
}
  
AT_Record_List AT_Index::search
  (AT_String term)
  const
{
  try
  {
    bool found;
    Xpace::Index::Iter iter(x_Index->begin(Xpace::String(QString::fromAscii(term)), &found));
    if (found)
      return AT_Record_List(iter.getRefList(), db_records);
    return AT_Record_List(db_records);
  }
  catch (Xpace::Exception& ex)
  {
    throw AT_Xpace_Err(ex);
  }
}

AT_Record_List AT_Index::search
  (AT_Num start,
   AT_Num end)
  const
{
  try
  {
	  bool found;
    Xpace::Index::Iter iter(x_Index->begin(start, &found));
	  Xpace::Index::Iter last(x_Index->begin(end, &found));
	  if (found)
	    last.move(1);
    return AT_Record_List(iter.getRefList(last), db_records);
  }
  catch (Xpace::Exception& ex)
  {
    throw AT_Xpace_Err(ex);
  }
}

AT_Record_List AT_Index::search
  (AT_String start,
   bool startIncl,
   AT_String end,
   bool endIncl)
  const
{
  try
  {
	  bool found;
    Xpace::Index::Iter iter(x_Index->begin(Xpace::String(QString::fromAscii(start)), &found));
    if (found && !startIncl)
      iter.move(1);

    Xpace::Index::Iter last;
    if (!!end)
    {
      last = x_Index->begin(Xpace::String(QString::fromAscii(end)), &found);
      if (found && endIncl)
       last.move(1);
    }
    else
      last = x_Index->end();

    Xpace::RefListCursor ref_list(iter.getRefList(last));
    return AT_Record_List(ref_list, db_records);
  }
  catch (Xpace::Exception& ex)
  {
    throw AT_Xpace_Err(ex);
  }
}

AT_Record_List AT_Index::searchAll
  ()
  const
{
  throw AT_Function_Err("AT_Index", "searchAll");
  //return AT_Record_List(db_records);
}

AT_Record_List AT_Index::searchNone
  ()
  const
{
  throw AT_Function_Err("AT_Index", "searchNone");
  //return AT_Record_List(db_records);
}

#ifndef NDEBUG
bool AT_Index::test
  (AT_Num term,
   uint32 recNum)
  const
{
  return true;
}

bool AT_Index::test
  (AT_String term,
   uint32 recNum)
  const
{
  return true;
}
#endif

AT_Index::Iter AT_Index::Iter::null;

AT_Index::Iter AT_Index::begin
  (AT_Num term)
  const
{
  try
  {
    bool found;
    Xpace::Index_Iter* iter(new Xpace::Index_Iter(x_Index->begin(term, &found)));
    return AT_Index::Iter(x_Index, iter, db_records);
  }
  catch (Xpace::Exception& ex)
  {
    throw AT_Xpace_Err(ex);
  }
}

AT_Index::Iter AT_Index::begin
  (AT_String term)
  const
{
  try
  {
    bool found;
    Xpace::Index::Iter i(x_Index->begin(!!term ? Xpace::String(QString::fromAscii(term)) : Xpace::String(), &found));
    Xpace::Index_Iter* iter(new Xpace::Index_Iter(i));
    return AT_Index::Iter(x_Index, iter, db_records);
  }
  catch (Xpace::Exception& ex)
  {
    throw AT_Xpace_Err(ex);
  }
}

AT_Index::Iter AT_Index::end
  ()
  const
{
  try
  {
    Xpace::Index_Iter* iter(new Xpace::Index_Iter(x_Index->end()));
    return AT_Index::Iter(x_Index, iter, db_records);
  }
  catch (Xpace::Exception& ex)
  {
    throw AT_Xpace_Err(ex);
  }
}

uint8 AT_Index::getIndexType
  ()
  const
{
  assert(0);
  return 0;
}

class doForEachRef : public Xpace::RefListCursor::forEachRef
{
public :
  doForEachRef
    (AT_Index::directAccess* c) :
      call_this(c)
  {};

  virtual bool operator()
    (const uint64* r,
     uint len)
  {
    return call_this->rec(uint(*r));
  };    

private :
  AT_Index::directAccess* call_this;
};

// static
bool AT_Index::forEachRecord
  (AT_Index::Iter& it,
   AT_Index::Iter& end,
   AT_Index::directAccess *called)
{
  try
  {
    doForEachRef act(called);
    while (it < end)
    {
      if (!called->term(it) ||
          !Xpace::Index::Iter(*it.x_Iter).getRefList().forEach(act))
        return false;
      ++it;
    }
    return true;
  }
  catch (Xpace::Exception& ex)
  {
    throw AT_Xpace_Err_noThis(AT_Index, ex);
  }
}

// static
bool AT_Index::forEachRecordInFile
  (AT_Index::Iter& begin,
   AT_Index::Iter& end,
   AT_Index::directAccess *called,
   AT_Read_Rand_File *f)
{
  assert(0);
  return false;
}

// static
bool AT_Index::forEachTerm
  (AT_Index::Iter& it,
   AT_Index::Iter& end,
   directAccess *called)
{
  try
  {
    while (it < end)
    {
      if (!called->term(it))
        return false;
      ++it;
    }
    return true;
  }
  catch (Xpace::Exception& ex)
  {
    throw AT_Xpace_Err_noThis(AT_Index, ex);
  }
}

// ================================== INDEX (TERM) ITERATOR ===

AT_Index::Iter::Iter
  (const AT_Index *index) try :
    x_Index(index->x_Index),
    x_Iter(new Xpace::Index_Iter(index->x_Index->begin())),
    db_records(index->getRecordCount())
{
}
catch (Xpace::Exception& ex)
{
  throw AT_Constructor_Err(translateException(ex));
}


AT_Index::Iter::Iter
  (const Iter& rhs) try :
    x_Index(rhs.x_Index),
    x_Iter(new Xpace::Index_Iter(*rhs.x_Iter)),
    db_records(rhs.db_records)
{
}
catch (Xpace::Exception& ex)
{
  throw AT_Constructor_Err(translateException(ex));
}

bool AT_Index::Iter::operator!
  ()
  const
{
  return !x_Iter || !*x_Iter;
}

AT_Index::Iter::~Iter
  ()
{
}

uint32 AT_Index::Iter::getRange
  ()
  const
{
  assert(0);
  return 0;
}

bool AT_Index::Iter::operator==
  (const Iter& rhs)
  const
{
  try
  {
    return (x_Iter->diff(*rhs.x_Iter) != 0);
  }
  catch (Xpace::Exception& ex)
  {
    throw AT_Xpace_Err(ex);
  }
}

AT_Index::Iter& AT_Index::Iter::operator=
  (const AT_Index::Iter& rhs)
{
  try
  {
    x_Index = rhs.x_Index;
    x_Iter = new Xpace::Index_Iter(*rhs.x_Iter);
    return *this;
  }
  catch (Xpace::Exception& ex)
  {
    throw AT_Xpace_Err(ex);
  }
}

AT_Index_Term AT_Index::Iter::operator*
  ()
  const
{
  try
  {
    const Xpace::Key key(x_Iter->getKey());
    const Xpace::Collator* coll(x_Index->getCollator());
    Xpace::String str(coll->keyToString(key));
    AT_Index_Term t;
    t.term.resize(str.getLength() + 1);
    str.toUtf8((char*)&t.term[0], t.term.size());
    if (coll->getFixedSize())
    {
      t.val = 0;
      memcpy(reinterpret_cast<byte*>(&t.val), coll->getKeyData(&key), std::min(sizeof(t.val), coll->getKeySize(key)));
    }
    return t;
  }
  catch (Xpace::Exception& ex)
  {
    throw AT_Xpace_Err(ex);
  }
}

AT_Index::Iter& AT_Index::Iter::operator++
  ()
{
  try
  {
    x_Iter->move(1);
    return *this;
  }
  catch (Xpace::Exception& ex)
  {
    throw AT_Xpace_Err(ex);
  }
}

AT_Index::Iter AT_Index::Iter::operator++
  (int)
{
  try
  {
    AT_Index::Iter it(*this);
    x_Iter->move(1);
    return it;
  }
  catch (Xpace::Exception& ex)
  {
    throw AT_Xpace_Err(ex);
  }
}

AT_Index::Iter& AT_Index::Iter::operator--
  ()
{
  try
  {
    x_Iter->move(-1);
    return *this;
  }
  catch (Xpace::Exception& ex)
  {
    throw AT_Xpace_Err(ex);
  }
}

AT_Index::Iter AT_Index::Iter::operator--
  (int)
{
  try
  {
    AT_Index::Iter it(*this);
    x_Iter->move(-1);
    return it;
  }
  catch (Xpace::Exception& ex)
  {
    throw AT_Xpace_Err(ex);
  }
}

AT_Index::Iter& AT_Index::Iter::operator+=
  (int32 n)
{
  try
  {
    x_Iter->move(n);
    return *this;
  }
  catch (Xpace::Exception& ex)
  {
    throw AT_Xpace_Err(ex);
  }
}

AT_Index::Iter AT_Index::Iter::operator+
  (int32 n)
  const
{
  try
  {
    AT_Index::Iter it(*this);
    it.x_Iter->move(n);
    return it;
  }
  catch (Xpace::Exception& ex)
  {
    throw AT_Xpace_Err(ex);
  }
}

AT_Index::Iter& AT_Index::Iter::operator-=
  (int32 n)
{
  try
  {
    x_Iter->move(-n);
    return *this;
  }
  catch (Xpace::Exception& ex)
  {
    throw AT_Xpace_Err(ex);
  }
}

int32 AT_Index::Iter::operator-
  (const AT_Index::Iter& rhs)
  const
{
  try
  {
    return Xpace::int32(x_Iter->diff(*rhs.x_Iter));
  }
  catch (Xpace::Exception& ex)
  {
    throw AT_Xpace_Err(ex);
  }
}

const AT_Index_Term AT_Index::Iter::operator[]
  (uint32 n)
  const
{
  try
  {
    Xpace::Index::Iter it(*x_Iter);
    Xpace::Key key(it.getKey());
    const Xpace::Collator *coll(x_Index->getCollator());
    return (it.move(n)) 
      ? AT_Index_Term(!!coll->getFixedSize(), coll->getKeyData(&key), coll->getKeySize(key)) 
      : AT_Index_Term();
  }
  catch (Xpace::Exception& ex)
  {
    throw AT_Xpace_Err(ex);
  }
}

bool AT_Index::Iter::operator<
  (const Iter &rhs)
  const
{
  try
  {
    return (x_Iter->diff(*rhs.x_Iter) < 0);
  }
  catch (Xpace::Exception& ex)
  {
    throw AT_Xpace_Err(ex);
  }
}

bool AT_Index::Iter::operator>
  (const Iter &rhs)
  const
{
  try
  {
    return (x_Iter->diff(*rhs.x_Iter) > 0);
  }
  catch (Xpace::Exception& ex)
  {
    throw AT_Xpace_Err(ex);
  }
}

bool AT_Index::Iter::operator<=
  (const Iter &rhs)
  const
{
  try
  {
    return (x_Iter->diff(*rhs.x_Iter) <= 0);
  }
  catch (Xpace::Exception& ex)
  {
    throw AT_Xpace_Err(ex);
  }
}

bool AT_Index::Iter::operator>=
  (const Iter &rhs)
  const
{
  try
  {
    return (x_Iter->diff(*rhs.x_Iter) >= 0);
  }
  catch (Xpace::Exception& ex)
  {
    throw AT_Xpace_Err(ex);
  }
}

// ================================== AT_INDEX_TERM ===========

AT_Index_Term::AT_Index_Term
  ()
{
}
  
AT_Index_Term::AT_Index_Term
  (bool numeric,
   const byte* data,
   size_t len) :
    term(len),
    val(0)
{
  term.resize(len + 1);
  memcpy(&term[0], data, len);
  term[len] = 0;

  if (numeric)
    memcpy(reinterpret_cast<byte*>(&val), data, std::min(len, sizeof(AT_Num)));
}

bool AT_Index_Term::operator!
  ()
  const
{
  return term.empty();
}

bool AT_Index_Term::operator==
  (const AT_Index_Term& rhs)
  const
{
  return (term == rhs.term);
}

AT_Num AT_Index_Term::getTerm
  (byte *buf,
   uint buflen)
  const
{
  if (buf)
  {
    memcpy(buf, &term[0], std::min(buflen, term.size()));
    if (buflen > term.size())
      buf[term.size()] = 0;
  }
  return val;
}

uint32 AT_Index_Term::getRecordCount
  ()
  const
{
  return 0;
}


// ============================================================
// ================================== INDEX LIST ==============

AT_Index_List::AT_Index_List
  (AT_Read_Rand_File *indexRefs,
   AT_Read_Rand_File *indexes) :
    x_IndexList(0)
{
  this->AT_Index_List::AT_Index_List(indexRefs->fullName(), indexes->fullName());
}

AT_Index_List::AT_Index_List
  (AT_String indexRefsName,
   AT_String indexesName) :
    x_IndexList(0)
{
  QString c;
  QTextStream(&c) << "<index_list " 
                  << "ii=\"" << indexRefsName << "\" " 
                  << "id=\"" << indexesName << "\"/>";
  try
  {
    x_IndexList = new Xpace::IndexList(Xpace::Configuration(c));
  }
  catch (Xpace::Exception& ex)
  {
    throw AT_Xpace_Err(ex);
  }
}
  
bool AT_Index_List::operator!
  ()
  const
{
  try
  {
    return x_IndexList->getSize() == 0;
  }
  catch (Xpace::Exception& ex)
  {
    throw AT_Xpace_Err(ex);
  }
}

AT_Index_List::~AT_Index_List
  ()
{
  delete x_IndexList;
}

uint32 AT_Index_List::getNumIndexes
  ()
  const
{ 
  try
  {
    return x_IndexList->getSize(); 
  }
  catch (Xpace::Exception& ex)
  {
    throw AT_Xpace_Err(ex);
  }
}

bool AT_Index_List::indexExists
  (uint32 indexNum)
  const
{
  return !!x_IndexList->getIndex(indexNum);
}

AT_Index AT_Index_List::openIndex
  (uint32 indexNum)
  const
{
  try
  {
    return AT_Index(new Xpace::Index(x_IndexList->getIndex(indexNum)));
  }
  catch (Xpace::Exception& ex)
  {
    throw AT_Constructor_Err(translateException(ex));
  }
}

