//+============================================================================
//
// file :               anyoption.cpp
//
// description :        C++ source code for the AnyOption
//						class. This class is used to manage the command line
//						line arguments. This code comes from the
//						http://www.hackorama.com/anyoption.
//
// project :            TANGO
//
// author(s) :          Kishan Thomas (E.Taurel)
//
// Copyright (C) :      2004,2005,2006,2007,2008,2009,2010
//						European Synchrotron Radiation Facility
//                      BP 220, Grenoble 38043
//                      FRANCE
//
// This code (and the associated include file) comes from the following
// web page: http://www.hackorama.com/anyoption/
//
// It is available there without any licensing informations.
//
// This file is part of Tango.
//
// Tango 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, either version 3 of the License, or
// (at your option) any later version.
//
// Tango 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 Tango.  If not, see <http://www.gnu.org/licenses/>.
//
//-============================================================================

#include "anyoption.h"

using namespace std;

AnyOption::AnyOption()
{
    init();
}

AnyOption::AnyOption(int maxopt)
{
    init(maxopt, maxopt);
}

AnyOption::AnyOption(int maxopt, int maxcharopt)
{
    init(maxopt, maxcharopt);
}

AnyOption::~AnyOption()
{
    if(mem_allocated)
    {
        cleanup();
    }
}

void AnyOption::init()
{
    init(DEFAULT_MAXOPTS, DEFAULT_MAXOPTS);
}

void AnyOption::init(int maxopt, int maxcharopt)
{
    max_options = maxopt;
    max_char_options = maxcharopt;
    max_usage_lines = DEFAULT_MAXUSAGE;
    usage_lines = 0;
    argc = 0;
    argv = nullptr;
    posix_style = true;
    verbose = false;
    filename = nullptr;
    appname = nullptr;
    option_counter = 0;
    optchar_counter = 0;
    new_argv = nullptr;
    new_argc = 0;
    max_legal_args = 0;
    command_set = false;
    file_set = false;
    values = nullptr;
    g_value_counter = 0;
    mem_allocated = false;
    command_set = false;
    file_set = false;
    opt_prefix_char = '-';
    file_delimiter_char = ':';
    file_comment_char = '#';
    equalsign = '=';
    comment = '#';
    delimiter = ':';
    endofline = '\n';
    whitespace = ' ';
    nullterminate = '\0';
    set = false;
    once = true;
    hasoptions = false;
    autousage = false;

    ::strcpy(long_opt_prefix, "--");

    if(alloc() == false)
    {
        cout << endl << "OPTIONS ERROR : Failed allocating memory";
        cout << endl;
        cout << "Exiting." << endl;
        exit(0);
    }
}

bool AnyOption::alloc()
{
    int i = 0;
    int size = 0;

    if(mem_allocated)
    {
        return true;
    }

    size = (max_options + 1) * sizeof(const char *);
    options = (const char **) malloc(size);
    optiontype = (int *) malloc((max_options + 1) * sizeof(int));
    optionindex = (int *) malloc((max_options + 1) * sizeof(int));
    if(options == nullptr || optiontype == nullptr || optionindex == nullptr)
    {
        return false;
    }
    else
    {
        mem_allocated = true;
    }
    for(i = 0; i < max_options; i++)
    {
        options[i] = nullptr;
        optiontype[i] = 0;
        optionindex[i] = -1;
    }
    optionchars = (char *) malloc((max_char_options + 1) * sizeof(char));
    optchartype = (int *) malloc((max_char_options + 1) * sizeof(int));
    optcharindex = (int *) malloc((max_char_options + 1) * sizeof(int));
    if(optionchars == nullptr || optchartype == nullptr || optcharindex == nullptr)
    {
        mem_allocated = false;
        return false;
    }
    for(i = 0; i < max_char_options; i++)
    {
        optionchars[i] = '0';
        optchartype[i] = 0;
        optcharindex[i] = -1;
    }

    size = (max_usage_lines + 1) * sizeof(const char *);
    usage = (const char **) malloc(size);

    if(usage == nullptr)
    {
        mem_allocated = false;
        return false;
    }
    for(i = 0; i < max_usage_lines; i++)
    {
        usage[i] = nullptr;
    }

    return true;
}

