/* Copyright (C) 2014 InfiniDB, Inc.

   This program 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; version 2 of
   the License.

   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.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
   MA 02110-1301, USA. */

/*******************************************************************************
* $Id$
*
*******************************************************************************/

/*
 * we_xmlgetter.cpp
 *
 *  Created on: Feb 7, 2012
 *      Author: bpaul
 */


#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdexcept>
#include <iostream>

#include <string>
#include <vector>
using namespace std;

#include "we_xmlgetter.h"

using namespace std;

namespace WriteEngine
{

//------------------------------------------------------------------------------
// WEXmlgetter constructor
//------------------------------------------------------------------------------
WEXmlgetter::WEXmlgetter(std::string& ConfigName):
    fConfigName(ConfigName),
    fDoc( NULL ),
    fpRoot( NULL )
{
    //  xmlNodePtr curPtr;
    fDoc = xmlParseFile( ConfigName.c_str() );

    if (fDoc == NULL )
        throw runtime_error("WEXmlgetter::getConfig(): no XML document!");

    fpRoot = xmlDocGetRootElement( fDoc );

    if ( fpRoot == NULL )
    {
        xmlFreeDoc( fDoc );
        fDoc = NULL;
        throw runtime_error("WEXmlgetter::getConfig(): no XML Root Tag!");
    }
}

//------------------------------------------------------------------------------
// WEXmlgetter destructor
//------------------------------------------------------------------------------
WEXmlgetter::~WEXmlgetter()
{
    xmlFreeDoc( fDoc );
    fDoc = NULL;
}

//------------------------------------------------------------------------------
// Get/return the property or attribute value (strVal) for the specified xml tag
// (pNode) and property/attribute (pTag)
//------------------------------------------------------------------------------
bool WEXmlgetter::getNodeAttribute(const xmlNode* pNode,
                                   const char* pTag, std::string& strVal) const
{
    xmlChar* pTmp = NULL;
    bool     bFound = false;

    pTmp = xmlGetProp( const_cast<xmlNode*>(pNode), (xmlChar*) pTag );

    if ( pTmp )
    {
        bFound = true;
        strVal = (char*)pTmp;
        xmlFree( pTmp );
    }
    else
    {
        strVal.clear();
    } // end if

    return bFound;
}

//------------------------------------------------------------------------------
// Get/return the node content (strVal) for the specified xml tag (pNode)
//------------------------------------------------------------------------------
bool WEXmlgetter::getNodeContent( const xmlNode* pNode,
                                  std::string& strVal) const
{
    xmlChar* pTmp = NULL;
    bool     bFound = false;

    if ( pNode->children != NULL )
    {
        pTmp = xmlNodeGetContent( pNode->children );

        if ( pTmp )
        {
            bFound = true;
            strVal = (char*)pTmp;
            xmlFree( pTmp );
        }
        else
        {
            strVal.clear();
        }
    }
    else
    {
        strVal.clear();
    }

    return bFound;
}

//------------------------------------------------------------------------------
// Get/returns node content for the "first" child node under each section/name.
// Example:
//   <section>
//     <name>
//       <subname1>
//       </subname1>
//     </name>
//     <name>
//       <subname1>
//       </subname1>
//     </name>
//   </section>
//
// Looks like xml2 is currently returning the text node as the first child
// node under a node.  So in the example above, this function is currently
// always returning the text node content inside each <name> rather than
// any <subname1> node that might be within each <name> tag.
//------------------------------------------------------------------------------
void WEXmlgetter::getConfig(const string& section,
                            const string& name, vector<string>& values) const
{
    string res;

    if (section.length() == 0)
        throw invalid_argument("Config::getConfig: section must have a length");

    xmlNode* pPtr = fpRoot->xmlChildrenNode;

    while (pPtr != NULL)
    {
        //cout << "pPtr->name:    " <<
        //	(const xmlChar*)pPtr->name << std::endl;

        if ((!xmlStrcmp(pPtr->name, (const xmlChar*)section.c_str())))
        {
            xmlNodePtr pPtr2 = pPtr->xmlChildrenNode;

            while (pPtr2 != NULL)
            {
                //cout << "  pPtr2->name: " <<
                //	(const xmlChar*)pPtr2->name << std::endl;

                if ((!xmlStrcmp(pPtr2->name, (const xmlChar*)name.c_str())))
                {
                    xmlNodePtr pPtr3 = pPtr2->xmlChildrenNode;
                    values.push_back((const char*)pPtr3->content);

                    //cout << "    pPtr3->name: " <<
                    //	(const xmlChar*)pPtr3->name <<
                    //	"; content: " << (const xmlChar*)pPtr3->content <<
                    //	"; len: " << strlen((char*)pPtr3->content) << std::endl;
                }

                pPtr2 = pPtr2->next;
            }
        }

        pPtr = pPtr->next;
    }
}

//------------------------------------------------------------------------------
// Returns node content for the last node in the node tree defined by
// "sections".  So if sections[] were:
//   sections[0] = "house"
//   sections[1] = "room"
// Then this function would return the node content for the first <room>
// tag found under the first <house> tag.
// Function assumes that the desired node has no children nodes other than
// the text content node.
//------------------------------------------------------------------------------
std::string WEXmlgetter::getValue(const vector<string>& sections) const
{
    std::string aRet;
    const xmlNode* pPtr = fpRoot;
    int aSize = sections.size();
    int aIdx = 0;

    //cout << aSize << endl;
    while (aIdx < aSize)
    {
        //cout << aIdx <<" "<< sections[aIdx] << endl;
        pPtr = getNode(pPtr, sections[aIdx]);

        if ((pPtr == NULL) || (aIdx == aSize - 1)) break;
        else
        {
            //cout << "getValue Name " << (const char*)pPtr->name << endl;
            pPtr = pPtr->xmlChildrenNode;
            aIdx++;
        }
    }

    if (pPtr != NULL)
    {
        //aRet = (const char*)pPtr->content;
        std::string aBuff;

        if (getNodeContent(pPtr, aBuff)) aRet = aBuff;
    }

    return aRet;
}

//------------------------------------------------------------------------------
// Iterate through the sibling nodes starting with pParent, looking for
// a node with the specified name (section).  The xmlNode (if found) is
// returned.
//------------------------------------------------------------------------------
const xmlNode* WEXmlgetter::getNode(const xmlNode* pParent,
                                    const string& section)const
{
    if (pParent == NULL) return NULL;

    const xmlNode* pPtr = pParent;

    while (pPtr != NULL )
    {
        //cout << "getNode Name " << (const char*)pPtr->name << endl;
        if (!xmlStrcmp(pPtr->name, (const xmlChar*)section.c_str()))
            return pPtr;
        else
            pPtr = pPtr->next;
    }

    return pPtr;
}

//------------------------------------------------------------------------------
// Iterate down through the node tree represented by the sections vector.
// In the last child of this tree, we look for the specified attribute tag,
// and return its value.
//------------------------------------------------------------------------------
std::string WEXmlgetter::getAttribute(const vector<string>& sections,
                                      const string& Tag) const
{
    std::string aRet;
    const xmlNode* pPtr = fpRoot;
    int aSize = sections.size();

    if (aSize == 0)
        throw invalid_argument("WEXmlgetter::getAttribute(): section must be valid");

    int aIdx = 0;

    //cout << aSize << endl;
    while (aIdx < aSize)
    {
        //cout << aIdx <<" "<< sections[aIdx] << endl;
        pPtr = getNode(pPtr, sections[aIdx]);

        if ((pPtr == NULL) || (aIdx == aSize - 1)) break;
        else
        {
            //cout << "getValue Name " << (const char*)pPtr->name << endl;
            pPtr = pPtr->xmlChildrenNode;
            aIdx++;
        }
    }

    if (pPtr != NULL)
    {
        std::string aBuff;

        //cout << "attrTagNode Name " << (const char*)pPtr->name << endl;
        if (getNodeAttribute(pPtr, Tag.c_str(), aBuff))
            aRet = aBuff;

        //aRet = (const char*)pPtr->content;
        //cout << "Attribute("<<Tag<<") = "<< aRet<< endl;
    }

    return aRet;
}

//------------------------------------------------------------------------------
// Iterate down through the node tree represented by the sections vector.
// At the end of the branch, there may be several sibling nodes matching
// the node search vector.
// For each of the matching children nodes found, we look for the specified
// attribute tag, and return its value.  Hence a vector of attribute values
// is returned.
//------------------------------------------------------------------------------
void WEXmlgetter::getAttributeListForAllChildren(
    const vector<string>& sections,
    const string& attributeTag,
    vector<string>& attributeValues)
{
    const xmlNode* pPtr = fpRoot;
    int aSize = sections.size();

    if (aSize == 0)
    {
        throw invalid_argument("WEXmlgetter::getAttributeListForAllChildren():"
                               " No XML nodes specified in section search list");
    }

    // Step down the branch that has the nodes of interest
    int aIdx = 0;

    while (aIdx < aSize)
    {
        pPtr = getNode(pPtr, sections[aIdx]);

        if ((pPtr == NULL) || (aIdx == aSize - 1))
        {
            break;
        }
        else
        {
            pPtr = pPtr->xmlChildrenNode;
            aIdx++;
        }
    }

    // Look for all the "matching" nodes at the end of the branch, and
    // get the requested attribute value for each matching node.
    if (pPtr != NULL)
    {
        while (pPtr != NULL )
        {
            std::string attrib;

            if (getNodeAttribute(pPtr, attributeTag.c_str(), attrib))
            {
                attributeValues.push_back(attrib);
            }

            pPtr = pPtr->next;
        }
    }
}

} /* namespace WriteEngine */