// Copyright 2022, FOSS-VG Developers and Contributers
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, version 3.
//
// This program 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// version 3 along with this program.
// If not, see https://www.gnu.org/licenses/agpl-3.0.en.html

#pragma once

#include <string>
#include <vector>
#include <map>

#include "error.hpp"

namespace CLI {

    struct Flag {
        char shortName;
        std::string longName;
        // used for automatic usage generation
        std::string description;

        bool present;

        Flag();
        Flag(char shortName, std::string longName, std::string description);
    };

    struct Option {
        char shortName;
        std::string longName;
        // used for automatic usage generation
        std::string description;
        std::string placeholder; // the "COUNT" in "ping [-c <COUNT>] <HOST>"

        bool present;
        std::string value;

        Option();
        Option(char shortName, std::string longName, std::string placeholder, std::string description);
    };

    struct Argument {
        // used for automatic usage generation
        std::string description;
        std::string placeholder; // the "HOST" in "ping [-c <COUNT>] <HOST>"

        bool present;
        std::string value;

        Argument();
        Argument(std::string placeholder, std::string description);
    };

    class ArgumentsParser {
        private:
            std::map<char, Flag*> flagsByShortName;
            std::map<std::string, Flag*> flagsByLongName;
            std::map<char, Option*> optionsByShortName;
            std::map<std::string, Option*> optionsByLongName;
            std::vector<Argument> arguments;
            std::string description;
            std::string additionalInfo;

        public:
            std::string programName;
            bool wrongUsage;
            std::vector<std::string> wrongUsageMessages;

            // using int here bc that's how main() is defined
            ArgumentsParser(int argc, const char* const argv[], std::vector<Flag> flags, std::vector<Option> options, std::vector<Argument> arguments);
            ArgumentsParser(int argc, const char* const argv[], std::vector<Flag> flags, std::vector<Option> options, std::vector<Argument> arguments, std::string description);
            ArgumentsParser(int argc, const char* const argv[], std::vector<Flag> flags, std::vector<Option> options, std::vector<Argument> arguments, std::string description, std::string additionalInfo);
            ~ArgumentsParser();

            ErrorOr<bool> getFlag(char shortName);
            ErrorOr<bool> getFlag(std::string longName);
            ErrorOr<std::string> getArgument(std::vector<CLI::Argument>::size_type position);
            ErrorOr<std::string> getOption(char shortName);
            ErrorOr<std::string> getOption(std::string longName);

            std::string getUsage();
    };

}
