#include "argparse_util.hpp"
#include <cstring>
#include <algorithm>

namespace argparse {

    std::array<std::string,2> split_leading_dashes(std::string str) {
        auto iter = str.begin();
        while(*iter == '-') {
            ++iter;
        }

        std::string dashes(str.begin(), iter);
        std::string name(iter, str.end());
        std::array<std::string,2> array = {dashes, name};

        return array;
    }

    bool is_argument(std::string str, const std::map<std::string,std::shared_ptr<Argument>>& arg_map) {

        for (const auto& kv : arg_map) {
            auto iter = arg_map.find(str);

            if (iter != arg_map.end()) {
                //Exact match to short/long option
                return true;
            }

            if (kv.first.size() == 2 && kv.first[0] == '-') {
                //Check iff this is a short option with no spaces
                if (str[0] == kv.first[0] && str[1] == kv.first[1]) {
                    //Matches first two characters
                    return true;
                }
            }

        }
        return false;
    }

    bool is_valid_choice(std::string str, const std::vector<std::string>& choices) {
        if (choices.empty()) return true;

        auto find_iter = std::find(choices.begin(), choices.end(), str);
        if (find_iter == choices.end()) {
            return false;
        }
        return true;
    }

    std::string toupper(std::string str) {
        std::string upper;
        for (size_t i = 0; i < str.size(); ++i) {
            char C = ::toupper(str[i]);
            upper.push_back(C);
        }
        return upper;
    }

    std::string tolower(std::string str) {
        std::string lower;
        for (size_t i = 0; i < str.size(); ++i) {
            char C = ::tolower(str[i]);
            lower.push_back(C);
        }
        return lower;
    }

    char* strdup(const char* str) {
        size_t len = std::strlen(str);
        char* res = new char[len+1]; //+1 for terminator
        std::strcpy(res, str);
        return res;
    }

    std::vector<std::string> wrap_width(std::string str, size_t width, std::vector<std::string> break_strs) {
        std::vector<std::string> wrapped_lines;

        size_t start = 0;
        size_t end = 0;
        size_t last_break = 0;
        for(end = 0; end < str.size(); ++end) {

            size_t len = end - start;

            if (len > width) {
                auto wrapped_line = std::string(str, start, last_break - start) + "\n";
                wrapped_lines.push_back(wrapped_line);
                start = last_break;
            }

            //Find the next break
            for (const auto& brk_str : break_strs) {
                auto pos = str.find(brk_str, end); //FIXME: this is inefficient
                if (pos == end) {
                    last_break = end + 1;
                }
            }

            //If there are embedded new-lines then take them as forced breaks
            char c = str[end];
            if (c == '\n') {
                last_break = end + 1;
                auto wrapped_line = std::string(str, start, last_break - start);
                wrapped_lines.push_back(wrapped_line);
                start = last_break;
            }
        }

        auto last_line = std::string(str, start, end - start);
        wrapped_lines.push_back(last_line);

        return wrapped_lines;
    }

    std::string basename(std::string filepath) {
#ifdef _WIN32
        //Windows uses back-slash as directory divider
        auto pos = filepath.rfind('\\');
#else
        //*nix-like uses forward-slash as directory divider
        auto pos = filepath.rfind('/');
#endif
        if (pos == std::string::npos) {
            pos = 0;
        } else {
            pos += 1;
        }

        return std::string(filepath, pos, filepath.size() - pos);
    }
} //namespace
