#include "ModuleBase.h"
#include <algorithm>

namespace mdd {

    std::string ModuleBase::setType(std:: string type){
        _type=type;
        updateID();
        return getID();
    }
    std::string ModuleBase::getType(){
        return _type;
    }

    std::vector<std::string> ModuleBase::getInputs() {
        std::vector<std::string> ret;
        for (auto &input  : _inputs) {
            ret.push_back(input->getType());
        }
        return ret;
    }

    std::vector<std::string> ModuleBase::getInputIDs(){
        std::vector<std::string> ret;
        for (auto &input  : _inputs) {
            ret.push_back(input->getID());
        }
        return ret;
    }

    std::vector<std::string> ModuleBase::getOutputs() {
        std::vector<std::string> ret;
        for (auto &output  : _outputs) {
            ret.push_back(output->getType());
        }
        return ret;
    }

    std::vector<std::string> ModuleBase::getOutputIDs(){
        std::vector<std::string> ret;
        for (auto &output  : _outputs) {
            ret.push_back(output->getID());
        }
        return ret;
    }

    std::vector<state> ModuleBase::getInputStates() {
        std::vector<state> ret;
        for (auto& input : _inputs) {
            ret.push_back(input->getState());
        }
        return ret;
    }

    std::vector<state> ModuleBase::getOutputStates() {
        std::vector<state> ret;
        for (auto& output : _outputs) {
            ret.push_back(output->getState());
        }
        return ret;
    }

    std::vector<std::shared_ptr<IOutput>> ModuleBase::getInputConnections()
    {
        std::vector<std::shared_ptr<IOutput>> ret;
        for (auto& input : _inputs) {
            ret.push_back(input->getConnection());
        }
        return ret;
    }

    int ModuleBase::addInput(const std::string& type , const std::vector<double>& value) {
        _inputs.emplace_back(std::make_shared<Input>(type, _inputs.size(), value));
        _inputs.back()->setPrefix(getID());
        return _inputs.size() - 1;
    }


    std::vector<std::vector<std::shared_ptr<IInput>>> ModuleBase::getOutputConnections()
    {
        std::vector<std::vector<std::shared_ptr<IInput>>> ret;
        for (auto& output : _outputs) {
            ret.push_back(output->getConnections());
        }
        return ret;
    }

    std::vector<std::shared_ptr<IInput>> ModuleBase::getOptimizableInputs()
    {
        std::vector<std::shared_ptr<IInput>> ret;
        for (auto& it = _inputs.begin(); it != _inputs.end();  ++it)
        {
            if ((*it)->isOptimizable())
            {
                ret.push_back((*it));
            }
        }
        return ret;
    }

    std::vector<std::shared_ptr<IOutput>> ModuleBase::getOptimizableOutputs()
    {
        std::vector<std::shared_ptr<IOutput>> ret;
        for (auto& it = _outputs.begin(); it != _outputs.end(); ++it)
        {
            if ((*it)->isOptimizable())
            {
                ret.push_back((*it));
            }
        }
        return ret;
    }

    std::shared_ptr<IOutput> ModuleBase::getOutput(std::string output_id) {
        for(auto& output : _outputs){
                if (output->getID() == output_id) {
                    return output;
                }
        }
        return nullptr;
    }

    std::shared_ptr<IInput> ModuleBase::getInput(std::string input_id){
        for(auto& input : _inputs){
            if (input->getID() == input_id) {
                return input;
            }
        }
        return nullptr;
    }

    std::shared_ptr<IOutput> ModuleBase::getOutput(int handle) {
        return _outputs[handle];
    }

    std::shared_ptr<IInput> ModuleBase::getInput(int handle){
        return _inputs[handle];
    }

    int ModuleBase::addOutput(const std::string& type, const std::vector<double>& initial) {
        _outputs.push_back(std::make_shared<Output>( type, _outputs.size(), initial));
        _outputs.back()->setPrefix(getID());
        return  _outputs.size()-1;
    }

    int ModuleBase::pop_backInput(){
        _inputs.pop_back();
        return _inputs.size();
    }
    int ModuleBase::pop_backOutput(){
        _outputs.pop_back();
        return _outputs.size();
    }

    int ModuleBase::clearInput(){
        _inputs.clear();
        return _inputs.size();
    }
    int ModuleBase::clearOutput(){
        _outputs.clear();
        return _outputs.size();
    }

    int ModuleBase::eraseInput(int index){
        _inputs.erase(_inputs.begin()+index);
        return _inputs.size();
    }
    int ModuleBase::eraseOutput(int index){
        _outputs.erase(_outputs.begin()+index);
        return _outputs.size();
    }

    std::string ModuleBase::getID(){
        return _prefix + "/" + _type + std::to_string(_appendix);
    }

    std::string ModuleBase::setPrefix(std::string prefix){
        _prefix = prefix;
        updateID();
        return getID();
    }

    std::string ModuleBase::setAppendix(int appendix){
        _appendix = appendix;
        updateID();
        return getID();
    }

    void ModuleBase::updateID(){
        std::string new_prefix =  getID();
        for (auto &input  : _inputs){
            input->setPrefix(new_prefix);
        }
        for (auto &output  : _outputs){
            output->setPrefix(new_prefix);
        }
    }
}