#include "Input.h"

namespace mdd{
    int Input::addConnection(std::shared_ptr<IOutput> output)
    {
        _output = output;
        return 1;
    }
    int Input::removeConnection(std::shared_ptr<IOutput> output)
    {
        _output = nullptr;
        return 0;
    }
    Input::Input(const std::string& name, int appendix, const std::vector<double>& default_value) {
        _name = name;
        _value = default_value;
        _appendix = appendix;
        _optimizable = false;

        type = "input";
        key = "Input";
    }

    std::string Input::getType() {
        return  type;
    }

    std::string Input::getGeneratorKey() {
        return  key;
    }
 
    std::string Input::setName(const std::string& name){
        _name = name;
        return getID();
    }

    std::string Input::getName() {
        return  _name;
    }
    
    std::string Input::setAppendix(int appendix){
        _appendix = appendix;
        return  getID();
    }

    int Input::getAppendix() {
        return _appendix;
    }

    std::string Input::getID() {
        return key + std::to_string(_appendix);
    }

    void Input::setParent(std::shared_ptr<IModule> parent) {
        _parent = parent;
    }

    std::vector<std::string> Input::getParentID() {
        std::vector<std::string> ret;
        if (auto parent_ptr = _parent.lock())
        {
            ret = parent_ptr->getParentID();
            ret.push_back(parent_ptr->getID());
        }
        return ret;
    }

    state Input::getState() {
        if (_output == nullptr) {
            return state::UNCHANGED;
        }
        else {
            return _output->getState();
        }
    }

    void Input::resetState() {

    }

    const std::vector<double>& Input::getValue() {
        if(_output == nullptr){
            return _value;
        } else{
            return _output->getValue();
        }
    }

    std::shared_ptr<IOutput> Input::getConnection() { return _output; }

    std::vector<double>& Input::setValue(){
        return _value;
    }

    state Input::setValue(const std::vector<double>& val) {
        if (val != _value)
        {
            _value = val;
            return state::CHANGED;
        }
        else {
            return state::UNCHANGED;
        }
    }

    bool Input::connect(std::shared_ptr<IOutput> output){
        if (_output != nullptr) {
            if (_output->getIdentifier() != output->getIdentifier())
            {
                _output->removeConnection(shared_from_this());
                removeConnection();
            }
        }
        if (_output == nullptr)
        {
            addConnection(output);
            _output->addConnection(shared_from_this());
        }
        return true;
    }

    void Input::disconnect() {
        if (_output != nullptr)
        {
            _output->removeConnection(std::make_shared<Input>((*this)));
        }
    }

    bool Input::isOptimizable() {
        return _optimizable;
    }

    void Input::setOptimizability(bool state) {
        _optimizable = state;
        if (_optimizable && _limit == nullptr)
        {
            _limit = std::make_shared<limits>();
        }
    }
    const std::shared_ptr<limits> Input::getLimits()
    {
        return _limit;
    }
    std::shared_ptr<limits>& Input::setLimits()
    {
        return _limit;
    }

