#pragma once
#include "Arguments.h"
#include <string>
#include <vector>

namespace ArgumentParser {

class ArgParser {

public:

    ArgParser(const std::string& name)
      : parser_name_(name)
    {}
    ~ArgParser() {};

    bool Parse(std::vector<std::string> args);

    String& AddStringArgument(const std::string param);
    String& AddStringArgument(const char short_param, const std::string param);
    String& AddStringArgument(const char short_param, const std::string param, const std::string description);
    std::string GetStringValue(const std::string param);

    Int& AddIntArgument(const std::string number);
    Int& AddIntArgument(const std::string numer, const std::string description);
    int GetIntValue(const std::string param);
    Int& AddIntArgument(const char short_param, const std::string param);
    int GetIntValue(const std::string param, const int ind);

    Bool& AddFlag(const char short_flag, const std::string flag);
    Bool& AddFlag(const char short_flag, const std::string flag, const std::string description);
    Bool& AddFlag(const std::string flag, const std::string description);

    bool GetFlag(const std::string flag);

    Args& AddHelp(const char short_help, const std::string help, const std::string description);
    bool Help();
    std::string HelpDescription();

private:

    std::string parser_name_;
    std::vector<Args*> all_;
    
};

}
#include "ArgParser.h"
#include "Arguments.h"
#include <string>
#include <vector>
#include <iostream>

namespace ArgumentParser {