bool AnyOption::doubleOptStorage()
{
    const char **tmp_ptr;
    int *tmp_ptr_int;
    tmp_ptr = options;
    options = (const char **) realloc(tmp_ptr, ((2 * max_options) + 1) * sizeof(const char *));
    if(options == nullptr)
    {
        return false;
    }
    tmp_ptr_int = optiontype;
    optiontype = (int *) realloc(tmp_ptr_int, ((2 * max_options) + 1) * sizeof(int));
    if(optiontype == nullptr)
    {
        return false;
    }
    tmp_ptr_int = optionindex;
    optionindex = (int *) realloc(tmp_ptr_int, ((2 * max_options) + 1) * sizeof(int));
    if(optionindex == nullptr)
    {
        return false;
    }
    /* init new storage */
    for(int i = max_options; i < 2 * max_options; i++)
    {
        options[i] = nullptr;
        optiontype[i] = 0;
        optionindex[i] = -1;
    }
    max_options = 2 * max_options;
    return true;
}

bool AnyOption::doubleCharStorage()
{
    char *tmp_ptr;
    int *tmp_ptr_int;
    tmp_ptr = optionchars;
    optionchars = (char *) realloc(tmp_ptr, ((2 * max_char_options) + 1) * sizeof(char));
    if(optionchars == nullptr)
    {
        return false;
    }
    tmp_ptr_int = optchartype;
    optchartype = (int *) realloc(tmp_ptr_int, ((2 * max_char_options) + 1) * sizeof(int));
    if(optchartype == nullptr)
    {
        return false;
    }
    tmp_ptr_int = optcharindex;
    optcharindex = (int *) realloc(tmp_ptr_int, ((2 * max_char_options) + 1) * sizeof(int));
    if(optcharindex == nullptr)
    {
        return false;
    }
    /* init new storage */
    for(int i = max_char_options; i < 2 * max_char_options; i++)
    {
        optionchars[i] = '0';
        optchartype[i] = 0;
        optcharindex[i] = -1;
    }
    max_char_options = 2 * max_char_options;
    return true;
}

bool AnyOption::doubleUsageStorage()
{
    const char **new_usage = (const char **) realloc(usage, ((2 * max_usage_lines) + 1) * sizeof(const char *));
    if(new_usage == nullptr)
    {
        free(usage);
        return false;
    }
    usage = new_usage;
    for(int i = max_usage_lines; i < 2 * max_usage_lines; i++)
    {
        usage[i] = nullptr;
    }
    max_usage_lines = 2 * max_usage_lines;
    return true;
}

void AnyOption::cleanup()
{
    if(options != nullptr)
    {
        free(options);
    }
    if(optiontype != nullptr)
    {
        free(optiontype);
    }
    if(optionindex != nullptr)
    {
        free(optionindex);
    }
    if(optionchars != nullptr)
    {
        free(optionchars);
    }
    if(optchartype != nullptr)
    {
        free(optchartype);
    }
    if(optcharindex != nullptr)
    {
        free(optcharindex);
    }
    if(usage != nullptr)
    {
        free(usage);
    }
    if(values != nullptr)
    {
        free(values);
    }
    if(new_argv != nullptr)
    {
        free(new_argv);
    }
}

void AnyOption::setCommandPrefixChar(char _prefix)
{
    opt_prefix_char = _prefix;
}

void AnyOption::setCommandLongPrefix(char *_prefix)
{
    if(strlen(_prefix) > MAX_LONG_PREFIX_LENGTH)
    {
        *(_prefix + MAX_LONG_PREFIX_LENGTH) = '\0';
    }

    strcpy(long_opt_prefix, _prefix);
}

void AnyOption::setFileCommentChar(char _comment)
{
    file_delimiter_char = _comment;
}

void AnyOption::setFileDelimiterChar(char _delimiter)
{
    file_comment_char = _delimiter;
}

bool AnyOption::CommandSet()
{
    return (command_set);
}

bool AnyOption::FileSet()
{
    return (file_set);
}

void AnyOption::noPOSIX()
{
    posix_style = false;
}

bool AnyOption::POSIX()
{
    return posix_style;
}

void AnyOption::setVerbose()
{
    verbose = true;
}

