// This file is part of necsim project which is released under MIT license.
// See file **LICENSE.txt** or visit https://opensource.org/licenses/MIT) for full license details.
//
/**
 * @author Sam Thompson
 * @file ConfigFileParser.h
 * @brief ConfigOption and SectionOption classes for importing command line parameters from a config text file,
 * originally designed for usage within coalescence simulations on a cluster.
 *
 * Contact: samuel.thompson14@imperial.ac.uk or thompsonsed@gmail.com
 * @copyright <a href="https://opensource.org/licenses/MIT"> MIT Licence.</a>
 *
 */

#ifndef CONFIGPARSER_H
#define CONFIGPARSER_H

/************************************************************
																																																INCLUDES
 ************************************************************/
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
#include <stdexcept>
#include <vector>
#include <cstring>

#ifndef WIN_INSTALL

#include <unistd.h>

#endif

#include <cmath>
#include <cctype>
#include <algorithm>

using std::string;
using std::vector;

namespace necsim
{
    /**
     * @brief Import the command line arguments in to the vector for future processing.
     * Arguments will be placed in the vector comargs.
     * @param argc the number of arguments.
     * @param argv a point to the array of arguments in raw character form.
     * @param comargs a vector of the command-line arguments to be filled.
     */
    void importArgs(const unsigned int &argc, char* argv[], vector<string> &comargs);

    //

    /**
     * @struct SectionOption
     * @brief A simple container for importing options from a config file.
     */
    struct SectionOption
    {
        string section;
        vector<string> val;
        vector<string> refs;

        /**
         * @brief Default constructor for SectionOption
         */
        SectionOption() : val(), refs()
        {
            section = "nullSectionOption";
        }

        /**
         * @brief Returns the value for the provided reference from within the key
         * @param refval the reference to obtain the value of
         * @return the requested value as a string. Returns string "null" if no reference is found.
         */
        string getOption(string refval);

        /**
         * @brief Overloading the << operator for outputting to the output stream.
         * @param os the output stream.
         * @param k the KeyOption object.
         * @return os the output stream.
         */
        friend std::ostream &operator<<(std::ostream &os, const SectionOption &k);

        /**
         * @brief Overloading the >> operator for inputting from an input stream.
         * @param is the input stream
         * @param k the KeyOption object
         * @return is the input stream
         */
        friend std::istream &operator>>(std::istream &is, SectionOption &k);
    };

    /**
     * @brief Stores and parses configuration options from the command line and config files.
     */
    class ConfigParser
    {
    private:
        string config_file;
        bool configSet;
        bool isMain;  // is true if this is the main command line import (and therefore we want to delete the first few
        // command line options)
        bool isFullParser;  // if this is true, each KeyOption structure will be returned after each read.
        vector<SectionOption> configs;  // all config data if full parse is true.
    public:
        /**
         * @brief default construtor for ConfigOption
         */
        ConfigParser() : configs()
        {
            config_file = "none";
            configSet = false;
            isMain = false;
            isFullParser = false;
        }

        /**
         * @brief Sets the config file the specified string.
         * A boolean is also provided, set equal to true if this is the main command line import.
         * This causes the deletion of the first few command line options after import.
         * @param file the target config file (in .txt format).
         * @param main boolean of if this is the main command line import.
         * @param full_parse sets bFullParse to provided value
         */
        void setConfig(const string &file, bool main, bool full_parse = false);

        /**
         * @brief Reads a config file of a specific configuration.
         * Each section begins with '[section_name]'.
         * Each variable is defined as 'key=value', and must be one per line
         * Each key's variable will be read as a string into a KeyOption structure.
         */
        void parseConfig();

        /**
         * @brief Reads a config stream of a specific configuration.
         * Each section begins with '[section_name]'.
         * Each variable is defined as 'key=value', and must be one per line
         * Each key's variable will be read as a string into a KeyOption structure.
         */
        void parseConfig(std::istream &istream1);

        /**
        * @brief Returns the vector of key options imported from the file.
        * @return vector of key options
        */
        vector<SectionOption> getSectionOptions();

        /**
         * @brief Sets the section option with the provided section, key and value.
         * @param section the section name
         * @param reference the reference key for the parameter
         * @param value the value of the key
         */
        void setSectionOption(string section, string reference, string value);

        /**
         * @brief Gets the SectionOption at the provided index
         * @param index the index of the SectionOption to obtain, must be less than configs.size()
         * @return the section option at the index
         */
        SectionOption operator[](unsigned long index);

        /**
             * @brief Gets the size of the key options vector.
             * @return the size of the configuration vector.
             */
        unsigned long getSectionOptionsSize();

        /**
         * @brief Gets the sections contained in the SectionOptions object.
         * @return A vector of the section names.
         */
        vector<string> getSections();

        /**
         * @brief Checks whether the config option has the specified section.
         * @param sec the section name to check for
         * @return true if the section has been found
         */
        bool hasSection(const string &sec);

        /**
         * @brief Gets all values within a section.
         *
         * Throws a Config_Exception if the section is not found.
         * @param sec the section to find values for
         * @return a vector of the section's values.
         */
        vector<string> getSectionValues(string sec);

        /**
         * @brief Returns a specific value for a particular key options and reference.
         * @param section the section to match
         * @param ref the reference to match
         * @return the string at the correct place in KeyOptions.val
         */
        string getSectionOptions(string section, string ref);

        /**
         * @brief Returns a specific value for a particular key options and reference.
         * This overloaded version of the function returns the default value def when
         * no match is found.
         * @param section the section to match
         * @param ref the reference to match
         * @param def the default value to return if no match is found
         * @return the string at the correct place in KeyOptions.val
         */
        string getSectionOptions(string section, string ref, string def);

        /**
         * @brief Imports the parameters from the config file and returns an integer of the number of arguments.
         * @param comargs a vector of command line arguments to import to from file.
         * @return a count of the number of arguments (should also be the size of comargs).
         */
        int importConfig(vector<string> &comargs);

        /**
         * @brief Overloading the << operator for outputting to the output stream.
         * @param os the output stream.
         * @param c the ConfigOption object.
         * @return os the output stream.
         */
        friend std::ostream &operator<<(std::ostream &os, const ConfigParser &c);

        /**
         * @brief Overloading the >> operator for inputting from an input stream.
         * Note that the config file must still exist for re-inport and parsing.
         * @param is the input stream
         * @param c the ConfigOption object
         * @return is the input stream
         */
        friend std::istream &operator>>(std::istream &is, ConfigParser &c);
    };
}
#endif // CONFIGPARSER_H
