/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 *  Import and export general routing data using a XML file.
 */
#include "OverrideLog.h"
#include "RouteDataSet.h"
#include "libxml/xmlmemory.h"
#include <errno.h>
#include <sys/stat.h>

extern char bcm_nfc_location[];


/*******************************************************************************
**
** Function:        AidBuffer
**
** Description:     Parse a string of hex numbers.  Store result in an array of
**                  bytes.
**                  aid: string of hex numbers.
**
** Returns:         None.
**
*******************************************************************************/
AidBuffer::AidBuffer (std::string& aid)
:   mBuffer (NULL),
    mBufferLen (0)
{
    unsigned int num = 0;
    const char delimiter = ':';
    std::string::size_type pos1 = 0;
    std::string::size_type pos2 = aid.find_first_of (delimiter);

    //parse the AID string; each hex number is separated by a colon;
    mBuffer = new UINT8 [aid.length()];
    while (true)
    {
        num = 0;
        if (pos2 == std::string::npos)
        {
            sscanf (aid.substr(pos1).c_str(), "%x", &num);
            mBuffer [mBufferLen] = (UINT8) num;
            mBufferLen++;
            break;
        }
        else
        {
            sscanf (aid.substr(pos1, pos2-pos1+1).c_str(), "%x", &num);
            mBuffer [mBufferLen] = (UINT8) num;
            mBufferLen++;
            pos1 = pos2 + 1;
            pos2 = aid.find_first_of (delimiter, pos1);
        }
    }
}


/*******************************************************************************
**
** Function:        ~AidBuffer
**
** Description:     Release all resources.
**
** Returns:         None.
**
*******************************************************************************/
AidBuffer::~AidBuffer ()
{
    delete [] mBuffer;
}


/*******************************************************************************/
/*******************************************************************************/


const char* RouteDataSet::sConfigFile = "/param/route.xml";


/*******************************************************************************
**
** Function:        ~RouteDataSet
**
** Description:     Release all resources.
**
** Returns:         None.
**
*******************************************************************************/
RouteDataSet::~RouteDataSet ()
{
    deleteDatabase ();
}


/*******************************************************************************
**
** Function:        initialize
**
** Description:     Initialize resources.
**
** Returns:         True if ok.
**
*******************************************************************************/
bool RouteDataSet::initialize ()
{
    static const char fn [] = "RouteDataSet::initialize";
    ALOGD ("%s: enter", fn);
    //check that the libxml2 version in use is compatible
    //with the version the software has been compiled with
    LIBXML_TEST_VERSION
    ALOGD ("%s: exit; return=true", fn);
    return true;
}


/*******************************************************************************
**
** Function:        deleteDatabase
**
** Description:     Delete all routes stored in all databases.
**
** Returns:         None.
**
*******************************************************************************/
void RouteDataSet::deleteDatabase ()
{
    static const char fn [] = "RouteDataSet::deleteDatabase";
    ALOGD ("%s: default db size=%zu; sec elem db size=%zu", fn, mDefaultRouteDatabase.size(), mSecElemRouteDatabase.size());
    Database::iterator it;

    for (it = mDefaultRouteDatabase.begin(); it != mDefaultRouteDatabase.end(); it++)
        delete (*it);
    mDefaultRouteDatabase.clear ();

    for (it = mSecElemRouteDatabase.begin(); it != mSecElemRouteDatabase.end(); it++)
        delete (*it);
    mSecElemRouteDatabase.clear ();
}