void AnyOption::printVerbose()
{
    if(verbose)
    {
        cout << endl;
    }
}

void AnyOption::printVerbose(const char *msg)
{
    if(verbose)
    {
        cout << msg;
    }
}

void AnyOption::printVerbose(char *msg)
{
    if(verbose)
    {
        cout << msg;
    }
}

void AnyOption::printVerbose(char ch)
{
    if(verbose)
    {
        cout << ch;
    }
}

bool AnyOption::hasOptions()
{
    return hasoptions;
}

void AnyOption::autoUsagePrint(bool _autousage)
{
    autousage = _autousage;
}

void AnyOption::useCommandArgs(int _argc, char **_argv)
{
    argc = _argc;
    argv = _argv;
    command_set = true;
    appname = argv[0];
    if(argc > 1)
    {
        hasoptions = true;
    }
}

void AnyOption::useFiileName(const char *_filename)
{
    filename = _filename;
    file_set = true;
}

/*
 * set methods for options
 */

void AnyOption::setCommandOption(const char *opt)
{
    addOption(opt, COMMAND_OPT);
    g_value_counter++;
}

void AnyOption::setCommandOption(char opt)
{
    addOption(opt, COMMAND_OPT);
    g_value_counter++;
}

void AnyOption::setCommandOption(const char *opt, char optchar)
{
    addOption(opt, COMMAND_OPT);
    addOption(optchar, COMMAND_OPT);
    g_value_counter++;
}

void AnyOption::setCommandFlag(const char *opt)
{
    addOption(opt, COMMAND_FLAG);
    g_value_counter++;
}

void AnyOption::setCommandFlag(char opt)
{
    addOption(opt, COMMAND_FLAG);
    g_value_counter++;
}

void AnyOption::setCommandFlag(const char *opt, char optchar)
{
    addOption(opt, COMMAND_FLAG);
    addOption(optchar, COMMAND_FLAG);
    g_value_counter++;
}

void AnyOption::setFileOption(const char *opt)
{
    addOption(opt, FILE_OPT);
    g_value_counter++;
}

void AnyOption::setFileOption(char opt)
{
    addOption(opt, FILE_OPT);
    g_value_counter++;
}

void AnyOption::setFileOption(const char *opt, char optchar)
{
    addOption(opt, FILE_OPT);
    addOption(optchar, FILE_OPT);
    g_value_counter++;
}

void AnyOption::setFileFlag(const char *opt)
{
    addOption(opt, FILE_FLAG);
    g_value_counter++;
}

void AnyOption::setFileFlag(char opt)
{
    addOption(opt, FILE_FLAG);
    g_value_counter++;
}

void AnyOption::setFileFlag(const char *opt, char optchar)
{
    addOption(opt, FILE_FLAG);
    addOption(optchar, FILE_FLAG);
    g_value_counter++;
}

void AnyOption::setOption(const char *opt)
{
    addOption(opt, COMMON_OPT);
    g_value_counter++;
}

void AnyOption::setOption(char opt)
{
    addOption(opt, COMMON_OPT);
    g_value_counter++;
}

void AnyOption::setOption(const char *opt, char optchar)
{
    addOption(opt, COMMON_OPT);
    addOption(optchar, COMMON_OPT);
    g_value_counter++;
}

void AnyOption::setFlag(const char *opt)
{
    addOption(opt, COMMON_FLAG);
    g_value_counter++;
}

void AnyOption::setFlag(const char opt)
{
    addOption(opt, COMMON_FLAG);
    g_value_counter++;
}

void AnyOption::setFlag(const char *opt, char optchar)
{
    addOption(opt, COMMON_FLAG);
    addOption(optchar, COMMON_FLAG);
    g_value_counter++;
}

void AnyOption::addOption(const char *opt, int type)
{
    if(option_counter >= max_options)
    {
        if(doubleOptStorage() == false)
        {
            addOptionError(opt);
            return;
        }
    }
    options[option_counter] = opt;
    optiontype[option_counter] = type;
    optionindex[option_counter] = g_value_counter;
    option_counter++;
}

