/* ------------------------------------------------------------------
 * Copyright (C) 1998-2009 PacketVideo
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 * -------------------------------------------------------------------
 */
#ifndef STRING_KEYVALUE_STORE_H_INCLUDED
#define STRING_KEYVALUE_STORE_H_INCLUDED

#ifndef OSCL_BASE_H_INCLUDED
#include "oscl_base.h"
#endif
#ifndef OSCL_MEM_H_INCLUDED
#include "oscl_mem.h"
#endif
#ifndef OSCL_MEM_MEMPOOL_H_INCLUDED
#include "oscl_mem_mempool.h"
#endif
#ifndef OSCL_STR_PTR_LEN_H_INCLUDED
#include "oscl_str_ptr_len.h"
#endif
#ifndef OSCL_VECTOR_H_INCLUDED
#include "oscl_vector.h"
#endif

#define KEYVALUESTORE_HASH_TABLE_SIZE_FOR_KEYS 1000
#define KEYVALUESTORE_MAX_SIZE 4000                 // same as the RTSP parcom, but this size shouldn't include entity body size, which the composer library has no control
#define KEYVALUESTORE_VECTOR_RESERVE_VALUE 10       // for iStrCSumPtrLenWrapperVector.reserve()


// This StrCSumPtrLen wrapper wraps StrCSumPtrLen with the following new features.
// (1) changed checksum calculation algorithm to make checksum as an identifier to differentiate different StrCSumPtrLen object.
//     This will be used in quick string comparison/search for field key of HTTP header.
//     Note that in StrCSumPtrLen, the checksum is calulated as the sum of the ASCII codes of all string charaters (lowercase).
//     And this is not quite efficient. The modified version is contrained within 500 (see the following getChecksum()), with tiny
//     performance loss.
// (2) support simplified link list. This is mainly used for multiple same header field cases (i.e. same field key, but multiple field
//     values).

struct StrCSumPtrLenWrapper
{
public:
    // constructor
    StrCSumPtrLenWrapper() : iNext(NULL)
    {
        ;
    }

    // copy constructor
    StrCSumPtrLenWrapper(const StrCSumPtrLen &x) : iStr(x), iNext(NULL)
    {
        ;
    }
    StrCSumPtrLenWrapper(const char *x) : iStr(x), iNext(NULL)
    {
        ;
    }
    StrCSumPtrLenWrapper(const char *x, const uint32 len) : iStr(x, len), iNext(NULL)
    {
        ;
    }

    // destructor
    ~StrCSumPtrLenWrapper()
    {
        clear();
    }

    // use checksum (with some changes) as an identifier, will be used to differentiate different StrCSumPtrLen objects.
    uint32 getChecksum()
    {
        // algorithm: checksum = (checksum % 1000) / 2
        return (uint32)((uint32)iStr.getCheckSum() % KEYVALUESTORE_HASH_TABLE_SIZE_FOR_KEYS) / 2;
    }

    // add to linked list
    void push_back(StrCSumPtrLenWrapper *aStrLinkTo)
    {
        StrCSumPtrLenWrapper **pWrapper = &iNext;
        while (*pWrapper) pWrapper = &((*pWrapper)->iNext);
        *pWrapper = aStrLinkTo;
    }

    // get next element
    StrCSumPtrLenWrapper *getNext()
    {
        return iNext;
    }

    // clear
    void clear()
    {
        iStr.setPtrLen("", 0);
        iNext = NULL;
    }

    // empty
    bool empty()
    {
        return (iNext == NULL && iStr.length() == 0);
    }

    // wrap some functions of StrCSumPtrLen
    const char* c_str() const
    {
        return iStr.c_str();
    }
    uint32 length() const
    {
        return (uint32)iStr.length();
    }
    uint32 size() const
    {
        return (uint32)iStr.size();
    }
    bool isCIEquivalentTo(const StrCSumPtrLen& rhs) const
    {
        return (iStr.isCIEquivalentTo(rhs) > 0);
    }
    bool isCIEquivalentTo(const char*s, const uint32 len) const
    {
        StrCSumPtrLen str(s, len);
        return (iStr.isCIEquivalentTo(str) > 0);
    }
    void setPtrLen(const char* newPtr, uint32 newLen)
    {
        iStr.setPtrLen(newPtr, newLen);
        iNext = NULL;
    }

    void setPtrLen(const StrPtrLen &aString)
    {
        iStr.setPtrLen(aString.c_str(), aString.length());
        iNext = NULL;
    }

    // operator =
    StrCSumPtrLenWrapper& operator=(const StrCSumPtrLen& rhs)
    {
        iStr = rhs;
        iNext = NULL;
        return *this;
    }

    StrCSumPtrLenWrapper& operator=(const StrPtrLen& rhs)
    {
        iStr = rhs;
        iNext = NULL;
        return *this;
    }

    StrCSumPtrLenWrapper& operator=(const StrCSumPtrLenWrapper& rhs)
    {
        iStr = rhs.iStr;
        iNext = rhs.iNext;
        return *this;
    }

private:
    StrCSumPtrLen iStr;
    StrCSumPtrLenWrapper *iNext;
};

// class declaration
class OsclMemPoolVariableChunkAllocator;

