/*
* 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_AttrReader.h"
#include "XMLCPB_Reader.h"

using namespace XMLCPB;


//////////////////////////////////////////////////////////////////////////
// format in mem: look at CAttrLive::Compact()

CAttrReader::CAttrReader(CReader& Reader)
    : m_Reader(Reader)
    , m_addr(XMLCPB_INVALID_FLATADDR)
    , m_nameId(XMLCPB_INVALID_ID)
    , m_type(DT_INVALID)
{
}


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

void CAttrReader::InitFromCompact(FlatAddr addr, uint16 header)
{
    assert(addr != XMLCPB_INVALID_FLATADDR);

    m_type = eAttrDataType(header & MASK_TYPEID);
    m_nameId = header >> BITS_TYPEID;  // nameStringId is always in the upper part, typeID in the lower part
    m_addr = addr;
}


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

FlatAddr CAttrReader::GetAddrNextAttr() const
{
    if (m_type != DT_RAWDATA)
    {
        return GetDataAddr() + GetDataTypeSize(m_type);
    }

    // Read in the size of the raw data which should be stored first at our location
    uint32 size;
    FlatAddr addr = m_Reader.ReadFromBuffer(GetDataAddr(), size);
    return addr + size;
}


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

const char* CAttrReader::GetName() const
{
    return m_Reader.GetAttrNamesTable().GetString(m_nameId);
}


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

void CAttrReader::Get(int32& val) const
{
    if (m_type >= DT_0 && m_type <= DT_10)
    {
        val = m_type - DT_0;
        return;
    }

    switch (m_type)
    {
    case DT_POS8:
    {
        uint8 rawVal;
        m_Reader.ReadFromBuffer(GetDataAddr(), rawVal);
        val = rawVal;
        break;
    }

    case DT_POS16:
    {
        uint16 rawVal;
        m_Reader.ReadFromBufferEndianAware(GetDataAddr(), rawVal);
        val = rawVal;
        break;
    }

    case DT_255:
    {
        val = 255;
        break;
    }

    case DT_INT32:
    {
        m_Reader.ReadFromBufferEndianAware(GetDataAddr(), val);
        break;
    }

    case DT_NEG16:
    {
        uint16 rawVal;
        m_Reader.ReadFromBufferEndianAware(GetDataAddr(), rawVal);
        val = -int(rawVal);
        break;
    }


    case DT_NEG8:
    {
        uint8 rawVal;
        m_Reader.ReadFromBuffer(GetDataAddr(), rawVal);
        val = -int(rawVal);
        break;
    }

    case DT_STR:
    {
        const char* pStr = NULL;
        Get(pStr);
        if (pStr)
        {
            val = atoi(pStr);
        }
        break;
    }

    case DT_0:
    {
        val = 0;
        break;
    }

    default:
    {
        assert(false);
        break;
    }
    }
}

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

void CAttrReader::Get(int64& val) const
{
    switch (m_type)
    {
    case DT_INT64:
        m_Reader.ReadFromBufferEndianAware(GetDataAddr(), val);
        break;

    default:
    {
        int32 int32Val;
        Get(int32Val);
        val = int32Val;
        break;
    }
    }
}

void CAttrReader::Get(uint64& val) const
{
    int64 valInt64;
    Get(valInt64);
    val = uint64(valInt64);
}



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

void CAttrReader::Get(uint& val) const
{
    int valInt;
    Get(valInt);
    val = uint(valInt);
}


void CAttrReader::Get(int16& val) const
{
    int valInt;
    Get(valInt);
    val = valInt;
}


void CAttrReader::Get(int8& val) const
{
    int valInt;
    Get(valInt);
    val = valInt;
}

void CAttrReader::Get(uint8& val) const
{
    int valInt;
    Get(valInt);
    val = valInt;
}

void CAttrReader::Get(uint16& val) const
{
    int valInt;
    Get(valInt);
    val = valInt;
}


void CAttrReader::Get(bool& val) const
{
    int valInt;
    Get(valInt);
    val = valInt != 0;
}


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

void CAttrReader::Get(const char*& pStr) const
{
    if (m_type == DT_RAWDATA) // very big strings are stored as raw data
    {
        uint32 size;
        FlatAddr addr = m_Reader.ReadFromBuffer(GetDataAddr(), size);
        pStr = (const char*)(m_Reader.GetPointerFromFlatAddr(addr));
        assert(pStr[size - 1] == 0);
        return;
    }
    if (IsTypeStrConstant(m_type))
    {
        assert(m_type >= DT_FIRST_CONST_STR && m_type <= DT_LAST_CONST_STR);
        pStr = GetConstantString(m_type - DT_FIRST_CONST_STR);
        return;
    }

    if (m_type != DT_STR)
    {
        assert(false);
        pStr = "";
        return;
    }

    uint16 rawVal;
    m_Reader.ReadFromBufferEndianAware(GetDataAddr(), rawVal);
    StringID stringId = rawVal;
    pStr = m_Reader.GetStrDataTable().GetString(stringId);
}



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

void CAttrReader::Get(float& val) const
{
    switch (m_type)
    {
    case DT_F1:
    {
        m_Reader.ReadFromBuffer(GetDataAddr(), val);
        break;
    }

    case DT_STR:
    {
        const char* pStr = NULL;
        Get(pStr);
        if (pStr)
        {
            val = float(atof(pStr));
        }
        break;
    }

    case DT_F1_1DEC:
    {
        int8 byteVal;
        m_Reader.ReadFromBuffer(GetDataAddr(), byteVal);
        val = byteVal;
        val /= 10;
        break;
    }

    default:
    {
        int32 intVal;
        Get(intVal);
        val = float( intVal );
        break;
    }
    }
}

void CAttrReader::Get(double& val) const
{
    switch (m_type)
    {
    case DT_F1:
    {
        m_Reader.ReadFromBuffer(GetDataAddr(), val);
        break;
    }

    case DT_STR:
    {
        const char* pStr = NULL;
        Get(pStr);
        if (pStr)
        {
            val = atof(pStr);
        }
        break;
    }

    case DT_F1_1DEC:
    {
        int8 byteVal;
        m_Reader.ReadFromBuffer(GetDataAddr(), byteVal);
        val = byteVal;
        val /= 10;
        break;
    }

    default:
    {
        int32 intVal;
        Get(intVal);
        val = double(intVal);
        break;
    }
    }
}

void CAttrReader::Get(Vec2& _val) const
{
    Vec3 val;
    Get(val);
    _val.x = val.x;
    _val.y = val.y;
}

void CAttrReader::Get(Ang3& _val) const
{
    Vec3 val;
    Get(val);
    _val.x = val.x;
    _val.y = val.y;
    _val.z = val.z;
}

//////////////////////////////////////////////////////////////////////////
// look at CAttrWriter::PackFloatInSemiConstType() for some explanation.

float CAttrReader::UnpackFloatInSemiConstType(uint8 mask, uint32 ind, FlatAddr& addr) const
{
    uint32 shift = ind * 2;

    uint32 type = (mask >> shift) & 3;

    switch (type)
    {
    case PFSC_0:
        return 0;
    case PFSC_1:
        return 1;
    case PFSC_N1:
        return -1;
    case PFSC_VAL:
    {
        float val;
        addr = m_Reader.ReadFromBuffer(addr, val);
        return val;
    }
    default:
    {
        assert(false);
        return 0;
    }
    }
}


void CAttrReader::Get(Vec3& val) const
{
    switch (m_type)
    {
    case DT_F3:
    {
        FlatAddr addr = m_Reader.ReadFromBuffer(GetDataAddr(), val.x);
        addr = m_Reader.ReadFromBuffer(addr, val.y);
        m_Reader.ReadFromBuffer(addr, val.z);
        break;
    }

    case DT_F3_1CONST:
    case DT_F3_2CONST:
    case DT_F3_3CONST:
    {
        uint8 mask;
        FlatAddr addr = m_Reader.ReadFromBuffer(GetDataAddr(), mask);
        val.x = UnpackFloatInSemiConstType(mask, 0, addr);
        val.y = UnpackFloatInSemiConstType(mask, 1, addr);
        val.z = UnpackFloatInSemiConstType(mask, 2, addr);
        break;
    }

    case DT_F3_100:
    {
        val.x = 1;
        val.y = 0;
        val.z = 0;
        break;
    }

    case DT_F3_010:
    {
        val.x = 0;
        val.y = 1;
        val.z = 0;
        break;
    }

    case DT_F3_001:
    {
        val.x = 0;
        val.y = 0;
        val.z = 1;
        break;
    }

    case DT_0:
    {
        val.x = val.y = val.z = 0;
        break;
    }

    default:
    {
        assert(false);
        break;
    }
    }
}


void CAttrReader::Get(Quat& val) const
{
    switch (m_type)
    {
    case DT_F4:
    {
        FlatAddr addr = m_Reader.ReadFromBuffer(GetDataAddr(), val.v.x);
        addr = m_Reader.ReadFromBuffer(addr, val.v.y);
        addr = m_Reader.ReadFromBuffer(addr, val.v.z);
        m_Reader.ReadFromBuffer(addr, val.w);
        break;
    }

    case DT_F4_1CONST:
    case DT_F4_2CONST:
    case DT_F4_3CONST:
    case DT_F4_4CONST:
    {
        uint8 mask;
        FlatAddr addr = m_Reader.ReadFromBuffer(GetDataAddr(), mask);
        val.v.x = UnpackFloatInSemiConstType(mask, 0, addr);
        val.v.y = UnpackFloatInSemiConstType(mask, 1, addr);
        val.v.z = UnpackFloatInSemiConstType(mask, 2, addr);
        val.w = UnpackFloatInSemiConstType(mask, 3, addr);
        break;
    }


    case DT_0:
    {
        val.v.x = val.v.y = val.v.z = val.w = 0;
        break;
    }

    default:
    {
        assert(false);
        break;
    }
    }
}


void CAttrReader::Get(uint8*& rdata, uint32& outSize) const
{
    assert(m_type == DT_RAWDATA);

    // Read in the size of the raw chunk which follows
    uint32 size;
    FlatAddr addr = m_Reader.ReadFromBuffer(GetDataAddr(), size);

    // Realloc the buffer so it's large enough to handle the new data's size
    rdata = (uint8*)realloc((void*)rdata, size * sizeof(uint8));
    m_Reader.ReadFromBuffer(addr, rdata, size);
    outSize = size;
}




//////////////////////////////////////////////////////////////////////////
// reads any attr value, converts to string and store it into an string
// is separated from the normal Get functions in purpose: This function should be used only for error handling or other special situations

void CAttrReader::GetValueAsString(string& str) const
{
    switch (XMLCPB::GetBasicDataType(m_type))
    {
    case DT_STR:
        ValueToString<const char*>(str, "%s");
        break;

    case DT_INT32:
        ValueToString<uint32>(str, "%d");
        break;

    case DT_F1:
        ValueToString<float>(str, "%f");
        break;

    case DT_F3:
    {
        Vec3 val;
        Get(val);
        str.Format("%f,%f,%f", val.x, val.y, val.z);
        break;
    }

    case DT_F4:
    {
        Quat val;
        Get(val);
        str.Format("%f,%f,%f,%f", val.v.x, val.v.y, val.v.z, val.w);
        break;
    }

    case DT_INT64:
        ValueToString<uint64>(str, "%lu");
        break;

    case DT_RAWDATA:
    {
        uint32 size;
        m_Reader.ReadFromBuffer(GetDataAddr(), size);
        str.Format("RawData %d", size);
        break;
    }

    default:
        str = "";
        assert(false);
        break;
    }
}