void AnyOption::addOption(char opt, int type)
{
    if(!POSIX())
    {
        printVerbose("Ignoring the option character \"");
        printVerbose(opt);
        printVerbose("\" ( POSIX options are turned off )");
        printVerbose();
        return;
    }

    if(optchar_counter >= max_char_options)
    {
        if(doubleCharStorage() == false)
        {
            addOptionError(opt);
            return;
        }
    }
    optionchars[optchar_counter] = opt;
    optchartype[optchar_counter] = type;
    optcharindex[optchar_counter] = g_value_counter;
    optchar_counter++;
}

void AnyOption::addOptionError(const char *opt)
{
    cout << endl;
    cout << "OPTIONS ERROR : Failed allocating extra memory " << endl;
    cout << "While adding the option : \"" << opt << "\"" << endl;
    cout << "Exiting." << endl;
    cout << endl;
    exit(0);
}

void AnyOption::addOptionError(char opt)
{
    cout << endl;
    cout << "OPTIONS ERROR : Failed allocating extra memory " << endl;
    cout << "While adding the option: \"" << opt << "\"" << endl;
    cout << "Exiting." << endl;
    cout << endl;
    exit(0);
}

void AnyOption::processOptions()
{
    if(!valueStoreOK())
    {
        return;
    }
}

void AnyOption::processCommandArgs(int max_args)
{
    max_legal_args = max_args;
    processCommandArgs();
}

void AnyOption::processCommandArgs(int _argc, char **_argv, int max_args)
{
    max_legal_args = max_args;
    processCommandArgs(_argc, _argv);
}

void AnyOption::processCommandArgs(int _argc, char **_argv)
{
    useCommandArgs(_argc, _argv);
    processCommandArgs();
}

void AnyOption::processCommandArgs()
{
    if(!(valueStoreOK() && CommandSet()))
    {
        return;
    }

    if(max_legal_args == 0)
    {
        max_legal_args = argc;
    }
    new_argv = (int *) malloc((max_legal_args + 1) * sizeof(int));
    for(int i = 1; i < argc; i++)
    { /* ignore first argv */
        if(argv[i][0] == long_opt_prefix[0] && argv[i][1] == long_opt_prefix[1])
        {                                         /* long GNU option */
            int match_at = parseGNU(argv[i] + 2); /* skip -- */
            if(match_at >= 0 && i < argc - 1)     /* found match */
            {
                setValue(options[match_at], argv[++i]);
            }
        }
        else if(argv[i][0] == opt_prefix_char)
        { /* POSIX char */
            if(POSIX())
            {
                char ch = parsePOSIX(argv[i] + 1); /* skip - */
                if(ch != '0' && i < argc - 1)      /* matching char */
                {
                    setValue(ch, argv[++i]);
                }
                else if(new_argc < max_legal_args)
                { // negative numbers
                    new_argv[new_argc] = i;
                    new_argc++;
                }
            }
            else
            {                                         /* treat it as GNU option with a - */
                int match_at = parseGNU(argv[i] + 1); /* skip - */
                if(match_at >= 0 && i < argc - 1)     /* found match */
                {
                    setValue(options[match_at], argv[++i]);
                }
                else if(new_argc < max_legal_args)
                { // negative numbers
                    new_argv[new_argc] = i;
                    new_argc++;
                }
            }
        }
        else
        { /* not option but an argument keep index */
            if(new_argc < max_legal_args)
            {
                new_argv[new_argc] = i;
                new_argc++;
            }
            else
            { /* ignore extra arguments */
                printVerbose("Ignoring extra argument: ");
                printVerbose(argv[i]);
                printVerbose();
                printAutoUsage();
            }
            printVerbose("Unknown command argument option : ");
            printVerbose(argv[i]);
            printVerbose();
            printAutoUsage();
        }
    }
}

char AnyOption::parsePOSIX(char *arg)
{
    for(unsigned int i = 0; i < strlen(arg); i++)
    {
        char ch = arg[i];
        if(matchChar(ch))
        { /* keep matching flags till an option */
            /*if last char argv[++i] is the value */
            if(i == strlen(arg) - 1)
            {
                return ch;
            }
            else
            {        /* else the rest of arg is the value */
                i++; /* skip any '=' and ' ' */
                while(arg[i] == whitespace || arg[i] == equalsign)
                {
                    i++;
                }
                setValue(ch, arg + i);
                return '0';
            }
        }
    }
    printVerbose("Unknown command argument option : ");
    printVerbose(arg);
    printVerbose();
    printAutoUsage();
    return '0';
}

