// Change History
// 11/20/03 AV Development: BC6 conversion.
// 11/18/03 AV Development: Merge From Production version 15
// 11/12/03 AV Production: More 64 bit
// 11/11/03 AV Production: IntegerIndex::search changes 
// 11/10/03 AV Production: More 64 bit. SeekTerm method 
// 10/29/03 AV Production: 64 bit conversion

//DRS_Idx.cpp

#include <stdlib.h>
#include <cstdio.>
#include <string>

#include "base/at_utls.h"
#include "xdart/x_at_index.h"
#include "xdart/x_at_reclist.h"
#include "retrieve/at_parse.h"
#include "retrieve/at_retrieve.h"

#include "drs/drs_idx.h"
#include "drs/drs_appl.h"

DRS_IndexDefInteger::DRS_IndexDefInteger( AT_String sFile, AT_String sSection, DRS_Setting BaseKey)
{
  DRS_Setting key = BaseKey;
  key += "Width";
  iWidth = GetPrivateProfileIntA( sSection, (LPCSTR) *key, 0, sFile);

  key = BaseKey;
  key += "Normal";
  iNormal = GetPrivateProfileIntA( sSection, (LPCSTR) *key, 0, sFile);

  key = BaseKey;
  key += "Precision";
  iPrecision = GetPrivateProfileIntA( sSection, (LPCSTR) *key, 0, sFile);

  key = BaseKey;
  key += "IsAllowSort";
  isAllowSort = (bool) GetPrivateProfileIntA( sSection, (LPCSTR) *key, true, sFile);
}

DRS_Index * DRS_IndexDefInteger::IndexOpen( DRS_Database &rDatabase, uint iIndexFileID, uint iIndexID)
{
  return new DRS_IntegerIndex( rDatabase.IndexOpen( iIndexFileID, iIndexID), *this);
}

DRS_IndexDefText::DRS_IndexDefText( AT_String sFile, AT_String sSection, DRS_Setting BaseKey)
    : sInclusions(0)
{
  DRS_Setting key = BaseKey;
  key += "Width";
  iWidth = GetPrivateProfileIntA( sSection, (LPCSTR) *key, 0, sFile);

  key = BaseKey;
  key += "IsAllowSort";
  isAllowSort = (bool) GetPrivateProfileIntA( sSection, (LPCSTR) *key, true, sFile);

  key = BaseKey;
  key += "IsCap";
  isCapitalize = (bool) GetPrivateProfileIntA( sSection, (LPCSTR) *key, false, sFile);

  key = BaseKey;
  key += "Inclusions";
  DRS_FileSetting setting = "";
  GetPrivateProfileStringA( (const char*) sSection, (LPCSTR) *key, "", (LPSTR) setting, sizeof(DRS_FileSetting), (LPSTR) (const char*) sFile);
  if( std::strlen( (const char*) setting))
    {
    //NOTE: Quoted profile strings: quotes removed.
      sInclusions = new byte[std::strlen((const char*) setting) + 1];
      strcpy( (char*) sInclusions, (const char*) setting);
    }
}

DRS_IndexDefText::DRS_IndexDefText( DRS_IndexDefText const &rCopy)
  : isAllowSort(rCopy.isAllowSort),
    iWidth(rCopy.iWidth),
    isCapitalize(rCopy.isCapitalize),
    sInclusions(0)
{
  if( rCopy.sInclusions)
    {
    sInclusions = new byte[std::strlen((const char*) rCopy.sInclusions) + 1];
    strcpy( (char *) sInclusions, (char *) rCopy.sInclusions);
    }
}

DRS_IndexDefText::~DRS_IndexDefText()
{
  delete [] sInclusions;
}

DRS_Index * DRS_IndexDefText::IndexOpen( DRS_Database &rDatabase, uint iIndexFileID, uint iIndexID)
{
  return new DRS_TextIndex( rDatabase.IndexOpen( iIndexFileID, iIndexID), *this);
}

DRS_Index::DRS_Index( const AT_Index &base) :
  AT_Index(base)
{
}

AT_Record_List DRS_Index::Search( byte *sSearchString) const
{
  AT_Parse parser( this);
  return parser.parse( sSearchString);
}

//Text indexes
DRS_TextIndex::DRS_TextIndex( AT_Index base, DRS_IndexDefText const &rIndexDef)
    : DRS_Index(base), DRS_IndexDefText(rIndexDef)
{}

DRS_TextIndex::DRS_TextIndex(const DRS_TextIndex &rhs)
    : DRS_Index(rhs), DRS_IndexDefText(rhs)
{}