    bool ArgParser::Parse(std::vector<std::string> args) {
        if (all_.empty() && args.size() == 1) {
            return true;
        }
        int count = 0;
        for (int i = 0; i < args.size(); ++i) {
            for (int j = 0; j < all_.size(); ++j) {
                TypeFlag type = all_[j]->GetTypeFlag();
                switch (type) {
                    case TypeFlag::INT: {
                        Int* arg = static_cast<Int*>(all_[j]);

                        std::string origin_param = "--" + arg->GetParam() + "=";
                        std::string origin_short_param = "-";
                        origin_short_param.push_back(arg->GetShortParam());
                        origin_short_param += "=";

                        if (!arg->GetParam().empty() && args[i].substr(0, origin_param.length()) == origin_param && !all_[j]->GetFlagMultiValue()) {
                            arg->PutValue(args[i].substr(origin_param.length(), args[i].length()));
                            ++count;
                        } else if (arg->GetFlagShortParam() && args[i].substr(0, origin_short_param.length()) == origin_short_param && !all_[j]->GetFlagMultiValue()) {
                            arg->PutValue(args[i].substr(origin_short_param.length(), args[i].length()));
                            
                            ++count;
                        } else if (all_[j]->GetFlagMultiValue()) {
                            if (args[i].substr(0, origin_param.length()) == origin_param) {
                                arg->PutValue(args[i].substr(origin_param.length(), args[i].length()));
                                all_[j]->MultiArgsCount();
                            } else if (arg->GetFlagShortParam() && args[i].substr(0, origin_short_param.length()) == origin_short_param) {
                                arg->PutValue(args[i].substr(origin_short_param.length(), args[i].length()));
                                all_[j]->MultiArgsCount();
                            }
                        }
                        if (all_[j]->GetFlagPositional()) {
                            int count_len = 0;
                            for (int k = 0; k < args[i].length(); ++k) {
                                if (!std::isdigit(args[i][k])){
                                    break;
                                } else {
                                    ++count_len;
                                }
                            }
                            if (count_len == args[i].length()) {
                                arg->PutValue(args[i]);
                                arg->MultiArgsCount();
                                if (arg->GetFlagStoreValues()) {
                                    arg->AddStoreValues(arg->GetValue());
                                    all_[j]->AddAllValues(arg->GetValue());
                                }
                            }
                        } 
                    }
                    break;
                    case TypeFlag::STRING: {
                        String* arg = static_cast<String*>(all_[j]);
                    
                        std::string origin_param = "--" + arg->GetParam() + "=";
                        std::string origin_short_param = "-";
                        origin_short_param.push_back(arg->GetShortParam());
                        origin_short_param += "=";

                        if (args.size() == 1 && !arg->GetDefaultValue().empty()) {
                            arg->PutValue(arg->GetDefaultValue());
                            ++count;
                        } else if (!arg->GetParam().empty() && args[i].substr(0, origin_param.length()) == origin_param && !all_[j]->GetFlagMultiValue()) {
                            arg->PutValue(args[i].substr(origin_param.length(), args[i].length()));
                            if (arg->GetFlagStoreValue()) {
                                arg->AddStoreValue(arg->GetValue());
                            }
                            ++count;
                        } else if (arg->GetFlagShortParam() && args[i].substr(0, origin_short_param.length()) == origin_short_param && !all_[j]->GetFlagMultiValue()) {
                            arg->PutValue(args[i].substr(origin_short_param.length(), args[i].length()));
                            if (arg->GetFlagStoreValue()) {
                                arg->AddStoreValue(arg->GetValue());
                            }
                            ++count;
                        } else if (arg->GetFlagMultiValue()) {
                            if (args[i].substr(0, origin_param.length()) == origin_param) {
                                arg->PutValue(args[i].substr(origin_param.length(), args[i].length()));
                                arg->MultiArgsCount();
                            } else if (arg->GetFlagShortParam() && args[i].substr(0, origin_short_param.length()) == origin_short_param) {
                                arg->PutValue(args[i].substr(origin_short_param.length(), args[i].length()));
                                arg->MultiArgsCount();
                            }
                        }
                    }
                    break;
                    case TypeFlag::BOOL: {
                        Bool* arg = static_cast<Bool*>(all_[j]);
                        std::string origin_flag = "--" + arg->GetFlag();

                        if (arg->GetIsFlag()) {
                            if (args[i].substr(0, origin_flag.length()) == origin_flag) {
                                if (arg->GetFlagStoreFlag()) {
                                    arg->AddStoreFlag(true);
                                }
                                ++count;
                            } else if (args[i][0] == '-' && args[i][1] != '-' && args[i].length() > 2) {
                                for (int z = 1; z < args[i].length(); ++z) {
                                    if (args[i][z] == arg->GetShortFlag()) {
                                        if (arg->GetFlagStoreFlag()) {
                                            arg->AddStoreFlag(true);
                                        }
                                        ++count;
                                    }
                                }
                            }
                        }
                    }
                    break;
                    case TypeFlag::UNKNOWN: {
                        Args* arg = static_cast<Args*>(all_[j]);
                        std::string origin_help = "--" + all_[j]->GetHelp();
                        std::string origin_short_help = "-";
                        origin_short_help.push_back(all_[j]->GetShortHelp());
                        if (args[i].substr(0, origin_help.length()) == origin_help || args[i].substr(0, origin_short_help.length()) == origin_short_help) {
                            all_[j]->FlagHelp();
                            ++count;
                        }
                    }
                    break;
                }
            }
        }
        for (int i = 0; i < all_.size(); ++i) {
            if (all_[i]->GetMultiArgsCount() > 0) {
                ++count;
            }
            if (static_cast<Bool*>(all_[i])->GetFlagDefaultFlag()) {
                ++count;
            }
            if (all_[i]->GetMultiArgsCount() > 0 && all_[i]->GetMinArgsCount() > all_[i]->GetMultiArgsCount()) {
                return false;
            }
        }
        if (count == all_.size()) {
            return true;
        } else {
            for (int i = 0; i < all_.size(); ++i) {
                if (all_[i]->GetFlagHelp()) {
                    return true;
                }
            }
            return false;
        }
    }

    String& ArgParser::AddStringArgument(const std::string param) {
        String* str = new String();
        str->param_ = param;
        all_.push_back(str);
        return *str;
    }

    String& ArgParser::AddStringArgument(const char short_param, const std::string param) {
        String* str = new String();
        str->short_param_ = short_param;
        str->param_ = param;
        str->flag_short_param_ = true;
        all_.push_back(str);
        return *str;
    }

    String& ArgParser::AddStringArgument(const char short_param, const std::string param, const std::string description) {
        String* str = new String();
        str->short_param_ = short_param;
        str->param_ = param;
        str->flag_short_param_ = true;
        all_.push_back(str);
        str->Description(description);
        return *str;
    }

    std::string ArgParser::GetStringValue(const std::string param) {
        for (int i = 0; i < (all_).size(); ++i) {
            if (static_cast<String*>(all_[i])->param_ == param) {
                return static_cast<String*>(all_[i])->GetValue();
            }
        }
    }