/*******************************************************************************
**
** Function:        import
**
** Description:     Import data from an XML file.  Fill the databases.
**
** Returns:         True if ok.
**
*******************************************************************************/
bool RouteDataSet::import ()
{
    static const char fn [] = "RouteDataSet::import";
    ALOGD ("%s: enter", fn);
    bool retval = false;
    xmlDocPtr doc;
    xmlNodePtr node1;
    std::string strFilename(bcm_nfc_location);
    strFilename += sConfigFile;

    deleteDatabase ();

    doc = xmlParseFile (strFilename.c_str());
    if (doc == NULL)
    {
        ALOGD ("%s: fail parse", fn);
        goto TheEnd;
    }

    node1 = xmlDocGetRootElement (doc);
    if (node1 == NULL)
    {
        ALOGE ("%s: fail root element", fn);
        goto TheEnd;
    }
    ALOGD ("%s: root=%s", fn, node1->name);

    node1 = node1->xmlChildrenNode;
    while (node1) //loop through all elements in <Routes ...
    {
        if (xmlStrcmp(node1->name, (const xmlChar*) "Route")==0)
        {
            xmlChar* value = xmlGetProp (node1, (const xmlChar*) "Type");
            if (value && (xmlStrcmp (value, (const xmlChar*) "SecElemSelectedRoutes") == 0))
            {
                ALOGD ("%s: found SecElemSelectedRoutes", fn);
                xmlNodePtr node2 = node1->xmlChildrenNode;
                while (node2) //loop all elements in <Route Type="SecElemSelectedRoutes" ...
                {
                    if (xmlStrcmp(node2->name, (const xmlChar*) "Proto")==0)
                        importProtocolRoute (node2, mSecElemRouteDatabase);
                    else if (xmlStrcmp(node2->name, (const xmlChar*) "Tech")==0)
                        importTechnologyRoute (node2, mSecElemRouteDatabase);
                    node2 = node2->next;
                } //loop all elements in <Route Type="SecElemSelectedRoutes" ...
            }
            else if (value && (xmlStrcmp (value, (const xmlChar*) "DefaultRoutes") == 0))
            {
                ALOGD ("%s: found DefaultRoutes", fn);
                xmlNodePtr node2 = node1->xmlChildrenNode;
                while (node2) //loop all elements in <Route Type="DefaultRoutes" ...
                {
                    if (xmlStrcmp(node2->name, (const xmlChar*) "Proto")==0)
                        importProtocolRoute (node2, mDefaultRouteDatabase);
                    else if (xmlStrcmp(node2->name, (const xmlChar*) "Tech")==0)
                        importTechnologyRoute (node2, mDefaultRouteDatabase);
                    node2 = node2->next;
                } //loop all elements in <Route Type="DefaultRoutes" ...
            }
            if (value)
                xmlFree (value);
        } //check <Route ...
        node1 = node1->next;
    } //loop through all elements in <Routes ...
    retval = true;

TheEnd:
    xmlFreeDoc (doc);
    xmlCleanupParser ();
    ALOGD ("%s: exit; return=%u", fn, retval);
    return retval;
}