void DRS_TextIndex::TermGet( AT_Index::Iter const &rIter, byte *sDest, uint iDestLen, uint32 *pOccurrences)
{

  AT_Index_Term idx_term = *rIter;
  if( !idx_term)
    throw AT_Logic_Err( *this, "index term");

  idx_term.getTerm( sDest, iWidth? min( iDestLen, iWidth) : iDestLen);
  if( iWidth && ( iDestLen > iWidth))
    {
    sDest[ iWidth] = 0;
    memmove( (void*) (sDest + iWidth - std::strlen((const char*) sDest)), sDest, std::strlen( (const char*) sDest) + 1);
    }
  if( isCapitalize) strupr( (char*) sDest);

  if( pOccurrences && hasCounts())
    *pOccurrences = idx_term.getRecordCount();

}
AT_Index::Iter DRS_TextIndex::SeekTerm( byte *sSeekString)
{
    return begin( sSeekString);
}

AT_Record_List DRS_TextIndex::Search( byte *sSearchString) const
{
  AT_Parse parser( this);
  return parser.parse( sSearchString, 0, sInclusions);
}

//Integer indexes
DRS_IntegerIndex::DRS_IntegerIndex( AT_Index base, DRS_IndexDefInteger const &rIndexDef)
    : DRS_Index(base), DRS_IndexDefInteger(rIndexDef)
{}

DRS_IntegerIndex::DRS_IntegerIndex(const DRS_IntegerIndex &rhs)
    : DRS_Index(rhs), DRS_IndexDefInteger(rhs)
{}

DRS_IntegerIndex::~DRS_IntegerIndex()
{
}

AT_Index::Iter DRS_IntegerIndex::SeekTerm( byte *sSeekString)
{
    //uint len;
    AT_Num val = atoAT_Num( (const char*) sSeekString);
    for( uint i = std::strlen((const char*) sSeekString); i < iWidth; i++) val *= 10;
    return begin( max( val, (AT_Num)iNormal) - iNormal);

/*
  AT_Index::Iter iter( begin());

  if( iWidth &&
      (std::strlen(sSeekString) < iWidth))
    {
    byte * sPadString = new byte[iWidth + 1];
    memset( sPadString, 0, iWidth + 1);
    std::strcpy( sPadString, sSeekString);
    uint l = std::strlen(sPadString);
    memset( &sPadString[l], '0', iWidth - l);
    iter = begin( sPadString);
    delete [] sPadString;
    }
  else
    iter = begin( sSeekString);

  return iter;
*/  
}

void DRS_IntegerIndex::TermGet
  ( AT_Index::Iter const &rIter,
    byte *sDest,
    uint iDestLen,
    uint32 *pOccurrences)
{
  if( !sDest || !iDestLen) return;

  AT_Index_Term idx_term = *rIter;
  if( !idx_term)
    throw AT_Logic_Err( *this, "index term");

  //GetTerm: Can I get the integer val and passs NULLs for sDest???
  //AV 64 bit
  AT_Num val = idx_term.getTerm( ) + iNormal; // sDest, iDestLen);
  if( iWidth)
         {
         uint len = min( iDestLen, iWidth);
         sprintf( (char *) sDest, "%0*.*lu", len, len, val);
         }
  else
         {
         byte sVal[34]; //ultoa max len = 33
         AT_Numtoa( val, (char*) sVal);
         std::strncpy( (char*) sDest, (const char*) sVal, iDestLen);
         sDest[iDestLen - 1] = 0;
         }

  if( pOccurrences && hasCounts())
    *pOccurrences = idx_term.getRecordCount();
}

AT_Record_List DRS_IntegerIndex::Search( byte *sSearchString) const
{
  AT_Parse parser( this);
  return parser.parse( sSearchString, iWidth);
}

//IntegerIndex: term search
AT_Record_List DRS_IntegerIndex::search
  (AT_Num term)
  const
{
    // AV if term == AT_MAXNUM then do not normalize term -> go to the end
    if( term >= iNormal) return AT_Index::search(term != AT_MAXNUM ? term - iNormal : term);
    else return AT_Record_List::null;
}

AT_Record_List DRS_IntegerIndex::search
  (AT_String term)
  const
{
    AT_Num t;
    if (!!term && ((t = atoAT_Num( term))) >= iNormal)
      return search(t);
    else
      return AT_Record_List::null;
}

// range search
AT_Record_List DRS_IntegerIndex::search
  (AT_Num start,
   AT_Num end)
  const
{
  if (end >= start)
    // AV if term == AT_MAXNUM then do not normalize term -> go to the end  
    return AT_Index::search((start >= iNormal && (start != AT_MAXNUM)) ? start - iNormal : start,
                            (end >= iNormal && (end != AT_MAXNUM)) ?  end - iNormal : end);
  return AT_Record_List::null;
}

