/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.

#include "CryLegacy_precompiled.h"
#include "XMLCPB_AttrSetTableWriter.h"
#include "XMLCPB_Writer.h"

using namespace XMLCPB;

const AttrSetID CAttrSetTableWriter::SEARCHING_ID = 0Xfffffffe;

//////////////////////////////////////////////////////////////////////////

CAttrSetTableWriter::CAttrSetTableWriter(uint32 maxNumSets, uint32 bufferSize)
    : m_maxNumSets(maxNumSets)
    , m_buffer(bufferSize)
    , m_pWriter(NULL)
    , m_pCheckSet(NULL)
    , m_checkHash(0)
    , m_sortedIndexes(256, SetHasher(GetThis()), SetHasher(GetThis()))
{
}

void CAttrSetTableWriter::Init(CWriter* pWriter)
{
    m_pWriter = pWriter;
    m_buffer.Init(pWriter, false);
}


//////////////////////////////////////////////////////////////////////////
// AddSet could be used directly instead, with some changes. But then it would be very inefficient because it would need to copy every to-compare set first into the buffer.
AttrSetID CAttrSetTableWriter::GetSetID(const CAttrSet& set)
{
    m_pCheckSet = &set;  // the internal std::set comparison code will use this set when trying to find SEARCHING_ID
    m_checkHash = SetHasher::HashSet(set);
    HashTableType::const_iterator iter = m_sortedIndexes.find(SEARCHING_ID);
    m_pCheckSet = NULL;

    // found the set
    if (iter != m_sortedIndexes.end())
    {
        return *iter;
    }

    // didnt find
    AttrSetID ID = AddSet(set);
    return ID;
}


//////////////////////////////////////////////////////////////////////////

AttrSetID CAttrSetTableWriter::AddSet(const CAttrSet& set)
{
    assert(m_setAddrs.size() < m_maxNumSets);
    assert(set.m_numAttrs <= MAX_NUM_ATTRS);

    AttrSetID addedSetId = m_setAddrs.size();
    m_setAddrs.push_back(CBufferWriter::SAddr());
    CBufferWriter::SAddr& setAddr = m_setAddrs.back();
    m_buffer.AddDataNoSplit(setAddr, set.m_pHeaders, sizeof(set.m_pHeaders[0]) * set.m_numAttrs);  // no split = can not be internally splited in diferent internal buffers. Because we want to be able to read it in one go with the translated pointer
    m_numAttrsArray.push_back(set.m_numAttrs);

    m_sortedIndexes.insert(addedSetId);

    return addedSetId;
}


//////////////////////////////////////////////////////////////////////////

void CAttrSetTableWriter::WriteToFile()
{
    uint32 numSets = GetNumSets();
    if (numSets == 0)
    {
        return;
    }

    FlatAddrVec flatAddrs;
    CalculateFlatAddrs(flatAddrs);

    uint8* pNumTypes = &(m_numAttrsArray[0]);
    m_pWriter->WriteDataIntoFile(pNumTypes, sizeof(*pNumTypes) * numSets);

    FlatAddr16* pFlatAddrs = &(flatAddrs[0]);
    m_pWriter->WriteDataIntoFile(pFlatAddrs, sizeof(*pFlatAddrs) * numSets);

    m_buffer.WriteToFile();
}


//////////////////////////////////////////////////////////////////////////

void CAttrSetTableWriter::WriteToMemory(uint8*& rpData, uint32& outWriteLoc)
{
    uint32 numSets = GetNumSets();
    if (numSets == 0)
    {
        return;
    }

    FlatAddrVec flatAddrs;
    CalculateFlatAddrs(flatAddrs);

    uint8* pNumTypes = &(m_numAttrsArray[0]);
    m_pWriter->WriteDataIntoMemory(rpData, pNumTypes, sizeof(*pNumTypes) * numSets, outWriteLoc);

    FlatAddr16* pFlatAddrs = &(flatAddrs[0]);
    m_pWriter->WriteDataIntoMemory(rpData, pFlatAddrs, sizeof(*pFlatAddrs) * numSets, outWriteLoc);

    m_buffer.WriteToMemory(rpData, outWriteLoc);
}


//////////////////////////////////////////////////////////////////////////
// total size

uint32 CAttrSetTableWriter::GetDataSize() const
{
    uint32 size = GetNumSets() * sizeof(uint8) + GetNumSets() * sizeof(FlatAddr16);
    size += GetSetsDataSize();

    return size;
}


//////////////////////////////////////////////////////////////////////////
// only the size of all the headers

uint32 CAttrSetTableWriter::GetSetsDataSize() const
{
    uint32 size = m_buffer.GetDataSize();
    return size;
}


//////////////////////////////////////////////////////////////////////////

void CAttrSetTableWriter::CalculateFlatAddrs(FlatAddrVec& outFlatAddrs)
{
    uint32 numSets = GetNumSets();
    outFlatAddrs.resize(numSets);

    for (int i = 0; i < numSets; ++i)
    {
        FlatAddr16& flatAddr = outFlatAddrs[i];
        flatAddr = m_buffer.ConvertSegmentedAddrToFlatAddr(m_setAddrs[i]);
    }
}