int AnyOption::parseGNU(char *arg)
{
    int split_at = 0;
    /* if has a '=' sign get value */
    for(unsigned int i = 0; i < strlen(arg); i++)
    {
        if(arg[i] == equalsign)
        {
            split_at = i;    /* store index */
            i = strlen(arg); /* get out of loop */
        }
    }
    if(split_at > 0)
    { /* it is an option value pair */
        char *tmp = (char *) malloc((split_at + 1) * sizeof(char));
        for(int i = 0; i < split_at; i++)
        {
            tmp[i] = arg[i];
        }
        tmp[split_at] = '\0';

        if(matchOpt(tmp) >= 0)
        {
            setValue(options[matchOpt(tmp)], arg + split_at + 1);
            free(tmp);
        }
        else
        {
            printVerbose("Unknown command argument option : ");
            printVerbose(arg);
            printVerbose();
            printAutoUsage();
            free(tmp);
            return -1;
        }
    }
    else
    { /* regular options with no '=' sign  */
        return matchOpt(arg);
    }
    return -1;
}

int AnyOption::matchOpt(char *opt)
{
    for(int i = 0; i < option_counter; i++)
    {
        if(strcmp(options[i], opt) == 0)
        {
            if(optiontype[i] == COMMON_OPT || optiontype[i] == COMMAND_OPT)
            { /* found option return index */
                return i;
            }
            else if(optiontype[i] == COMMON_FLAG || optiontype[i] == COMMAND_FLAG)
            { /* found flag, set it */
                setFlagOn(opt);
                return -1;
            }
        }
    }
    printVerbose("Unknown command argument option : ");
    printVerbose(opt);
    printVerbose();
    printAutoUsage();
    return -1;
}

bool AnyOption::matchChar(char c)
{
    for(int i = 0; i < optchar_counter; i++)
    {
        if(optionchars[i] == c)
        { /* found match */
            if(optchartype[i] == COMMON_OPT || optchartype[i] == COMMAND_OPT)
            { /* an option store and stop scanning */
                return true;
            }
            else if(optchartype[i] == COMMON_FLAG || optchartype[i] == COMMAND_FLAG)
            { /* a flag store and keep scanning */
                setFlagOn(c);
                return false;
            }
        }
    }
    printVerbose("Unknown command argument option : ");
    printVerbose(c);
    printVerbose();
    printAutoUsage();
    return false;
}

bool AnyOption::valueStoreOK()
{
    if(!set)
    {
        if(g_value_counter > 0)
        {
            int size = 0;
            size = g_value_counter * sizeof(char *);
            values = (char **) malloc(size);
            for(int i = 0; i < g_value_counter; i++)
            {
                values[i] = nullptr;
            }
            set = true;
        }
    }
    return set;
}

/*
 * public get methods
 */
char *AnyOption::getValue(const char *option)
{
    if(!valueStoreOK())
    {
        return nullptr;
    }

    for(int i = 0; i < option_counter; i++)
    {
        if(strcmp(options[i], option) == 0)
        {
            return values[optionindex[i]];
        }
    }
    return nullptr;
}

bool AnyOption::getFlag(const char *option)
{
    if(!valueStoreOK())
    {
        return false;
    }
    for(int i = 0; i < option_counter; i++)
    {
        if(strcmp(options[i], option) == 0)
        {
            return findFlag(values[optionindex[i]]);
        }
    }
    return false;
}

char *AnyOption::getValue(char option)
{
    if(!valueStoreOK())
    {
        return nullptr;
    }
    for(int i = 0; i < optchar_counter; i++)
    {
        if(optionchars[i] == option)
        {
            return values[optcharindex[i]];
        }
    }
    return nullptr;
}

bool AnyOption::getFlag(char option)
{
    if(!valueStoreOK())
    {
        return false;
    }
    for(int i = 0; i < optchar_counter; i++)
    {
        if(optionchars[i] == option)
        {
            return findFlag(values[optcharindex[i]]);
        }
    }
    return false;
}