// class for encapsulating string based key-value pair handlings
// The major feature for this class is hash-table based key search. To handle hash table conllision, linear search
// table is used. Basically a whole hash table is divided into two parts, the first part is for real hash table,
// the second part is for collision.
class StringKeyValueStore
{
    public:
        // add a key-value pair, key-value will be made a copy inside the store
        // return code is one of following StringKeyValueStoreReturnCodes
        int32 addKeyValuePair(const StrCSumPtrLen &aNewKey, const StrPtrLen &aNewValue, const bool aNeedReplaceOldValue = false);
        int32 addKeyValuePair(const StrCSumPtrLen &aNewKey, const char *aNewValue, const bool aNeedReplaceOldValue = false);

        // This overloaded function is for this case, when the key and value are parsed from the data stream, to avoid memory copy,
        // we locate the segment in the data stream for the key and value, which has no NULL terminatior.
        int32 addKeyValuePair(const char *aNewKey, const uint32 aKeyLength, const char *aNewValue, const uint32 aValueLength,
                              const bool aNeedReplaceOldValue = false);

        // get number of key-value pairs, which could be different from the number of keys,
        // when there might be a key with multiple values
        uint32 getNumberOfKeyValuePairs()
        {
            return iTotalNumberOfKeyValuePairs;
        }
        uint32 getNumberOfKeys()
        {
            return iFieldKeyTableIndexVector.size();
        }
        uint32 getTotalKeyValueLength()
        {
            return iTotalKeyValueLength;
        }
        // aListSize=0 means retrieves whatever the library has
        // return the actual list size
        uint32 getCurrentKeyList(StrPtrLen *&aFieldKeyList, const uint32 aListSize = 0);
        bool getValueByKey(const StrCSumPtrLen &aKey, StrPtrLen &aValue, const uint32 index = 0);

        // for one key multiple value cases.
        // return value: 0 => no value for the given key, 1 or more => number of values for the given key
        uint32 getNumberOfValuesByKey(const StrCSumPtrLen &aKey);

        // search
        bool isKeyValueAvailable(const StrCSumPtrLen &aKey);

        // remove
        bool removeKeyValuePair(const StrCSumPtrLen &aKey);

        // clear
        void clear();

        // copy
        bool copy(StringKeyValueStore& aStore);


        // query store infomation
        uint32 getCurrentMemoryUsage();
        uint32 getStoreSize();
        uint32 getAvailableSize();

        // constructor
        StringKeyValueStore() : iVariableSizeMemPool(NULL)
        {
            ;
        }

        // destructor
        ~StringKeyValueStore();

        // factory method
        static StringKeyValueStore* create(const uint32 aStoreSize = KEYVALUESTORE_MAX_SIZE);

        enum StringKeyValueStoreReturnCodes
        {
            StringKeyValueStore_Success = 0,
            StringKeyValueStore_Failure = -1,
            StringKeyValueStore_NoMemory = -2
        };

    private:
        uint32 calculateChecksum(const char *aBuffer, uint32 aBufferLength);
        bool construct(const uint32 aStoreSize);

        // for conflict handling
        // To handle hash table collisions, the hash table is divided into two parts, one part is for first hit elements, and
        // all the conflict elements is collected into another part. The reason for not choosing link list approach is, usually
        // conllision possibilities are very low, so no need to keep link list for every table element. (Note that if the collision
        // possibilities go high, hash function should be updated to keep this collision possibility low) Another reason is, don't
        // want to change the current structure too much.

        // aFindKey=true, the purpose of getting table index is for element search or removal;
        // aFindKey=false, the purpose is for adding a new element
        // return value: table index, for operation failure, return -1
        int32 getHashTableIndex(const StrCSumPtrLen &aKey, const bool aFindKey = true);
        // linear seach in linear search area of hash table for the given key
        // return table index for linear search area, -1 means the input key is not found
        int32 query(const StrCSumPtrLen &aKey);

        // supporting function for addKeyValuePair() and removeKeyValuePair()
        // return code is one of StringKeyValueStoreReturnCodes
        int32 addKeyToStore(const StrCSumPtrLen &aNewKey, int32 tableIndex);

        // centralize memory related operations within these two fucntions
        bool storeNewKeyValueItem(const char *aItem, const int32 aItemLength, char *&aNewLocation);
        void releaseOldKeyValueItem(const char *aItem, const int32 aItemLength);

    private:

        uint32 iTotalNumberOfKeyValuePairs;
        uint32 iTotalKeyValueLength;

        // field key@value tables (field key table is a hash table per se)
        StrCSumPtrLenWrapper iFieldKeys[KEYVALUESTORE_HASH_TABLE_SIZE_FOR_KEYS];
        StrPtrLen iFieldVals[KEYVALUESTORE_HASH_TABLE_SIZE_FOR_KEYS];

        // use variable-size memory pool to replace fixed-size memory storage
        // OsclMemPoolResizableAllocator is not used because of overhead.
        // That variable-size memory pool has fixed 28-byte header for each allocated memory segment. This
        // overhead is too much in the current user scenario, storing field key and value. Especially for
        // field key, it is usually less than 20 bytes. So that allocator is two expensive.
        OsclMemPoolVariableChunkAllocator* iVariableSizeMemPool;

        // storage for multiple field values with the same field key
        Oscl_Vector<StrCSumPtrLenWrapper, OsclMemAllocator> iStrCSumPtrLenWrapperVector;

        // save the parsed field key index of the field key table
        // The reason for this, usually less than a dozen of header fields are set for a message, comparing the
        // relatively big hash table, saving the table indices will save lots of search operations.
        Oscl_Vector<uint32, OsclMemAllocator> iFieldKeyTableIndexVector;
        uint32 iNumConflicts;
};

#endif // STRING_KEYVALUE_STORE_H_INCLUDED