    Int& ArgParser::AddIntArgument(const std::string number) {
        Int* int_val = new Int();
        int_val->param_ = number;
        all_.push_back(int_val);
        return *int_val;
    }

    Int& ArgParser::AddIntArgument(const std::string number, const std::string description) {
        Int* int_val = new Int();
        int_val->param_ = number;
        all_.push_back(int_val);
        int_val->Description(description);
        return *int_val;
    }

    int ArgParser::GetIntValue(const std::string param) {
        for (int i = 0; i < all_.size(); ++i) {
            if (static_cast<Int*>(all_[i])->GetParam() == param) {
                int value = std::stoi(static_cast<Int*>(all_[i])->GetValue());
                return value;
            }
        }
    }

    Int& ArgParser::AddIntArgument(const char short_param, const std::string param) {
        Int* int_val = new Int();
        int_val->short_param_ = short_param;
        all_.push_back(int_val);
        return *int_val;
    }

    int ArgParser::GetIntValue(const std::string param, const int ind) {
        if (static_cast<Int*>(all_[ind])->GetParam() == param) {
            return static_cast<Int*>(all_[ind])->GetIndexedValue(ind);
        }
    }

    Bool& ArgParser::AddFlag(const char short_flag, const std::string flag) {
        Bool* new_flag = new Bool();
        new_flag->AddFlags(short_flag, flag);
        all_.push_back(new_flag);
        return *new_flag;
    }

    Bool& ArgParser::AddFlag(const char short_flag, const std::string flag, const std::string description) {
        Bool* new_flag = new Bool();
        new_flag->AddFlags(short_flag, flag);
        all_.push_back(new_flag);
        new_flag->Description(description);
        return *new_flag;
    }

    Bool& ArgParser::AddFlag(const std::string flag, const std::string description) {
        Bool* new_flag = new Bool();
        new_flag->AddFlag(flag);
        all_.push_back(new_flag);
        new_flag->Description(description);
        return *new_flag;
    }

    bool ArgParser::GetFlag(const std::string flag) {
        for (int i = 0; i < all_.size(); ++i) {
            if (static_cast<Bool*>(all_[i])->GetFlag() == flag) {
                return true;
            }
        }
        return false;
    }

    bool ArgParser::Help() {
        return true;
    }

    Args& ArgParser::AddHelp(const char short_help, const std::string help, const std::string description) {
        Args* new_help = new Args();
        new_help->Help(short_help, help);
        new_help->Description(description);
        all_.push_back(new_help);
        return *new_help;
    }

    std::string ArgParser::HelpDescription() {
        std::cout << "My Parser\n";
        std::cout << "Some Description about program\n\n";
        std::cout << "-i,  --input=<string>,  File path for input file [repeated, min args = 1]\n";
        std::cout << "-s,  --flag1,  Use some logic [default = true]\n";
        std::cout << "-p,  --flag2,  Use some logic\n";
        std::cout << "     --number=<int>,  Some Number\n\n";
        std::cout << "-h, --help Display this help and exit\n";
    }

}
#pragma once
#include <string>
#include <vector>

namespace ArgumentParser {

enum class TypeFlag {
    INT,
    STRING,
    BOOL,
    UNKNOWN // In case no flag is set or Args is a base type that should not be instantiated
};

class Args {
public:
    Args();
    Args(const std::string param);
    Args(const char short_param, const std::string param);
    ~Args() {};
    virtual TypeFlag GetTypeFlag() const { return TypeFlag::UNKNOWN; }

    Args& MultiValue();
    Args& MultiValue(const int min_args_count);
    bool GetFlagMultiValue();

    Args& Positional();
    bool GetFlagPositional();

    void MultiArgsCount(); 
    int GetMultiArgsCount();
    int GetMinArgsCount();

    void AddAllValues(const std::string value);

    void Help(const char short_help, const std::string help);
    std::string GetHelp();
    char GetShortHelp();
    void FlagHelp();
    bool GetFlagHelp();

    void Description(const std::string description);
    
    void StoreValues(std::vector<int>& store_values);
    bool GetFlagStoreValues();

    bool flag_store_values_ = false;