bool AnyOption::findFlag(char *val)
{
    if(val == nullptr)
    {
        return false;
    }

    if(strcmp(TRUE_FLAG, val) == 0)
    {
        return true;
    }

    return false;
}

/*
 * private set methods
 */
bool AnyOption::setValue(const char *option, char *value)
{
    if(!valueStoreOK())
    {
        return false;
    }
    for(int i = 0; i < option_counter; i++)
    {
        if(strcmp(options[i], option) == 0)
        {
            values[optionindex[i]] = (char *) malloc((strlen(value) + 1) * sizeof(char));
            strcpy(values[optionindex[i]], value);
            return true;
        }
    }
    return false;
}

bool AnyOption::setFlagOn(const char *option)
{
    if(!valueStoreOK())
    {
        return false;
    }
    for(int i = 0; i < option_counter; i++)
    {
        if(strcmp(options[i], option) == 0)
        {
            values[optionindex[i]] = (char *) malloc((strlen(TRUE_FLAG) + 1) * sizeof(char));
            strcpy(values[optionindex[i]], TRUE_FLAG);
            return true;
        }
    }
    return false;
}

bool AnyOption::setValue(char option, char *value)
{
    if(!valueStoreOK())
    {
        return false;
    }
    for(int i = 0; i < optchar_counter; i++)
    {
        if(optionchars[i] == option)
        {
            values[optcharindex[i]] = (char *) malloc((strlen(value) + 1) * sizeof(char));
            strcpy(values[optcharindex[i]], value);
            return true;
        }
    }
    return false;
}

bool AnyOption::setFlagOn(char option)
{
    if(!valueStoreOK())
    {
        return false;
    }
    for(int i = 0; i < optchar_counter; i++)
    {
        if(optionchars[i] == option)
        {
            values[optcharindex[i]] = (char *) malloc((strlen(TRUE_FLAG) + 1) * sizeof(char));
            strcpy(values[optcharindex[i]], TRUE_FLAG);
            return true;
        }
    }
    return false;
}

int AnyOption::getArgc()
{
    return new_argc;
}

char *AnyOption::getArgv(int index)
{
    if(index < new_argc)
    {
        return (argv[new_argv[index]]);
    }
    return nullptr;
}

/* dotfile sub routines */

bool AnyOption::processFile()
{
    if(!(valueStoreOK() && FileSet()))
    {
        return false;
    }
    return (consumeFile(readFile()));
}

bool AnyOption::processFile(const char *filename)
{
    useFiileName(filename);
    return (processFile());
}

char *AnyOption::readFile()
{
    return (readFile(filename));
}

/*
 * read the file contents to a character buffer
 */

char *AnyOption::readFile(const char *fname)
{
    int length;
    char *buffer;
    ifstream is;
    is.open(fname, ifstream::in);
    if(!is.good())
    {
        is.close();
        return nullptr;
    }
    is.seekg(0, ios::end);
    length = is.tellg();
    is.seekg(0, ios::beg);
    buffer = (char *) malloc(length * sizeof(char));
    is.read(buffer, length);
    is.close();
    return buffer;
}

/*
 * scans a char* buffer for lines that does not
 * start with the specified comment character.
 */
bool AnyOption::consumeFile(char *buffer)
{
    if(buffer == nullptr)
    {
        return false;
    }

    char *cursor = buffer; /* preserve the ptr */
    char *pline = nullptr;
    int linelength = 0;
    bool newline = true;
    for(unsigned int i = 0; i < strlen(buffer); i++)
    {
        if(*cursor == endofline)
        {                        /* end of line */
            if(pline != nullptr) /* valid line */
            {
                processLine(pline, linelength);
                pline = nullptr;
                newline = true;
            }
        }
        else if(newline)
        { /* start of line */
            newline = false;
            if((*cursor != comment))
            { /* not a comment */
                pline = cursor;
                linelength = 0;
            }
        }
        cursor++; /* keep moving */
        linelength++;
    }
    free(buffer);
    return true;
}

