//--------------------------------------------------------------------------------------
// Copyright (C) 2008 http://shift.delblanco.es
//--------------------------------------------------------------------------------------
// SHIFT Videogame is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by the
// Free Software Foundation, either version 3 of the License, or later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
//--------------------------------------------------------------------------------------
// XMLIO.c
//--------------------------------------------------------------------------------------
#include "dxstdafx.h"
#include "XMLIO.h"

#include <atlbase.h>

//--------------------------------------------------------------------------------------
// Constructor
//--------------------------------------------------------------------------------------
CXMLIO::CXMLIO()
{
    // Inicializamos variables
    m_pXMLDoc = NULL;
    m_pThisElm = NULL;
    m_Opened = false;

    // Tomamos e inicializamos el objeto XML
    HRESULT hr;
    V(CoInitialize(NULL));
    V(CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, IID_IXMLDOMDocument, (void**)&m_pXMLDoc));
    /*m_pXMLDoc->AddRef();*//* NO */
}

//--------------------------------------------------------------------------------------
// Destructor
//--------------------------------------------------------------------------------------
CXMLIO::~CXMLIO()
{
    SAFE_RELEASE(m_pXMLDoc);
}

//--------------------------------------------------------------------------------------
// Carga un fichero XML
//--------------------------------------------------------------------------------------
bool CXMLIO::LoadFile(LPCWSTR FileName)
{
    HRESULT hr;
    VARIANT_BOOL Success;
    if(!m_pXMLDoc) return false;

    // Cargamos el fichero
    hr = m_pXMLDoc->load(CComVariant(FileName), &Success);
    m_Opened = SUCCEEDED(hr) && Success;

    // Tomamos un puntero de la raíz
    if(m_Opened) m_Opened = MoveRoot();

    return m_Opened;
}

//--------------------------------------------------------------------------------------
// Guarda el fichero XML actual
//--------------------------------------------------------------------------------------
bool CXMLIO::SaveFile(LPCWSTR FileName)
{
    if(!m_pXMLDoc || !m_Opened) return false;

    // Guardamos el fichero
    if(!SUCCEEDED(m_pXMLDoc->save(CComVariant(FileName)))) return false;

    return true;
}

//--------------------------------------------------------------------------------------
// Crea un fichero XML con un elemento raíz
//--------------------------------------------------------------------------------------
bool CXMLIO::CreateFile(LPCWSTR FileName, LPCWSTR Root)
{
    VARIANT_BOOL Success;
    WCHAR txt[128];
    int len, pos = 0;

    if(!m_pXMLDoc) return false;
    len = lstrlenW(Root);
    if(len > 32) return false;

    // Preparamos el texto XML
    lstrcpyW(txt + pos, L"<?xml version=\"1.0\"?>\n<"); pos += 23;
    lstrcpyW(txt + pos, Root); pos += len;
    lstrcpyW(txt + pos, L"></"); pos += 3;
    lstrcpyW(txt + pos, Root); pos += len;
    lstrcpyW(txt + pos, L">\n"); pos += 2;

    // Cargamos y guardamos el fichero
    if(!SUCCEEDED(m_pXMLDoc->loadXML(txt, &Success))) return false;
    if(!SUCCEEDED(m_pXMLDoc->save(CComVariant(FileName)))) return false;

    return true;
}

//--------------------------------------------------------------------------------------
// Toma el valor de un atributo del elemento y fichero actuales
//--------------------------------------------------------------------------------------
bool CXMLIO::GetAttribute(LPCWSTR Attribute, LPWSTR Value)
{
    return GetSubAttribute(NULL, Attribute, Value);
}

//--------------------------------------------------------------------------------------
// Fija el valor de un atributo del elemento y fichero actuales
//--------------------------------------------------------------------------------------
bool CXMLIO::SetAttribute(LPCWSTR Attribute, LPCWSTR Value)
{
    return SetSubAttribute(NULL, Attribute, Value);
}

//--------------------------------------------------------------------------------------
// Toma el valor del elemento actual del fichero actual
//--------------------------------------------------------------------------------------
bool CXMLIO::GetElement(LPWSTR Value)
{
    return GetSubElement(NULL, Value);
}

//--------------------------------------------------------------------------------------
// Fija el valor del elemento actual del fichero actual
//--------------------------------------------------------------------------------------
bool CXMLIO::SetElement(LPCWSTR Value)
{
    return SetSubElement(NULL, Value);
}

//--------------------------------------------------------------------------------------
// Toma el valor de un atributo de un sub-elemento del elemento y fichero actuales
//--------------------------------------------------------------------------------------
bool CXMLIO::GetSubAttribute(LPWSTR Element, LPCWSTR Attribute, LPWSTR Value, long num)
{
    if(!m_pXMLDoc || !m_pThisElm || !m_Opened) return false;

    // Buscamos el elemento
    if(Element)
        if(!SubElement(Element, false, num)) return false;
    if(!m_pSubElm)
        m_pSubElm = m_pThisElm;

    // Tomamos el atributo
    CComBSTR name(Attribute);
    CComVariant val(VT_EMPTY);
    if(!SUCCEEDED(m_pSubElm->getAttribute(name,&val))) return false;
    if(val.vt != VT_BSTR) return false;

    lstrcpynW(Value, val.bstrVal, MAX_STRING);

    return true;
}

