
//***************************************************
// Generic Configuration handler class
//
// Copyright (C) 2008 Heiko Nitzsche
//
//   This software is provided 'as-is', without any express or implied
//   warranty.  In no event will the author be held liable for any damages
//   arising from the use of this software.
//
//   Permission is granted to anyone to use this software for any purpose,
//   including commercial applications, and to alter it and redistribute it
//   freely, subject to the following restrictions:
//
//   1. The origin of this software must not be misrepresented; you must not
//      claim that you wrote the original software. If you use this software
//      in a product, an acknowledgment in the product documentation would be
//      appreciated but is not required.
//   2. Altered source versions must be plainly marked as such, and must not be
//      misrepresented as being the original software.
//   3. This notice may not be removed or altered from any source distribution.
//
//***************************************************

#ifdef __IBMCPP__
  #pragma strings( readonly )
#endif

#include "ConfigHandler.hpp"
#include <string.h>
#include <stdio.h>
#include <assert.h>

// ---------------------------------------------------------
// ---------------------------------------------------------

ConfigHandler::ConfigHandler()
  : mKeys(NULL),
    mNumKeys(0)
{
}

// ---------------------------------------------------------

ConfigHandler::~ConfigHandler()
{
    clear();
}

// ---------------------------------------------------------

void ConfigHandler::clear()
{
    delete [] mKeys;
    mKeys = NULL;
    mNumKeys = 0;
}

// ---------------------------------------------------------

BOOL ConfigHandler::append(const char * keyname, const char * value)
{
    int i;

    // check if the key already exists
    for (i = 0; i < mNumKeys; i++)
    {
        const Key & key(mKeys[i]);
        if (strcmp(keyname, key.getKeyname()) == 0)
        {
            // update the entry
            mKeys[i] = Key(keyname, value);
            return TRUE;
        }
    }

    // increase the key array
    Key * newKeys = new Key[mNumKeys + 1];
    if (newKeys == NULL)
    {
        return FALSE;
    }
    // copy the old elements to the new array
    for (i = 0; i < mNumKeys; i++)
    {
        newKeys[i] = mKeys[i];
        assert((newKeys[i].getKeyname() != NULL) &&
               (newKeys[i].getValue()   != NULL));
    }
    // add the new key
    newKeys[mNumKeys] = Key(keyname, value);
    assert((newKeys[mNumKeys].getKeyname() != NULL) &&
           (newKeys[mNumKeys].getValue()   != NULL));

    delete [] mKeys;
    mKeys = newKeys;

    mNumKeys++;

    return TRUE;
}

// ---------------------------------------------------------

BOOL ConfigHandler::readFromFile(const char * filename)
{
    clear();

    if (filename == NULL)
    {
        return FALSE;
    }

    // read an ASCII file line by line, ignore everything behind # character
    FILE * file = fopen(filename, "r");
    if (file != NULL)
    {
        char readBuffer[101]  = { 0 };
        char keyBuffer[101]   = { 0 };
        char valueBuffer[101] = { 0 };

        // get the next line
        while (fgets(readBuffer, sizeof(readBuffer), file) != NULL)
        {
            // skip line if it starts with a # (coment)
            char * pComment = readBuffer;
            while (*pComment == ' ')
            {
                pComment++;
            }
            switch(*pComment)
            {
                case 0:
                case '\n':
                case '#':
                    continue; // read next line

                default:
                    // all other characters are handled below
                    break;
            }

            // split into key and value
            const int ret = sscanf(readBuffer,
                                   "%100[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]%*[=]%100[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]",
                                   keyBuffer, valueBuffer);
            // key/value found?
            if (ret == 2)
            {
                if (! this->append(keyBuffer, valueBuffer))
                {
                    fclose(file);
                    clear();
                    return FALSE;
                }
            }
        }
        fclose(file);
    }
    return TRUE;
}

// ---------------------------------------------------------

ConfigHandler::KeyIterator ConfigHandler::getIterator() const
{
    return ConfigHandler::KeyIterator(*this);
}

// ---------------------------------------------------------

const char * ConfigHandler::getValueOfKey(const char * keyname) const
{
    if (keyname != NULL)
    {
        // lookup key
        for (int i = 0; i < mNumKeys; i++)
        {
            const Key & key(mKeys[i]);
            if (strcmp(keyname, key.getKeyname()) == 0)
            {
                return mKeys[i].getValue();
            }
        }
    }
    return NULL;
}

// ---------------------------------------------------------

int ConfigHandler::getNumberOfKeys() const
{
    return mNumKeys;
}

// ---------------------------------------------------------
// ---------------------------------------------------------

ConfigHandler::KeyIterator::KeyIterator(const ConfigHandler & reader)
  : mIndex(0),
    mReader(reader)
{
}

// ---------------------------------------------------------

ConfigHandler::KeyIterator::~KeyIterator()
{
}

// ---------------------------------------------------------

BOOL ConfigHandler::KeyIterator::hasMore() const
{
    return (mIndex < mReader.mNumKeys) ? TRUE : FALSE;
}

// ---------------------------------------------------------

const ConfigHandler::Key & ConfigHandler::KeyIterator::next()
{
    assert(hasMore());
    const Key & key(mReader.mKeys[mIndex]);
    mIndex++;
    return key;
}


