
/**********************************************************//**
 **
 ** @file index/collator/collator.h
 **
 ** Copyright (C) 2008  Xpace, LLC.  All rights reserved
 **
 ** www.xpace.net
 **
 **************************************************************/


#ifndef XPACE_COLLATOR_H
#define XPACE_COLLATOR_H

#include "base/sharedimpl.h"
#include "base/config.h"
#include "base/exception.h"

namespace Xpace
{
  /// A set of bytes for an indexer
  /// Keys are managed by Collators
  class Key
  {
  public:
    Key
      ();

  private:
    uint64 val;
    friend class Collator;
  };

  /// A Collator manages Keys.
  /// Contains functions to construct, compare, and represent Keys
  class XPACE_EXPORT Collator : public Configurable
  {
  public:
    /// creation failed
    class CantCreate : public Exception
    {
    public:
      /// @param config the Collator's Configuration
      /// @reason what caused the failure (e.g. File_Cant_Open)
      CantCreate
        (const String config,
         const Exception reason = Exception()) :
          Exception("Can't construct collator from XML fragment \"%1\".", reason)
      {
        addParam(config);
      }
    };

    /// @return a copy of this Collator
    virtual Collator* clone
      ()
	    const
    = 0;

    virtual ~Collator
      ()
    = 0;

    /// @return fixed size if collator returns fixed-size keys 
    ///   otherwise, return 0 
    virtual uint getFixedSize
      ()
      const
    = 0;

    /// remove collator state (if any)
    virtual void reset
      ()
    {
    }

    // --------------------------------------------------------
    // make/copy/get-from key

    /// get some memory for a key
    class Alloc
    {
    public:
      virtual ~Alloc
        ();

      /// @param size how many bytes
      /// @return the memory
      virtual byte* operator()
        (size_t size);
    };

    /// Construct a Key
    /// @param length this many bytes
    ///   max length = 32767, some collators less
    /// @param data create key from these bytes
    /// @param storage get memory from here iff storage != 0 and memory is needed
    /// @return the Key
    /// @throw Key_Too_Long
    virtual const Key makeKey
      (const byte* data,
       size_t length,
       Alloc* storage)
	    const
    = 0;

    class Key_Too_Long : public Exception
    {
    public :
      Key_Too_Long
        (String collatorConfig,
         size_t actual,
         size_t maximum) :
      Exception("Key too long in collator %1: actual length %2, maximum for this collator %3")
      {
        addParam(collatorConfig);
        addParam(String().setNum(actual));
        addParam(String().setNum(maximum));
      }
    };

    /// Get a Key's size, in bytes
    /// @param key the Key
    /// @return the size
    virtual size_t getKeySize
      (const Key& key)
      const
    = 0;   

    /// Get a Key's data
    /// @param key a pointer to the Key
    /// @return the data, not valid if Key is destroyed
    virtual const byte* getKeyData
      (const Key* key)
      const
    = 0;

    // --------------------------------------------------------
    // encode/decode keys

    /// a numeric stream, used to encode keys
    class numStream
    {
    public:
      virtual ~numStream() 
      { 
      }

      /// add a number to the stream
      /// @param n the number to add
      /// @return true on success, false on failure
      virtual bool add
        (int n)
      = 0;
      virtual bool add
        (uint n)
      = 0;
      virtual bool add
        (int64 n)
      = 0;
      virtual bool add
        (uint64 n)
      = 0;
    
      /// get a number from the stream
      /// @return the number
      virtual int getInt
        ()
      = 0;
      virtual int getUint
        ()
      = 0;
      virtual int getInt64
        ()
      = 0;
      virtual int getUint64
        ()
      = 0;
    };

    /// write a Key to a NumStream
    /// @param targ the stream to which to write
    /// @param key the Key
    /// @return false iff NumStream::add() returns false
    virtual bool writeKey
      (numStream* targ,
       const Key& key)
    = 0;