AT_Record_List DRS_IntegerIndex::search
  (AT_String start,
   bool startIncl,
   AT_String end,
   bool endIncl)
  const
{
  // AV copy code from quantile search function
  AT_Num start_val = (!!start) ? (atoAT_Num(start) + !startIncl) : 0;
  AT_Num end_val   =   (!!end) ? (atoAT_Num(  end) - !endIncl) : AT_MAXNUM;
  if (!!end && (end_val == AT_MAXNUM || end_val < iNormal))
    // AV <0 searches should return 0 recs
    return AT_Record_List::null;

  if (end_val >= start_val)
    return search(start_val, end_val);

  return AT_Record_List::null;
}

///////////////////////////////////////////////
//Round indexes
DRS_RoundIndex::DRS_RoundIndex( AT_Index base, DRS_IndexDefInteger const &rIndexDef, uint prefPrecision)
    : DRS_IntegerIndex(base, rIndexDef),
      iPrefPrecision(prefPrecision),
      round_index(ARL_CreateRoundIndex(base, prefPrecision, rIndexDef.Precision(), rIndexDef.Normal()))
{ if (prefPrecision < rIndexDef.Precision()) iNormal = 0; }

// ???PS
DRS_RoundIndex::DRS_RoundIndex( const DRS_RoundIndex &rhs)
    : DRS_IntegerIndex(/* *(rhs.round_index->getBaseIndex())*/ *rhs.round_index, rhs),
      iPrefPrecision(rhs.iPrefPrecision),
      round_index(ARL_CopyRoundIndex(*dynamic_cast<AT_Round_Index*>(rhs.round_index)))
{
}

DRS_RoundIndex::~DRS_RoundIndex()
{
  ARL_DeleteRoundIndex(dynamic_cast<AT_Round_Index*>(round_index));
}

AT_Index::Iter DRS_RoundIndex::SeekTerm( byte *sSeekString)
{
  return DRS_IntegerIndex::SeekTerm(RemoveDecimal(sSeekString));
}

void DRS_RoundIndex::TermGet
  ( AT_Index::Iter const &rIter,
    byte *sDest,
    uint iDestLen,
    uint32 *pOccurrences)
{
  DRS_IntegerIndex::TermGet(rIter, sDest, iDestLen, pOccurrences);
  InsertDecimal(sDest, std::strlen((const char*) sDest));
}

AT_Record_List DRS_RoundIndex::Search( byte *sSearchString) const
{
  AT_Parse parser( (DRS_IntegerIndex*)this);
  return parser.parse( sSearchString, iWidth);
}

//IntegerIndex: term search
AT_Record_List DRS_RoundIndex::search
  (AT_Num term)
  const
{
    if( term >= iNormal) return round_index->search(term - iNormal);
    else return AT_Record_List::null;
}

AT_Record_List DRS_RoundIndex::search
  (AT_String term)
  const
{
  return DRS_IntegerIndex::search(RemoveDecimal(term));
}

// range search
AT_Record_List DRS_RoundIndex::search
  (AT_Num start,
   AT_Num end)
  const
{
  if (end >= start)
    return round_index->search((start >= iNormal) ? start - iNormal : start,
                            (end >= iNormal) ?  end - iNormal : end);
  return AT_Record_List::null;
}

AT_Record_List DRS_RoundIndex::search
  (AT_String start,
   bool startIncl,
   AT_String end,
   bool endIncl)
  const
{
  return DRS_IntegerIndex::search(RemoveDecimal(start),
                                  startIncl,
                                  RemoveDecimal(end),
                                  endIncl);
}

inline byte *DRS_RoundIndex::RemoveDecimal
  (const byte *interm,
   uint *len) const
{
  static byte out_buf[100];

  if (iPrefPrecision && interm)
  {
    uint l;
    if (!len)
      len = &l;
      
    *len = std::strlen((const char*) interm);
    uint size = min(*len, sizeof(out_buf) - iPrefPrecision);
    const byte *end = &interm[size];
    const byte *dec = (const byte*) strchr((const char*)interm, '.');
    if (dec)
    {
      uint dec_len = end - dec;
      memcpy(out_buf, interm, dec - interm);
      memcpy(&out_buf[dec - interm], dec + 1, dec_len - 1);

      if (dec_len < iPrefPrecision)
      {
        memset(&out_buf[size - 1], '0', iPrefPrecision - dec_len);
        *len = size - 1 + iPrefPrecision - dec_len;
        out_buf[*len] = 0;
      }
      else
      {
        *len = size - 1 - iPrefPrecision + dec_len;
        out_buf[size - 1 - iPrefPrecision + dec_len] = 0;
      }

      return out_buf;
    }
    else
    {
      memcpy(out_buf, interm, size);
      memset(&out_buf[size], '0', iPrefPrecision);
      *len = size + iPrefPrecision;
      out_buf[*len] = 0;
    }

    return out_buf;
  }
  else
    return const_cast<byte*>(interm);
}

