// XMLDataStore.cpp: implementation of the WMXMLDataStore class.
//
//////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2002 Robert Cunnings. All rights reserved.
//
// You may copy, modify, distribute or publish this code free of charge.
//
// NO WARRANTY: This software is provided AS IS, without warranty of any kind.
//
//////////////////////////////////////////////////////////////////////
//
// WMXMLDocument class - implementation of simple XML storage container
// Tree based on nested "container" elements holding "item" elements
// which may have any content. Searchable using XPath like syntax.
//  
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "xmldatastore.h"
#include "xmlparser.h"

static wchar_t *g_pwszDataStoreNS = L"http://whitemesa.com/xmldatastore/";

WMXMLDataStore::WMXMLDataStore(void)
{
    m_pCurrent = NULL;
    m_bIsContainer = false;
    m_bIsItem = false;
    m_bElemFound = false;
    m_mapNS.insert(map<wstring, wstring>::value_type(wstring(L"ns"), wstring(g_pwszDataStoreNS)));
}

WMXMLDataStore::~WMXMLDataStore(void)
{
}

void WMXMLDataStore::ParsePath(const wstring& wstrPath)
{
    while (m_stackPath.empty() == false)
    {
        m_stackPath.pop();
    }
    wstring wstrTemp;
    if (wstrPath.empty() == false)
    {
        if (wstrPath[0] == wchar_t('/'))
        {
            wstrTemp = wstrPath.substr(1);
        }
        else
        {
            wstrTemp = wstrPath;
        }
        wstring::size_type pos;
        while ((pos = wstrTemp.rfind('/')) != wstring::npos)
        {
            m_stackPath.push(wstrTemp.substr(pos + 1));
            wstrTemp = wstrTemp.substr(0, pos);
        }
        if (wstrTemp.empty() == false)
        {
            m_stackPath.push(wstrTemp);
        }
    }
}

bool WMXMLDataStore::Seek(const wstring& wstrPath)
{
    m_bIsContainer = false;
    m_bIsItem = false;
    m_bElemFound = false;

    if ((m_pCurrent = GetRootElem()) == NULL)
    {
        return false;
    }

    ParsePath(wstrPath);

    // navigate through data tree
    while (m_stackPath.empty() == false)
    {
        bool bContinue = false;
        WMXMLElement *pValue = NULL;
        wstring wstrName = m_stackPath.top();
        m_stackPath.pop();
        if (m_stackPath.empty() == true)
        {
            // look for leaf elements
            if (SelectElems(m_pCurrent, wstring(L"ns:item"), m_mapNS) == true)
            {
                while ((pValue = NextElem()) != NULL)
                {
                    wstring wstrTemp;
                    if (pValue->GetAttribute(wstring(L""), wstring(L"name"), wstrTemp) == false)
                    {
                        continue;
                    }
                    if (wstrTemp == wstrName)
                    {
                        m_pCurrent = pValue;
                        m_bIsItem = true;
                        m_bElemFound = true;
                        break;
                    }
                }
            }
        }
        // look for containers
        if (m_bElemFound == false && SelectElems(m_pCurrent, wstring(L"ns:container"), m_mapNS) == true)
        {
            while ((pValue = NextElem()) != NULL)
            {
                wstring wstrTemp;
                if (pValue->GetAttribute(wstring(L""), wstring(L"name"), wstrTemp) == false)
                {
                    continue;
                }
                if (wstrTemp == wstrName)
                {
                    m_pCurrent = pValue;
                    if (m_stackPath.empty() == true)
                    {
                        m_bIsContainer = true;
                        m_bElemFound = true;
                        break;
                    }
                    bContinue = true;
                }
            }
        }
        // keep going?
        if (bContinue == false)
        {
            break;
        }
    }
    return m_bElemFound;
}

WMXMLElement *WMXMLDataStore::CreateContainerElem(const wstring& wstrName)
{
	WMXMLElement *pElem = new WMXMLElement( wstring(L"ds:container"),
                                            wstring(g_pwszDataStoreNS),
                                            wstring(L"container"));
    if (pElem != NULL)
    {
        pElem->SetAttribute(wstring(L"name"), wstring(L""), wstrName);
        m_pCurrent->AddChildElement(pElem);
    }
    return pElem;
}

WMXMLElement *WMXMLDataStore::CreateItemElem(const wstring& wstrName)
{
	WMXMLElement *pElem = new WMXMLElement( wstring(L"ds:item"),
                                            wstring(g_pwszDataStoreNS),
                                            wstring(L"item"));
    if (pElem != NULL)
    {
        pElem->SetAttribute(wstring(L"name"), wstring(L""), wstrName);
        m_pCurrent->AddChildElement(pElem);
    }
    return pElem;
}

bool WMXMLDataStore::GetElementName(WMXMLElement *pElem, wstring& wstrName)
{
    return pElem->GetAttribute(wstring(L""), wstring(L"name"), wstrName);
}

bool WMXMLDataStore::CreateContainer(const wstring& wstrPath)
{
    m_bIsContainer = false;
    m_bIsItem = false;
    m_bElemFound = false;

    if ((m_pCurrent = GetRootElem()) == NULL)
    {
        return false;
    }

    ParsePath(wstrPath);

    // navigate through data tree
    while (m_stackPath.empty() == false)
    {
        bool bFound = false;
        bool bContinue = false;
        WMXMLElement *pValue = NULL;
        wstring wstrName = m_stackPath.top();
        m_stackPath.pop();
        // look for container
        if (SelectElems(m_pCurrent, wstring(L"ns:container"), m_mapNS) == true)
        {
            while ((pValue = NextElem()) != NULL)
            {
                wstring wstrTemp;
                if (pValue->GetAttribute(wstring(L""), wstring(L"name"), wstrTemp) == false)
                {
                    continue;
                }
                if (wstrTemp == wstrName)
                {
                    // this path component exists
                    bFound = true;
                    m_pCurrent = pValue;
                    if (m_stackPath.empty() == true)
                    {
                        // done
                        m_bElemFound = true;
                        break;
                    }
                    bContinue = true;
                }
            }
        }
        if (bFound == false)
        {
            // path component doesn't exist so create
            WMXMLElement *pNew = CreateContainerElem(wstrName);
            if (pNew == NULL)
            {
                // bail out
                return false;
            }
            m_pCurrent = pNew;
            if (m_stackPath.empty() == false)
            {
                // not done yet
                bContinue = true;
            }
        }
        // keep going?
        if (bContinue == false)
        {
            break;
        }
    }
    m_bIsContainer = true;
    return true;
}