    /// write delta (key2 - key1) to NumStream
    ///   N.B. key2 > key1
    /// @param delta difference goes here
    /// @param key1 subtract this key
    /// @param key2 from this key
    /// @return false iff NumStream::add() returns false
    virtual bool makeDeltaKey
      (numStream* delta,
       const Key& key1,
       const Key& key2)
    = 0;

    /// read a key from a NumStream
    /// @param stream the stream
    /// @storage get memory from here (if necessary)
    /// @return the key
    virtual const Key readKey
      (numStream* stream,
       Alloc* storage)
    = 0;

    /// construct key from base + delta
    /// @param base add delta to this key
    /// @delta get delta from here
    /// @storage get memory from here (if necessary)
    virtual const Key makeAbsKey
      (const Key& base,
       numStream* delta,
       byte** storage)
    = 0;

    // --------------------------------------------------------
    // compare two keys

    /// @return <0 iff (key1 < key2), 0 iff (key 1 == key2), >0 iff (key1 > key2)
    virtual int compare      
      (const Key& key1,
       const Key& key2)
      const
    = 0;

    /// @return true iff key1 < key2
    virtual bool lt    
      (const Key& key1,
       const Key& key2)
      const
    {
      return compare(key1, key2) < 0;
    };

    /// @return true iff key1 == key2
    virtual bool eq     
      (const Key& key1,
       const Key& key2)
      const
    {
      return !compare(key1, key2);
    };
    
    /// @return true iff eq(key, makeKey(ref))
    virtual bool eq
      (const Key& key,
       const BytesRef& ref)
      const
    = 0;

    /// @return true iff key is prefixed with prefix
    virtual bool eqPrefix   
      (const Key& key,
       const Key& prefix)
      const
    = 0;

    // --------------------------------------------------------
    // Truncation functions

    /// @return true iff term can match a regular expression (next() must return something meaningful)
    virtual bool regexOK()
      const
    = 0;

    virtual String next(const String& key)
      const
    = 0;

    // --------------------------------------------------------
    // Key <=> String

    /// @param key the Key
    /// @return a representation of the Key 
    virtual String keyToString
      (const Key& key)
      const
    = 0;

    /// @param key the Key
    /// @param string a buffer into which to copy a representation of the Key
    ///   NB the representation will be truncated if the buffer is not long enough
    /// @return the length of the Kay
    virtual size_t keyToString
      (const Key& key,
       Buf<utf16_t>* string)
      const
    = 0;

    /// convert a representation into a Key
    /// @param str the String to be converted
    /// @storage get memory from here (if necessary)
    /// @return the Key
    virtual const Key stringToKey
      (const String str,
       Alloc* storage)
      const
    = 0;

    // --------------------------------------------------------
    // helper functions

    /// get the internal value from a Key
    /// @param key the Key
    /// @return the vaue
    static
    uint64 getKeyVal
      (const Key& key);

    /// get a pointer to the internal value from a Key
    /// @param key the Key
    /// @return a pointer to the key's internal value
    static 
    const byte* getKeyValPtr
      (const Key* key);

    /// make a Key from an internal value
    /// @param val the internal value
    /// @return the Key
    static Key makeKey
      (uint64 val);

  protected:
    /// construct a collator
    /// @param config from this configuration
    Collator
      (const Configuration& config);
  };

  // ============================================================
  // ============================================================
  // ============================================================

  inline 
  Key::Key
    () :
      val(0)
  {
  };

  inline 
  Collator::~Collator
    ()
  {
  };

  // static
  inline uint64 Collator::getKeyVal
    (const Key& k)
  {
    return k.val;
  }

  // static
  inline const byte* Collator::getKeyValPtr
    (const Key* k)
  {
    return reinterpret_cast<const byte*>(&k->val);
  }

  // static
  inline Key Collator::makeKey
    (uint64 val)
  {
    Key k;
    k.val = val;
    return k;
  }

} // namespace

#endif