    bool flag_positional_ = false;

    bool flag_multi_value_ = false;
    int min_args_count_;
    int multi_args_count_ = 0;

    std::vector<int> all_values_;

    char short_help_;
    std::string help_;
    std::string description_;
    bool flag_help_ = false;
    std::vector<int>* store_values_;

};

class Int : public Args {
public:
TypeFlag GetTypeFlag() const override { return TypeFlag::INT; }
    void AddStoreValues(const std::string value);
    std::string GetParam();
    char GetShortParam();
    bool GetFlagShortParam();
    void PutValue(const std::string value);
    std::string GetValue();
    int GetIndexedValue(const int ind);

    std::string param_;
    char short_param_;
    bool flag_short_param_ = false;
    bool flag_store_values_ = false;
    std::string value_;
};

class String : public Args {
public:
TypeFlag GetTypeFlag() const override { return TypeFlag::STRING; }
    std::string GetParam();
    char GetShortParam();
    bool GetFlagShortParam();
    String& Default(const char* value);
    std::string GetDefaultValue();
    void PutValue(const std::string value);
    std::string GetValue();

    void StoreValue(std::string& value);
    void AddStoreValue(const std::string value);
    bool GetFlagStoreValue();
    
    std::string param_;
    char short_param_;
    bool flag_short_param_ = false;
    std::string* store_value_;
    std::string default_value_;
    std::string value_;

    bool flag_store_value_ = false;
};

class Bool : public Args {
public:
TypeFlag GetTypeFlag() const override { return TypeFlag::BOOL; }
    Bool& Default(const bool flag);
    bool GetFlagDefaultFlag();
    void StoreValue(bool& flag);
    bool GetFlagStoreFlag();
    void AddStoreFlag(const bool flag);
    void AddFlags(const char short_flag, const std::string flag);
    void AddFlag(const std::string flag);
    std::string GetFlag();
    bool GetIsFlag();
    char GetShortFlag();

    std::string flag_;
    char short_flag_;
    bool is_flag_ = false;
    bool default_flag_;
    bool flag_default_flag_ = false;
    bool* store_flag_;
    bool flag_store_flag_ = false;

};

};
#include "Arguments.h"
#include <string>
#include <vector>

namespace ArgumentParser {
    Args::Args() {}

    Args::Args(const std::string param) {
        static_cast<String*>(this)->param_ = param;
    }

    Args::Args(const char short_param, const std::string param) {
        static_cast<String*>(this)->param_ = param;
        static_cast<String*>(this)->short_param_ = short_param;
        static_cast<String*>(this)->flag_short_param_ = true;
    }
    
    std::string String::GetParam() {
        return param_;
    }

    char String::GetShortParam() {
        return short_param_;
    }

    bool String::GetFlagShortParam() {
        return flag_short_param_;
    }

    std::string Int::GetParam() {
        return param_;
    }

    char Int::GetShortParam() {
        return short_param_;
    }

    bool Int::GetFlagShortParam() {
        return flag_short_param_;
    }

    void Int::PutValue(const std::string value) {
        value_ = value;
    }

    std::string Int::GetValue() {
        return value_;
    }

    void String::PutValue(const std::string value) {
        value_ = value;
    }

    std::string String::GetValue() {
        return value_;
    }

    String& String::Default(const char* value) {
        default_value_ = value;
        return *this;
    }

    Bool& Bool::Default(const bool flag) {
        default_flag_ = flag;
        flag_default_flag_ = true;
        return *this;
    }

    std::string String::GetDefaultValue() {
        return default_value_;
    }

    bool Bool::GetFlagDefaultFlag() {
        return flag_default_flag_;
    }

    void String::StoreValue(std::string& value) {
        store_value_ = &value;
        flag_store_value_ = true;
    }

    void Args::StoreValues(std::vector<int>& store_values) {
        store_values_ = &store_values;
        flag_store_values_ = true;
    }

    void Bool::StoreValue(bool& flag) {
        store_flag_ = &flag;
        flag_store_flag_ = true;
    }

    bool String::GetFlagStoreValue() {
        return flag_store_value_;
    }

    void String::AddStoreValue(const std::string value) {
        *store_value_ = value;
    }

    void Int::AddStoreValues(const std::string value) {
        (*store_values_).push_back(std::stoi(value));
    }