/*******************************************************************************
**
** Function:        saveToFile
**
** Description:     Save XML data from a string into a file.
**                  routesXml: XML that represents routes.
**
** Returns:         True if ok.
**
*******************************************************************************/
bool RouteDataSet::saveToFile (const char* routesXml)
{
    static const char fn [] = "RouteDataSet::saveToFile";
    FILE* fh = NULL;
    size_t actualWritten = 0;
    bool retval = false;
    std::string filename (bcm_nfc_location);
    int stat = 0;

    filename.append (sConfigFile);
    fh = fopen (filename.c_str (), "w");
    if (fh == NULL)
    {
        ALOGE ("%s: fail to open file", fn);
        return false;
    }

    actualWritten = fwrite (routesXml, sizeof(char), strlen(routesXml), fh);
    retval = actualWritten == strlen(routesXml);
    fclose (fh);
    ALOGD ("%s: wrote %zu bytes", fn, actualWritten);
    if (retval == false)
        ALOGE ("%s: error during write", fn);

    //set file permission to
    //owner read, write; group read; other read
    stat = chmod (filename.c_str (), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    if (stat == -1)
        ALOGE ("%s: error during chmod", fn);
    return retval;
}


/*******************************************************************************
**
** Function:        loadFromFile
**
** Description:     Load XML data from file into a string.
**                  routesXml: string to receive XML data.
**
** Returns:         True if ok.
**
*******************************************************************************/
bool RouteDataSet::loadFromFile (std::string& routesXml)
{
    static const char fn [] = "RouteDataSet::loadFromFile";
    FILE* fh = NULL;
    size_t actual = 0;
    char buffer [1024];
    std::string filename (bcm_nfc_location);

    filename.append (sConfigFile);
    fh = fopen (filename.c_str (), "r");
    if (fh == NULL)
    {
        ALOGD ("%s: fail to open file", fn);
        return false;
    }

    while (true)
    {
        actual = fread (buffer, sizeof(char), sizeof(buffer), fh);
        if (actual == 0)
            break;
        routesXml.append (buffer, actual);
    }
    fclose (fh);
    ALOGD ("%s: read %zu bytes", fn, routesXml.length());
    return true;
}




/*******************************************************************************
**
** Function:        importProtocolRoute
**
** Description:     Parse data for protocol routes.
**                  element: XML node for one protocol route.
**                  database: store data in this database.
**
** Returns:         None.
**
*******************************************************************************/
void RouteDataSet::importProtocolRoute (xmlNodePtr& element, Database& database)
{
    static const char fn [] = "RouteDataSet::importProtocolRoute";
    const xmlChar* id = (const xmlChar*) "Id";
    const xmlChar* secElem = (const xmlChar*) "SecElem";
    const xmlChar* trueString = (const xmlChar*) "true";
    const xmlChar* switchOn = (const xmlChar*) "SwitchOn";
    const xmlChar* switchOff = (const xmlChar*) "SwitchOff";
    const xmlChar* batteryOff = (const xmlChar*) "BatteryOff";
    RouteDataForProtocol* data = new RouteDataForProtocol;
    xmlChar* value = NULL;

    ALOGD_IF (sDebug, "%s: element=%s", fn, element->name);
    value = xmlGetProp (element, id);
    if (value)
    {
        if (xmlStrcmp (value, (const xmlChar*) "T1T") == 0)
            data->mProtocol = NFA_PROTOCOL_MASK_T1T;
        else if (xmlStrcmp (value, (const xmlChar*) "T2T") == 0)
            data->mProtocol = NFA_PROTOCOL_MASK_T2T;
        else if (xmlStrcmp (value, (const xmlChar*) "T3T") == 0)
            data->mProtocol = NFA_PROTOCOL_MASK_T3T;
        else if (xmlStrcmp (value, (const xmlChar*) "IsoDep") == 0)
            data->mProtocol = NFA_PROTOCOL_MASK_ISO_DEP;
        xmlFree (value);
        ALOGD_IF (sDebug, "%s: %s=0x%X", fn, id, data->mProtocol);
    }

    value = xmlGetProp (element, secElem);
    if (value)
    {
        data->mNfaEeHandle = strtol ((char*) value, NULL, 16);
        xmlFree (value);
        data->mNfaEeHandle = data->mNfaEeHandle | NFA_HANDLE_GROUP_EE;
        ALOGD_IF (sDebug, "%s: %s=0x%X", fn, secElem, data->mNfaEeHandle);
    }

    value = xmlGetProp (element, switchOn);
    if (value)
    {
        data->mSwitchOn = (xmlStrcmp (value, trueString) == 0);
        xmlFree (value);
    }

    value = xmlGetProp (element, switchOff);
    if (value)
    {
        data->mSwitchOff = (xmlStrcmp (value, trueString) == 0);
        xmlFree (value);
    }

    value = xmlGetProp (element, batteryOff);
    if (value)
    {
        data->mBatteryOff = (xmlStrcmp (value, trueString) == 0);
        xmlFree (value);
    }
    database.push_back (data);
}


/*******************************************************************************
**
** Function:        importTechnologyRoute
**
** Description:     Parse data for technology routes.
**                  element: XML node for one technology route.
**                  database: store data in this database.
**
** Returns:         None.
**
*******************************************************************************/
void RouteDataSet::importTechnologyRoute (xmlNodePtr& element, Database& database)
{
    static const char fn [] = "RouteDataSet::importTechnologyRoute";
    const xmlChar* id = (const xmlChar*) "Id";
    const xmlChar* secElem = (const xmlChar*) "SecElem";
    const xmlChar* trueString = (const xmlChar*) "true";
    const xmlChar* switchOn = (const xmlChar*) "SwitchOn";
    const xmlChar* switchOff = (const xmlChar*) "SwitchOff";
    const xmlChar* batteryOff = (const xmlChar*) "BatteryOff";
    RouteDataForTechnology* data = new RouteDataForTechnology;
    xmlChar* value = NULL;

    ALOGD_IF (sDebug, "%s: element=%s", fn, element->name);
    value = xmlGetProp (element, id);
    if (value)
    {
        if (xmlStrcmp (value, (const xmlChar*) "NfcA") == 0)
            data->mTechnology = NFA_TECHNOLOGY_MASK_A;
        else if (xmlStrcmp (value, (const xmlChar*) "NfcB") == 0)
            data->mTechnology = NFA_TECHNOLOGY_MASK_B;
        else if (xmlStrcmp (value, (const xmlChar*) "NfcF") == 0)
            data->mTechnology = NFA_TECHNOLOGY_MASK_F;
        xmlFree (value);
        ALOGD_IF (sDebug, "%s: %s=0x%X", fn, id, data->mTechnology);
    }

    value = xmlGetProp (element, secElem);
    if (value)
    {
        data->mNfaEeHandle = strtol ((char*) value, NULL, 16);
        xmlFree (value);
        data->mNfaEeHandle = data->mNfaEeHandle | NFA_HANDLE_GROUP_EE;
        ALOGD_IF (sDebug, "%s: %s=0x%X", fn, secElem, data->mNfaEeHandle);
    }

    value = xmlGetProp (element, switchOn);
    if (value)
    {
        data->mSwitchOn = (xmlStrcmp (value, trueString) == 0);
        xmlFree (value);
    }

    value = xmlGetProp (element, switchOff);
    if (value)
    {
        data->mSwitchOff = (xmlStrcmp (value, trueString) == 0);
        xmlFree (value);
    }

    value = xmlGetProp (element, batteryOff);
    if (value)
    {
        data->mBatteryOff = (xmlStrcmp (value, trueString) == 0);
        xmlFree (value);
    }
    database.push_back (data);
}


/*******************************************************************************
**
** Function:        deleteFile
**
** Description:     Delete route data XML file.
**
** Returns:         True if ok.
**
*******************************************************************************/
bool RouteDataSet::deleteFile ()
{
    static const char fn [] = "RouteDataSet::deleteFile";
    std::string filename (bcm_nfc_location);
    filename.append (sConfigFile);
    int stat = remove (filename.c_str());
    ALOGD ("%s: exit %u", fn, stat==0);
    return stat == 0;
}


/*******************************************************************************
**
** Function:        getDatabase
**
** Description:     Obtain a database of routing data.
**                  selection: which database.
**
** Returns:         Pointer to database.
**
*******************************************************************************/
RouteDataSet::Database* RouteDataSet::getDatabase (DatabaseSelection selection)
{
    switch (selection)
    {
    case DefaultRouteDatabase:
        return &mDefaultRouteDatabase;
    case SecElemRouteDatabase:
        return &mSecElemRouteDatabase;
    }
    return NULL;
}


/*******************************************************************************
**
** Function:        printDiagnostic
**
** Description:     Print some diagnostic output.
**
** Returns:         None.
**
*******************************************************************************/
void RouteDataSet::printDiagnostic ()
{
    static const char fn [] = "RouteDataSet::printDiagnostic";
    Database* db = getDatabase (DefaultRouteDatabase);

    ALOGD ("%s: default route database", fn);
    for (Database::iterator iter = db->begin(); iter != db->end(); iter++)
    {
        RouteData* routeData = *iter;
        switch (routeData->mRouteType)
        {
        case RouteData::ProtocolRoute:
            {
                RouteDataForProtocol* proto = (RouteDataForProtocol*) routeData;
                ALOGD ("%s: ee h=0x%X; protocol=0x%X", fn, proto->mNfaEeHandle, proto->mProtocol);
            }
            break;
        case RouteData::TechnologyRoute:
            {
                RouteDataForTechnology* tech = (RouteDataForTechnology*) routeData;
                ALOGD ("%s: ee h=0x%X; technology=0x%X", fn, tech->mNfaEeHandle, tech->mTechnology);
            }
            break;
        }
    }

    ALOGD ("%s: sec elem route database", fn);
    db = getDatabase (SecElemRouteDatabase);
    for (Database::iterator iter2 = db->begin(); iter2 != db->end(); iter2++)
    {
        RouteData* routeData = *iter2;
        switch (routeData->mRouteType)
        {
        case RouteData::ProtocolRoute:
            {
                RouteDataForProtocol* proto = (RouteDataForProtocol*) routeData;
                ALOGD ("%s: ee h=0x%X; protocol=0x%X", fn, proto->mNfaEeHandle, proto->mProtocol);
            }
            break;
        case RouteData::TechnologyRoute:
            {
                RouteDataForTechnology* tech = (RouteDataForTechnology*) routeData;
                ALOGD ("%s: ee h=0x%X; technology=0x%X", fn, tech->mNfaEeHandle, tech->mTechnology);
            }
            break;
        }
    }
}