    bool Input::configure(const json& config)
    {
        json jparse = config;
        bool success = false;
        auto jit_config = jparse.find("configure");
        json jconfig = jparse;
        if (jit_config != jparse.end())
        {
            jconfig = *jit_config;
        }
        auto jit = jconfig.find("name");
        if (jit != jconfig.end())
        {
            _name = jit.value().get<std::string>();
            success = true;
        }

        jit = jparse.find("ID");
        if (jit != jparse.end())
        {
            auto jit2 = jit->find("appendix");
            if (jit2 != jit->end())
            {
                _appendix = jit2.value().get<int>();
                success = true;
            }
        }

        jit = jconfig.find("optimizable");
        if (jit != jconfig.end())
        {
            auto jopt = jit->find("value");
            if (jopt != jit->end())
            {
                json jval = jopt.value();
                if (jval.is_string()) {
                    jval = json::parse(jval.get<std::string>());
                }
                setOptimizability(jval.get<bool>());
            }
            else
            {
                setOptimizability(jit.value().get<bool>());
            }
            success = true;
        }

        jit = jconfig.find("value");
        if (jit != jconfig.end())
        {
            json jval = jit.value();
            try
            {
                if (jval.is_string()) {
                    jval = json::parse(jval.get<std::string>());
                }
                _value = jval.get<std::vector<double>>();
                success = true;
            }
            catch (const nlohmann::detail::parse_error&)
            {
                success = false;
            }
            
        }

        auto jit_limit = jparse.find("limit");
        if (jit_limit != jparse.end())
        {
            auto jit = jit_limit->find("min");
            if (jit != jit_limit->end())
            {
                auto jit_val = jit->find("value");
                json jval;
                if (jit_val != jit->end())
                {
                    jval = jit_val.value();
                }
                else
                {
                    jval = jit.value();
                }
               
                if (jval.is_string()) {
                    std::string jtest = jval.get<std::string>();
                    jval = json::parse(jtest);
                }
                _limit->min = jval.get<std::vector<double>>();
                success = true;
            }

            jit = jit_limit->find("max");
            if (jit != jit_limit->end())
            {
                auto jit_val = jit->find("value");
                json jval;
                if (jit_val != jit->end())
                {
                    jval = jit_val.value();
                }
                else
                {
                    jval = jit.value();
                }
                if (jval.is_string()) {
                    jval = json::parse(jval.get<std::string>());
                }
                _limit->max = jval.get<std::vector<double>>();
                success = true;
            }

            jit = jit_limit->find("step");
            if (jit != jit_limit->end())
            {
                auto jit_val = jit->find("value");
                json jval;
                if (jit_val != jit->end())
                {
                    jval = jit_val.value();
                }
                else
                {
                    jval = jit.value();
                }
                if (jval.is_string()) {
                    jval = json::parse(jval.get<std::string>());
                }
                _limit->step = jval.get<std::vector<double>>();
                success = true;
            }

            jit = jit_limit->find("rule");
            if (jit != jit_limit->end())
            {
                auto jit_val = jit->find("value");
                json jval;
                if (jit_val != jit->end())
                {
                    jval = jit_val.value();
                }
                else
                {
                    jval = jit.value();
                }
                _limit->rule = jval.get<std::string>();
                success = true;
            }

            jit = jit_limit->find("elements");
            if (jit != jit_limit->end())
            {
                auto jit_val = jit->find("value");
                json jval;
                if (jit_val != jit->end())
                {
                    jval = jit_val.value();
                }
                else
                {
                    jval = jit.value();
                }
                if (jval.is_string()) {
                    jval = json::parse(jval.get<std::string>());
                }
                _limit->elements = jval.get<std::vector<std::vector<double>>>();
                success = true;
            }
        }
        return success;
    }

    const json& Input::getConfiguration()
    {
        json jconfig;
        jconfig["name"] = _name;
        jconfig["optimizable"]["value"] = _optimizable;
        jconfig["optimizable"]["options"] = { true, false };
        jconfig["value"] = _value;
       
        _base_config["configure"] = jconfig;
        if (_optimizable == true && _limit != nullptr)
        {
            json jlimit;
            jlimit["min"] = _limit->min;
            jlimit["max"] = _limit->max;
            jlimit["step"] = _limit->step;
            jlimit["rule"] = _limit->rule;
            jlimit["elements"] = _limit->elements;
            _base_config["limit"] = jlimit;
        }
        return _base_config;
    }

    void Input::load(const json& j) 
    {
        configure(j);
        return;
    }

    json Input::dump() 
    {
        json jdump = getConfiguration();
        jdump["ID"] = getIdentifier();
        jdump["type"] = "input";
        jdump["key"] = "Input";
        return jdump;
    }

    json Input::getIdentifier() {
        json jID;
        jID["key"] = key;
        jID["appendix"] = _appendix;
        jID["type"] = "input";
        jID["prefix"] = getParentID();
        return jID;
    }
}