//--------------------------------------------------------------------------------------
// Fija el valor de un atributo de un sub-elemento del elemento y fichero actuales
//--------------------------------------------------------------------------------------
bool CXMLIO::SetSubAttribute(LPWSTR Element, LPCWSTR Attribute, LPCWSTR Value, long num)
{
    if(!m_pXMLDoc || !m_pThisElm || !m_Opened) return false;

    // Buscamos el elemento
    if(Element)
        if(!SubElement(Element, true, num)) return false;
    if(!m_pSubElm)
        m_pSubElm = m_pThisElm;

    // Ponemos el atributo
    CComBSTR name(Attribute);
    if(!SUCCEEDED(m_pSubElm->setAttribute(name,CComVariant(Value)))) return false;

    return true;
}

//--------------------------------------------------------------------------------------
// Toma el valor de un sub-elemento del elemento y fichero actuales
//--------------------------------------------------------------------------------------
bool CXMLIO::GetSubElement(LPWSTR Element, LPWSTR Value, long num)
{
    if(!m_pXMLDoc || !m_pThisElm || !m_Opened) return false;

    // Buscamos el elemento
    if(Element)
        if(!SubElement(Element, false, num)) return false;
    if(!m_pSubElm)
        m_pSubElm = m_pThisElm;

    // Tomamos el elemento
    CComVariant val(VT_EMPTY);
    if(!SUCCEEDED(m_pSubElm->get_nodeTypedValue(&val))) return false;
    if(val.vt != VT_BSTR) return false;

    lstrcpynW(Value, val.bstrVal, MAX_STRING);

    return true;
}

//--------------------------------------------------------------------------------------
// Fija el valor de un sub-elemento del elemento y fichero actuales
//--------------------------------------------------------------------------------------
bool CXMLIO::SetSubElement(LPWSTR Element, LPCWSTR Value, long num)
{
    if(!m_pXMLDoc || !m_pThisElm || !m_Opened) return false;

    // Buscamos el elemento
    if(Element)
        if(!SubElement(Element, true, num)) return false;
    if(!m_pSubElm)
        m_pSubElm = m_pThisElm;

    // Ponemos el elemento
    CComBSTR name(Element);
    if(!SUCCEEDED(m_pSubElm->put_nodeTypedValue(CComVariant(Value)))) return false;

    return true;
}


//--------------------------------------------------------------------------------------
// Moverse al elemento hijo indicado del elemento actual y fichero XML actual
//--------------------------------------------------------------------------------------
bool CXMLIO::MoveSubElement(LPWSTR Element, long num)
{
    if(!SubElement(Element, false, num)) return false;
    m_pThisElm = m_pSubElm;
    return true;
}

//--------------------------------------------------------------------------------------
// Moverse al elemento padre del elemento actual y fichero XML actual
//--------------------------------------------------------------------------------------
bool CXMLIO::MoveParent(void)
{
    CComPtr<IXMLDOMNode> Node = NULL;
    if(!SUCCEEDED(m_pThisElm->get_parentNode(&Node))) return false;

    CComQIPtr<IXMLDOMElement> e(Node);
    m_pThisElm = e;
    return true;
}

//--------------------------------------------------------------------------------------
// Moverse al elemento raíz del fichero XML actual
//--------------------------------------------------------------------------------------
bool CXMLIO::MoveRoot(void)
{
    m_pThisElm = NULL;
    if(!SUCCEEDED(m_pXMLDoc->get_documentElement(&m_pThisElm))) return false;

    return true;
}

//--------------------------------------------------------------------------------------
// Almacena en 'm_pSubElm' el elemento hijo indicado del elemento actual y fichero XML actual
//--------------------------------------------------------------------------------------
bool CXMLIO::SubElement(LPWSTR Element, bool create, long num)
{
    CComPtr<IXMLDOMNodeList> NodeList = NULL;
    CComPtr<IXMLDOMNode> Node = NULL;
    DOMNodeType type;
    CComBSTR name;
    long i, len;

    // Tomamos la lista de elementos hijos al actual
    m_pThisElm->get_childNodes(&NodeList);

    // Buscamos el elemento indicado
    NodeList->get_length(&len);
    for(i=0 ; i < len ; i++)
    {
        Node = NULL;
        NodeList->get_item(i,&Node);
        Node->get_nodeType(&type);
        if(Node && type==NODE_ELEMENT)
        {
            Node->get_nodeName(&name);
            if((lstrcmpW(name, Element) == 0) && (num-- == 0))
            {
                CComQIPtr<IXMLDOMElement> e(Node);
                m_pSubElm = e;
                return true;
            }
        }
    }

    // Si no existe el elemento, lo creamos si así se indicó
    if(create)
    {
        m_pSubElm = NULL;
        m_pXMLDoc->createElement(Element, &m_pSubElm);

        CComPtr<IXMLDOMNode> Node(m_pSubElm);
        CComPtr<IXMLDOMNode> outNode = NULL;

        m_pThisElm->appendChild(Node, &outNode);
        if(!outNode) return false;

        CComQIPtr<IXMLDOMElement> e(outNode);
        m_pSubElm = e;
        return true;
    }

    return false;
}