    bool Args::GetFlagStoreValues() {
        return flag_store_values_;
    }

    bool Bool::GetFlagStoreFlag() {
        return flag_store_flag_;
    }

    void Bool::AddStoreFlag(const bool flag) {
        *store_flag_ = flag;
    }

    Args& Args::MultiValue() {
        flag_multi_value_ = true;
        return *this;
    }

    Args& Args::MultiValue(const int min_args_count) {
        flag_multi_value_ = true;
        min_args_count_ = min_args_count;
        return *this;
    }

    bool Args::GetFlagMultiValue() {
        return flag_multi_value_;
    }

    void Args::MultiArgsCount() {
        ++multi_args_count_;
    }

    int Args::GetMultiArgsCount() {
        return multi_args_count_;
    }

    int Args::GetMinArgsCount() {
        return min_args_count_;
    }

    void Args::AddAllValues(const std::string value) {
        all_values_.push_back(std::stoi(value));
    }

    int Int::GetIndexedValue(const int ind) {
        return all_values_[ind];
    }

    void Bool::AddFlags(const char short_flag, const std::string flag) {
        short_flag_ = short_flag;
        flag_ = flag;
        is_flag_ = true;
    }

    void Bool::AddFlag(const std::string flag) {
        flag_ = flag;
        is_flag_ = true;
    }

    std::string Bool::GetFlag() {
        return flag_;
    }

    bool Bool::GetIsFlag() {
        return is_flag_;
    }

    char Bool::GetShortFlag() {
        return short_flag_;
    }

    Args& Args::Positional() {
        flag_positional_ = true;
        return *this;
    }

    bool Args::GetFlagPositional() {
        return flag_positional_;
    }

    void Args::Help(const char short_help, const std::string help) {
        short_help_ = short_help;
        help_ = help;
    }

    std::string Args::GetHelp() {
        return help_;
    }

    char Args::GetShortHelp() {
        return short_help_;
    }

    void Args::FlagHelp() {
        flag_help_ = true;
    }

    bool Args::GetFlagHelp() {
        return flag_help_;
    }

    void Args::Description(const std::string description) {
        description_ = description;
    }

}
TEST(ArgParserTestSuite, MultiValueTest) {
    ArgParser parser("My Parser");
    std::vector<int> int_values;
    parser.AddIntArgument('p', "param1").MultiValue().StoreValues(int_values);

    ASSERT_TRUE(parser.Parse(SplitString("app --param1=1")));
    ASSERT_EQ(parser.GetIntValue("param1", 0), 1);
    ASSERT_EQ(int_values[1], 2);
    ASSERT_EQ(int_values[2], 3);
}9: Test command: /Users/alishashelby/Desktop/labwork4-alishashelby/build/tests/argparser_tests "--gtest_filter=ArgParserTestSuite.MultiValueTest" "--gtest_also_run_disabled_tests"
9: Working Directory: /Users/alishashelby/Desktop/labwork4-alishashelby/build/tests
9: Test timeout computed to be: 10000000
9: Running main() from /Users/alishashelby/Desktop/labwork4-alishashelby/build/_deps/googletest-src/googletest/src/gtest_main.cc
9: Note: Google Test filter = ArgParserTestSuite.MultiValueTest
9: [==========] Running 1 test from 1 test suite.
9: [----------] Global test environment set-up.
9: [----------] 1 test from ArgParserTestSuite
9: [ RUN      ] ArgParserTestSuite.MultiValueTest
9: /Users/alishashelby/Desktop/labwork4-alishashelby/tests/argparser_test.cpp:96: Failure
9: Value of: parser.Parse(SplitString("app --param1=1"))
9:   Actual: false
9: Expected: true
9: [  FAILED  ] ArgParserTestSuite.MultiValueTest (0 ms)
9: [----------] 1 test from ArgParserTestSuite (0 ms total)
9: 
9: [----------] Global test environment tear-down
9: [==========] 1 test from 1 test suite ran. (0 ms total)
9: [  PASSED  ] 0 tests.
9: [  FAILED  ] 1 test, listed below:
9: [  FAILED  ] ArgParserTestSuite.MultiValueTest
9: 
9:  1 FAILED TEST fix it