/*
* 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 <StdAfx.h>
#include "XMLBinaryNode.h"
#include <CrySizer.h>

#pragma warning(disable : 6031) // Return value ignored: 'sscanf'

//////////////////////////////////////////////////////////////////////////
CBinaryXmlData::CBinaryXmlData()
    : pNodes(0)
    , pAttributes(0)
    , pChildIndices(0)
    , pStringData(0)
    , pFileContents(0)
    , nFileSize(0)
    , bOwnsFileContentsMemory(true)
    , pBinaryNodes(0)
    , nRefCount(0)
{
}

//////////////////////////////////////////////////////////////////////////
CBinaryXmlData::~CBinaryXmlData()
{
    if (bOwnsFileContentsMemory)
    {
        delete [] pFileContents;
    }
    pFileContents = 0;

    delete [] pBinaryNodes;
    pBinaryNodes = 0;
}

void CBinaryXmlData::GetMemoryUsage(ICrySizer* pSizer) const
{
    pSizer->AddObject(pFileContents, nFileSize);
    const XMLBinary::BinaryFileHeader* pHeader = reinterpret_cast<const XMLBinary::BinaryFileHeader*>(pFileContents);
    pSizer->AddObject(pBinaryNodes, sizeof(CBinaryXmlNode) * pHeader->nNodeCount);
}

//////////////////////////////////////////////////////////////////////////
// CBinaryXmlNode implementation.
//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
// collect allocated memory  informations
void CBinaryXmlNode::GetMemoryUsage(ICrySizer* pSizer) const
{
    pSizer->AddObject(m_pData);
}

//////////////////////////////////////////////////////////////////////////
XmlNodeRef CBinaryXmlNode::getParent() const
{
    const XMLBinary::Node* const pNode = _node();
    if (pNode->nParentIndex != (XMLBinary::NodeIndex)-1)
    {
        return &m_pData->pBinaryNodes[pNode->nParentIndex];
    }
    return XmlNodeRef();
}

XmlNodeRef CBinaryXmlNode::createNode(const char* tag)
{
    assert(0);
    return 0;
}

//////////////////////////////////////////////////////////////////////////
bool CBinaryXmlNode::isTag(const char* tag) const
{
    return g_pXmlStrCmp(tag, getTag()) == 0;
}

const char* CBinaryXmlNode::getAttr(const char* key) const
{
    const char* svalue = GetValue(key);
    if (svalue)
    {
        return svalue;
    }
    return "";
}

bool CBinaryXmlNode::getAttr(const char* key, const char** value) const
{
    const char* svalue = GetValue(key);
    if (svalue)
    {
        *value = svalue;
        return true;
    }
    else
    {
        *value = "";
        return false;
    }
}


bool CBinaryXmlNode::haveAttr(const char* key) const
{
    return (GetValue(key) != 0);
}

//////////////////////////////////////////////////////////////////////////
bool CBinaryXmlNode::getAttr(const char* key, int& value) const
{
    const char* svalue = GetValue(key);
    if (svalue)
    {
        value = atoi(svalue);
        return true;
    }
    return false;
}

bool CBinaryXmlNode::getAttr(const char* key, unsigned int& value) const
{
    const char* svalue = GetValue(key);
    if (svalue)
    {
        value = strtoul(svalue, NULL, 10);
        return true;
    }
    return false;
}

//////////////////////////////////////////////////////////////////////////
bool CBinaryXmlNode::getAttr(const char* key, int64& value) const
{
    const char* svalue = GetValue(key);
    if (svalue)
    {
        azsscanf(svalue, "%" PRId64, &value);
        return true;
    }
    return false;
}

//////////////////////////////////////////////////////////////////////////
bool CBinaryXmlNode::getAttr(const char* key, uint64& value, bool useHexFormat) const
{
    const char* svalue = GetValue(key);
    if (svalue)
    {
        if (useHexFormat)
        {
            azsscanf(svalue, "%" PRIX64, &value);
        }
        else
        {
            azsscanf(svalue, "%" PRIu64, &value);
        }
        return true;
    }
    return false;
}

bool CBinaryXmlNode::getAttr(const char* key, bool& value) const
{
    const char* svalue = GetValue(key);
    if (svalue)
    {
        value = atoi(svalue) != 0;
        return true;
    }
    return false;
}

bool CBinaryXmlNode::getAttr(const char* key, float& value) const
{
    const char* svalue = GetValue(key);
    if (svalue)
    {
        value = (float)atof(svalue);
        return true;
    }
    return false;
}

bool CBinaryXmlNode::getAttr(const char* key, double& value) const
{
    const char* svalue = GetValue(key);
    if (svalue)
    {
        value = atof(svalue);
        return true;
    }
    return false;
}

bool CBinaryXmlNode::getAttr(const char* key, Ang3& value) const
{
    const char* svalue = GetValue(key);
    if (svalue)
    {
        float x, y, z;
        if (azsscanf(svalue, "%f,%f,%f", &x, &y, &z) == 3)
        {
            value(x, y, z);
            return true;
        }
    }
    return false;
}

//////////////////////////////////////////////////////////////////////////
bool CBinaryXmlNode::getAttr(const char* key, Vec3& value) const
{
    const char* svalue = GetValue(key);
    if (svalue)
    {
        float x, y, z;
        if (azsscanf(svalue, "%f,%f,%f", &x, &y, &z) == 3)
        {
            value = Vec3(x, y, z);
            return true;
        }
    }
    return false;
}

//////////////////////////////////////////////////////////////////////////
bool CBinaryXmlNode::getAttr(const char* key, Vec4& value) const
{
    const char* svalue = GetValue(key);
    if (svalue)
    {
        float x, y, z, w;
        if (azsscanf(svalue, "%f,%f,%f,%f", &x, &y, &z, &w) == 4)
        {
            value = Vec4(x, y, z, w);
            return true;
        }
    }
    return false;
}

bool CBinaryXmlNode::getAttr(const char* key, Vec3d& value) const
{
    const char* svalue = GetValue(key);
    if (svalue)
    {
        double x, y, z;
        if (azsscanf(svalue, "%lf,%lf,%lf", &x, &y, &z) == 3)
        {
            value = Vec3d(x, y, z);
            return true;
        }
    }
    return false;
}

//////////////////////////////////////////////////////////////////////////
bool CBinaryXmlNode::getAttr(const char* key, Vec2& value) const
{
    const char* svalue = GetValue(key);
    if (svalue)
    {
        float x, y;
        if (azsscanf(svalue, "%f,%f", &x, &y) == 2)
        {
            value = Vec2(x, y);
            return true;
        }
    }
    return false;
}

//////////////////////////////////////////////////////////////////////////
bool CBinaryXmlNode::getAttr(const char* key, Vec2d& value) const
{
    const char* svalue = GetValue(key);
    if (svalue)
    {
        double x, y;
        if (azsscanf(svalue, "%lf,%lf", &x, &y) == 2)
        {
            value = Vec2d(x, y);
            return true;
        }
    }
    return false;
}

//////////////////////////////////////////////////////////////////////////
bool CBinaryXmlNode::getAttr(const char* key, Quat& value) const
{
    const char* svalue = GetValue(key);
    if (svalue)
    {
        float w, x, y, z;
        if (azsscanf(svalue, "%f,%f,%f,%f", &w, &x, &y, &z) == 4)
        {
            value = Quat(w, x, y, z);
            return true;
        }
    }
    return false;
}

//////////////////////////////////////////////////////////////////////////
bool CBinaryXmlNode::getAttr(const char* key, ColorB& value) const
{
    const char* svalue = GetValue(key);
    if (svalue)
    {
        unsigned int r, g, b, a = 255;
        int numFound = azsscanf(svalue, "%u,%u,%u,%u", &r, &g, &b, &a);
        if (numFound == 3 || numFound == 4)
        {
            // If we only found 3 values, a should be unchanged, and still be 255
            if (r < 256 && g < 256 && b < 256 && a < 256)
            {
                value = ColorB(r, g, b, a);
                return true;
            }
        }
    }
    return false;
}


XmlNodeRef CBinaryXmlNode::findChild(const char* tag) const
{
    const XMLBinary::Node* const pNode = _node();
    const uint32 nFirst = pNode->nFirstChildIndex;
    const uint32 nAfterLast = pNode->nFirstChildIndex + pNode->nChildCount;
    for (uint32 i = nFirst; i < nAfterLast; ++i)
    {
        const char* sChildTag = m_pData->pStringData + m_pData->pNodes[m_pData->pChildIndices[i]].nTagStringOffset;
        if (g_pXmlStrCmp(tag, sChildTag) == 0)
        {
            return m_pData->pBinaryNodes + m_pData->pChildIndices[i];
        }
    }
    return 0;
}

//! Get XML Node child nodes.
XmlNodeRef CBinaryXmlNode::getChild(int i) const
{
    const XMLBinary::Node* const pNode = _node();
    assert(i >= 0 && i < (int)pNode->nChildCount);
    return m_pData->pBinaryNodes + m_pData->pChildIndices[pNode->nFirstChildIndex + i];
}

//////////////////////////////////////////////////////////////////////////
bool CBinaryXmlNode::getAttributeByIndex(int index, const char** key, const char** value)
{
    const XMLBinary::Node* const pNode = _node();
    if (index >= 0 && index < pNode->nAttributeCount)
    {
        const XMLBinary::Attribute& attr = m_pData->pAttributes[pNode->nFirstAttributeIndex + index];
        *key = _string(attr.nKeyStringOffset);
        *value = _string(attr.nValueStringOffset);
        return true;
    }
    return false;
}
//////////////////////////////////////////////////////////////////////////
bool CBinaryXmlNode::getAttributeByIndex(int index, XmlString& key, XmlString& value)
{
    const XMLBinary::Node* const pNode = _node();
    if (index >= 0 && index < pNode->nAttributeCount)
    {
        const XMLBinary::Attribute& attr = m_pData->pAttributes[pNode->nFirstAttributeIndex + index];
        key = _string(attr.nKeyStringOffset);
        value = _string(attr.nValueStringOffset);
        return true;
    }
    return false;
}
//////////////////////////////////////////////////////////////////////////