#include <map>
#include <utility>
#include "function_signature.h"

namespace domain {

    class DivisibleValidator : public FunctionValidator {
    public:
        DivisibleValidator() : FunctionValidator("divisible", {Value::INT, Value::INT}) {}

        bool validate(std::vector<Value> values) override {
            if (validateSignature(values)) {
                long long first = values[0].asInt();
                long long second = values[1].asInt();
                return (first % second) == 0;
            }
            return false;
        }

        void clear() override {}
    };

    class LessThenValidator : public FunctionValidator {
    public:
        LessThenValidator() : FunctionValidator("less_then", {Value::INT, Value::INT}) {}

        bool validate(std::vector<Value> values) override {
            if (validateSignature(values)) {
                long long first = values[0].asInt();
                long long second = values[1].asInt();
                return first < second;
            }
            return false;
        }

        void clear() override {}
    };

    class GreaterThenValidator : public FunctionValidator {
    public:
        GreaterThenValidator() : FunctionValidator("greater_then", {Value::INT, Value::INT}) {}

        bool validate(std::vector<Value> values) override {
            if (validateSignature(values)) {
                long long first = values[0].asInt();
                long long second = values[1].asInt();
                return first > second;
            }
            return false;
        }

        void clear() override {}
    };


    class ContainsAddressValidator : public FunctionValidator {
    public:
        ContainsAddressValidator() : FunctionValidator("contains_address", {Value::ADDRESS_SPACE, Value::INT}) {}

        bool validate(std::vector<Value> values) override {
            if (validateSignature(values)) {
                AddressSpace space = values[0].asAddressSpace();
                long long address = values[1].asInt();
                return space.contains(address);
            }
            return false;
        }

        void clear() override {}
    };


    class ContainsValidator : public FunctionValidator {
    public:
        ContainsValidator() : FunctionValidator("contains", {Value::ADDRESS_SPACE, Value::INT, Value::INT}) {}

        bool validate(std::vector<Value> values) override {
            if (validateSignature(values)) {
                AddressSpace space = values[0].asAddressSpace();
                long long start = values[1].asInt();
                long long size = values[1].asInt();
                return space.contains(start, start + size);
            }
            return false;
        }

        void clear() override {}
    };


    class UniqueValidator : public FunctionValidator {

    private:
        std::map<std::string, std::vector<std::pair<long long int, long long int>>> spaces;

        bool overlaps(long long int start1, long long int end1, long long int start2, long long int end2) {
            return std::max((long long int) 0, std::min(end1, end2) - std::max(start1, start2)) > 0;
        }

    public:
        UniqueValidator() : FunctionValidator("unique", {Value::ADDRESS_SPACE, Value::INT, Value::INT}) {}

        bool validate(std::vector<Value> values) override {
            if (validateSignature(values)) {
                std::string space = values[0].asAddressSpace().getName();
                long long int start = values[1].asInt();
                long long int end = start + values[2].asInt();
                if (spaces.count(space) == 0) {
                    spaces.insert(std::make_pair(space, std::vector<std::pair<long long int, long long int>>{}));
                }
                for (auto &s: spaces[space]) {
                    if (overlaps(s.first, s.second, start, end)) {
                        return false;
                    }
                }
                spaces[space].push_back(std::make_pair(start, end));
                return true;
            }
            return false;
        }

        void clear() override {
            spaces.clear();
        }
    };


    std::vector<FunctionValidator *> getSupportedValidators() {
        std::vector<FunctionValidator *> validators;

        validators.push_back(new DivisibleValidator());
        validators.push_back(new LessThenValidator());
        validators.push_back(new GreaterThenValidator());
        validators.push_back(new ContainsAddressValidator());
        validators.push_back(new ContainsValidator());
        validators.push_back(new UniqueValidator());

        return validators;
    }


    FunctionValidator::FunctionValidator(std::string name, std::vector<Value::ValueType> signature)
        : name(std::move(name)), signature(std::move(signature)) {}

    std::string FunctionValidator::getName() {
        return name;
    }

    std::vector<Value::ValueType> FunctionValidator::getSignature() {
        return signature;
    }

    bool FunctionValidator::validateSignature(std::vector<Value> _signature) {
        if (this->signature.size() != _signature.size()) {
            return false;
        }

        for (int i = 0; i < this->signature.size(); i++) {
            if (this->signature[i] != _signature[i].getType()) {
                return false;
            }
        }
        return true;
    }

} // namespace domain