// Copyright (C) 2006-2014 David Sugar, Tycho Softworks.
// Copyright (C) 2015-2020 Cherokees of Idaho.
//
// This file is part of GNU uCommon C++.
//
// GNU uCommon C++ is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU uCommon C++ 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with GNU uCommon C++.  If not, see <http://www.gnu.org/licenses/>.

/**
 * Parsing of config files that have keyword/value pairs.  This includes
 * supporting classes to extract basic config data from files that are stored
 * as []'s, and uses several supporting classes.
 * @file ucommon/keydata.h
 */

/**
 * Some exercise of keydata routines.
 * @example keydata.cpp
 */

#ifndef _UCOMMON_KEYDATA_H_
#define _UCOMMON_KEYDATA_H_

#ifndef _UCOMMON_CONFIG_H_
#include <ucommon/platform.h>
#endif

#ifndef  _UCOMMON_LINKED_H_
#include <ucommon/linked.h>
#endif

#ifndef  _UCOMMON_MEMORY_H_
#include <ucommon/memory.h>
#endif

namespace ucommon {

class keyfile;

/**
 * Data keys parsed from a keyfile.  This is a specific [] section from a
 * fully loaded keyfile, and offers common means to access data members.
 * This is related to the original GNU Common C++ keydata object, although
 * it is formed in a keyfile class which is loaded from a config file all
 * at once.
 * @author David Sugar <dyfet@gnutelephony.org>
 */
class __EXPORT keydata : public OrderedObject
{
private:
    friend class keyfile;

    OrderedIndex index;
    const char *name;
    keyfile *root;

    keydata(keyfile *file);
    keydata(keyfile *file, const char *id);
    __DELETE_COPY(keydata);

public:
    /**
     * A key value set is used for iterative access.  Otherwise this class
     * is normally not used as we usually request the keys directly.
     * @author David Sugar <dyfet@gnutelephony.org>
     */
    class __LOCAL keyvalue : public OrderedObject
    {
    private:
        friend class keydata;
        friend class keyfile;
        keyvalue(keyfile *allocator, keydata *section, const char *key, const char *data);
        __DELETE_COPY(keyvalue);

    public:
        const char *id;
        const char *value;
    };

    friend class keyvalue;

    /**
     * Lookup a key value by it's id.
     * @param id to look for.
     * @return value string or NULL if not found.
     */
    const char *get(const char *id) const;

    /**
     * Lookup a key value by it's id.
     * @param id to look for.
     * @return value string or NULL if not found.
     */
    inline const char *operator()(const char *id) const {
        return get(id);
    }

    /**
     * Set a keyword and value in the keydata structure.  If the keyword
     * already exists, it is replaced.  Removed items still use pager
     * allocated memory.
     * @param id to set.
     * @param value for the id.
     */
    void set(const char *id, const char *value);

    /**
     * Remove a keyword id from the keydata structure.  Removed items
     * still use pager allocated memory.
     * @param id to remove.
     */
    void clear(const char *id);

    /**
     * Get the name of this section.  Useful in iterative examinations.
     * @return name of keydata section.
     */
    inline const char *get(void) const {
        return name;
    }

    /**
     * Get first value object, for iterative examinations.
     * @return first key value in chain.
     */
    inline keyvalue *begin(void) const {
        return (keyvalue *)index.begin();
    }

    /**
     * Get last value object, for iterative examinations.
     * @return first key value in chain.
     */
    inline keyvalue *end(void) const {
        return (keyvalue*)index.end();
    }

    /**
     * Convenience typedef for iterative pointer.
     */
    typedef linked_pointer<keyvalue> pointer;
};

/**
 * Traditional keypair config file parsing class.  This is used to get
 * generic config data either from a /etc/xxx.conf, a windows style
 * xxx.ini file, or a ~/.xxxrc file, and parses [] sections from the
 * entire file at once.
 */
class __EXPORT keyfile : public memalloc
{
private:
    friend class keydata;
    OrderedIndex index;
    keydata *defaults;
    int errcode;

protected:
    keydata *create(const char *section);

#ifdef  _MSWINDOWS_
    void load(HKEY root, keydata *section = NULL, const char *path = NULL);
    bool save(HKEY root, keydata *section = NULL, const char *path = NULL);
#endif

public:
    /**
     * Create an empty key file ready for loading.
     * @param pagesize for memory paging.
     */
    keyfile(size_t pagesize = 0);

    /**
     * Create a key file object from an existing config file.
     * @param path to load from.
     * @param pagesize for memory paging.
     */
    keyfile(const char *path, size_t pagesize = 0);

    keyfile(const keyfile &copy, size_t pagesize = 0);

    /**
     * Load (overlay) another config file over the currently loaded one.
     * This is used to merge key data, such as getting default values from
     * a global config, and then overlaying a local home config file.
     * @param path to load keys from into current object.
     */
    void load(const char *path);

    /**
     * Save (write) a set of config keys to dist.
     * @param path of file to save keys to.
     * @return true on success.
     */
    bool save(const char *path);

    /**
     * Load from an existing keyfile object.
     * @param source to copy from.
     */
    void load(const keyfile *source);

    /**
     * Load a single set of keys.
     * @param source of keys to copy.
     */
    void load(const keydata *source);

    /**
     * Release and re-initialize keyfile.
     */
    void release(void);

    /**
     * Get a keydata section name.
     * @param section name to look for.
     * @return keydata section object if found, NULL if not.
     */
    keydata *get(const char *section) const;

    inline keydata *operator()(const char *section) const {
        return get(section);
    }

    inline keydata *operator[](const char *section) const {
        return get(section);
    }

    /**
     * Get the non-sectioned defaults if there are any.
     * @return default key section.
     */
    inline keydata *get(void) const {
        return defaults;
    }

    /**
     * Get first keydata object, for iterative examinations.
     * @return first key value in chain.
     */
    inline keydata *begin(void) const {
        return (keydata *)index.begin();
    }

    /**
     * Get last keydata object, for iterative examinations.
     * @return first key value in chain.
     */
    inline keydata *end(void) const {
        return (keydata *)index.end();
    }

    /**
     * Convenience typedef for iterative pointer.
     */
    typedef linked_pointer<keydata> pointer;

    inline int err(void) const {
        return errcode;
    }

    /**
     * Assign foreign pager to us.  This relocates the heap references
     * to our object, clears the other object.
     */
    void assign(keyfile& source);

    inline keyfile& operator=(keyfile& source) {
        assign(source);
        return *this;
    }
};

} // namespace ucommon

#endif