inline byte *DRS_RoundIndex::InsertDecimal
  (const byte *interm,
   uint len)
{
  static byte out_buf[102];

  if (iPrefPrecision && interm)
  {
    memcpy(out_buf, interm, len - iPrefPrecision);
    out_buf[len - iPrefPrecision] = '.';
    memcpy(&out_buf[len - iPrefPrecision + 1], &interm[len - iPrefPrecision], iPrefPrecision);
  }
  else
    return const_cast<byte*>(interm);

  return out_buf;
}

///////////////////////////////////////////////
//Quantile indexes
DRS_QuantileIndex::DRS_QuantileIndex( DRS_Index &base,
                                      DRS_IndexDefInteger const &rIndexDef,
                                      AT_Quantile_List const &rQuantile,
                                      uint indexNum)
           : DRS_Index(base),
             DRS_IndexDefInteger( DRS_IndexDefInteger(rIndexDef)),
             quant_index(ARL_CreateQuantileIndex(base, rQuantile, indexNum))
{}

DRS_QuantileIndex::DRS_QuantileIndex( const DRS_QuantileIndex & rhs)
    : DRS_Index(*rhs.quant_index->getBaseIndex()),
      DRS_IndexDefInteger(rhs),
      quant_index(ARL_CopyQuantileIndex(*rhs.quant_index))
{
}

DRS_QuantileIndex::~DRS_QuantileIndex()
{
  ARL_DeleteQuantileIndex(quant_index);
}

AT_Index::Iter DRS_QuantileIndex::SeekTerm( byte *sSeekString)
{
//  throw AT_Function_Err( *this, "SeekTerm");
  AT_Num val = atoAT_Num( (const char*) sSeekString);
  return begin( max( val, (AT_Num)iNormal) - iNormal);
}

void DRS_QuantileIndex::TermGet( AT_Index::Iter const &rIter, byte *sDest, uint iDestLen, uint32 *pOccurrences)
{
  if( !sDest || !iDestLen) return;

  AT_Index_Term idx_term = *rIter;
  if( !idx_term)
    throw AT_Logic_Err( *this, "index term");

       //GetTerm: Can I get the integer val and passs NULLs for sDest???
       AT_Num val = idx_term.getTerm( ) + 1; // sDest, iDestLen);
         {
         char sVal[34]; //ultoa max len = 33
         AT_Numtoa( val, (char *) sVal);
         std::strncpy( (char*) sDest, sVal, iDestLen);
         sDest[iDestLen-1];
         }

  if( pOccurrences && DRS_Index::hasCounts())
    *pOccurrences = idx_term.getRecordCount();

 }

//IntegerIndex: term search
AT_Record_List DRS_QuantileIndex::search
  (AT_Num term)
  const
{
    if( term >= 1) return quant_index->search(term - 1);
    else return AT_Record_List::null;
}

AT_Record_List DRS_QuantileIndex::search
  (AT_String term)
  const
{
    AT_Num t;
    if (!!term && ((t = atoAT_Num(term))) >= 1)
      {
      return quant_index->search(t - 1);
      }
    else
      return AT_Record_List::null;
}

// range search
AT_Record_List DRS_QuantileIndex::search
  (AT_Num start,
   AT_Num end)
  const
{
  if (end >= start)
    return quant_index->search((start >= 1) ? start - 1 : start,
                            (end >= 1) ?  end - 1 : end);
  return AT_Record_List::null;
}

AT_Record_List DRS_QuantileIndex::search
  (AT_String start,
   bool startIncl,
   AT_String end,
   bool endIncl)
  const
{
  AT_Num start_val = (!!start) ? (atoAT_Num(start) + !startIncl) - 1 : 0;
  if (start_val == AT_MAXNUM)
    // AV >=0 search is the same as >0 because there is no quantile 0
    start_val = 0;

  AT_Num end_val   =   (!!end) ? (atoAT_Num(  end) - !endIncl)   - 1 : AT_MAXNUM;
  if (!!end && end_val >= AT_MAXNUM - 1)
    // AV <1, <=0, <0 searches should return 0 recs
    return AT_Record_List::null;

  if (end_val >= start_val)
    return quant_index->search(start_val, end_val);
  return AT_Record_List::null;
}

AT_Index::Iter DRS_QuantileIndex::begin(AT_Num term) const
{
  return quant_index->begin( term);
}
AT_Index::Iter DRS_QuantileIndex::begin(AT_String term) const
{
  return quant_index->begin( term);
}
AT_Index::Iter DRS_QuantileIndex::end() const
{
  return quant_index->end();
}