/*
 *  find a valid type value pair separated by a delimiter
 *  character and pass it to valuePairs()
 *  any line which is not valid will be considered a value
 *  and will get passed on to justValue()
 *
 *  assuming delimiter is ':' the behaviour will be,
 *
 *  width:10    - valid pair valuePairs( width, 10 );
 *  width : 10  - valid pair valuepairs( width, 10 );
 *
 *  ::::        - not valid
 *  width       - not valid
 *  :10         - not valid
 *  width:      - not valid
 *  ::          - not valid
 *  :           - not valid
 *
 */

void AnyOption::processLine(char *theline, int length)
{
    bool found = false;
    char *pline = (char *) malloc((length + 1) * sizeof(char));
    for(int i = 0; i < length; i++)
    {
        pline[i] = *(theline++);
    }
    pline[length] = nullterminate;
    char *cursor = pline; /* preserve the ptr */
    if(*cursor == delimiter || *(cursor + length - 1) == delimiter)
    {
        justValue(pline); /* line with start/end delimiter */
    }
    else
    {
        for(int i = 1; i < length - 1 && !found; i++)
        { /* delimiter */
            if(*cursor == delimiter)
            {
                *(cursor - 1) = nullterminate; /* two strings */
                found = true;
                valuePairs(pline, cursor + 1);
            }
            cursor++;
        }
        cursor++;
        if(!found) /* not a pair */
        {
            justValue(pline);
        }
    }
    free(pline);
}

/*
 * removes trailing and preceeding whitespaces from a string
 */
char *AnyOption::chomp(char *str)
{
    while(*str == whitespace)
    {
        str++;
    }
    char *end = str + strlen(str) - 1;
    while(*end == whitespace)
    {
        end--;
    }
    *(end + 1) = nullterminate;
    return str;
}

void AnyOption::valuePairs(char *type, char *value)
{
    if(strlen(chomp(type)) == 1)
    { /* this is a char option */
        for(int i = 0; i < optchar_counter; i++)
        {
            if(optionchars[i] == type[0])
            { /* match */
                if(optchartype[i] == COMMON_OPT || optchartype[i] == FILE_OPT)
                {
                    setValue(type[0], chomp(value));
                    return;
                }
            }
        }
    }
    /* if no char options matched */
    for(int i = 0; i < option_counter; i++)
    {
        if(strcmp(options[i], type) == 0)
        { /* match */
            if(optiontype[i] == COMMON_OPT || optiontype[i] == FILE_OPT)
            {
                setValue(type, chomp(value));
                return;
            }
        }
    }
    printVerbose("Unknown option in resourcefile : ");
    printVerbose(type);
    printVerbose();
}

void AnyOption::justValue(char *type)
{
    if(strlen(chomp(type)) == 1)
    { /* this is a char option */
        for(int i = 0; i < optchar_counter; i++)
        {
            if(optionchars[i] == type[0])
            { /* match */
                if(optchartype[i] == COMMON_FLAG || optchartype[i] == FILE_FLAG)
                {
                    setFlagOn(type[0]);
                    return;
                }
            }
        }
    }
    /* if no char options matched */
    for(int i = 0; i < option_counter; i++)
    {
        if(strcmp(options[i], type) == 0)
        { /* match */
            if(optiontype[i] == COMMON_FLAG || optiontype[i] == FILE_FLAG)
            {
                setFlagOn(type);
                return;
            }
        }
    }
    printVerbose("Unknown option in resourcefile : ");
    printVerbose(type);
    printVerbose();
}

/*
 * usage and help
 */

void AnyOption::printAutoUsage()
{
    if(autousage)
    {
        printUsage();
    }
}

void AnyOption::printUsage()
{
    if(once)
    {
        once = false;
        cout << endl;
        for(int i = 0; i < usage_lines; i++)
        {
            cout << usage[i] << endl;
        }
        cout << endl;
    }
}

void AnyOption::addUsage(const char *line)
{
    if(usage_lines >= max_usage_lines)
    {
        if(doubleUsageStorage() == false)
        {
            addUsageError(line);
            exit(1);
        }
    }
    usage[usage_lines] = line;
    usage_lines++;
}

void AnyOption::addUsageError(const char *line)
{
    cout << endl;
    cout << "OPTIONS ERROR : Failed allocating extra memory " << endl;
    cout << "While adding the usage/help  : \"" << line << "\"" << endl;
    cout << "Exiting." << endl;
    cout << endl;
    exit(0);
}
