#include "ProcessorBase.h"



namespace mdd{

    int ProcessorBase::addProcesorInput(const std::string &type, const std::vector<double>&value){
        _processor_inputs.push_back(std::make_shared<Input>( type, _processor_inputs.size(), value));
        _processor_inputs[_processor_inputs.size() - 1]->setPrefix(getID());
        return  _processor_inputs.size()-1;
    }

    int ProcessorBase::addProcessorOutput(const std::string& type, const std::vector<double>& initial){
        _processor_outputs.push_back(std::make_shared<Output>( type, _processor_outputs.size(), initial));
        _processor_outputs[_processor_outputs.size() - 1]->setPrefix(getID());
        return  _processor_outputs.size()-1;
    }

    std::shared_ptr<IOutput> ProcessorBase::getProcessorOutput(int handle){

        return _processor_outputs[handle];
    }

    std::shared_ptr<IInput> ProcessorBase::getProcessorInput(int handle){

        return _processor_inputs[handle];
    }

    std::shared_ptr<IModule> ProcessorBase::getModule(int handle){
        return _modules[handle];
    }

    void ProcessorBase::updateID(){
        std::string new_prefix =  getID();
        for (auto &input  : _processor_inputs){
            input->setPrefix(new_prefix);
        }
        for (auto &output  : _processor_outputs){
            output->setPrefix(new_prefix);
        }
        for (auto& module : _modules) {
            module->setPrefix(new_prefix);
        }
    }

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

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

    std::string ProcessorBase::setPrefix(const std::string& prefix){
        _prefix = prefix;
        updateID();
        return getID();
    }

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


    std::string ProcessorBase::addModule(std::shared_ptr<IModule> module){
        _modules.emplace_back(module);
        _modules.back()->setPrefix(getID());
        _modules.back()->setAppendix(_modules.size()-1);
        return _modules.back()->getID();
    }

    void ProcessorBase::removeModule(std::shared_ptr<IModule> module)
    {
        for (auto it = _module_inputs.begin(); it != _module_inputs.end(); ++it) {
            if (it->moduleHandler == module)
            {
                _module_inputs.erase(it);
            }
        }

        for (auto it = _module_outputs.begin(); it != _module_outputs.end(); ++it) {
            if (it->moduleHandler == module)
            {
                _module_outputs.erase(it);
            }
        }

        for (auto it = _modules.begin(); it != _modules.end(); ++it) {
            if ((*it) == module)
            {
                _modules.erase(it);
            }
        }
    }

    std::string ProcessorBase::addModuleInput(std::string module_ID, std::string input_ID){
        int ret = _processor_inputs.size();
        for (auto& module : _modules) {
            if(module->getID() == module_ID){
                _module_inputs.emplace_back(HandlerModule<IInput>(module, module->getInput(input_ID)));
            }
        }
        return _module_inputs.back().accessHandler->getID();
    }

    std::string ProcessorBase::addModuleInput(std::shared_ptr<IModule> module, std::shared_ptr<IInput> input){
        _module_inputs.emplace_back(HandlerModule<IInput>(module, input));
        return _module_inputs.back().accessHandler->getID();
    }

    std::string ProcessorBase::addModuleOutput(std::string module_ID, std::string output_ID){
        for (auto& module : _modules) {
            if(module->getID() == module_ID){
                _module_outputs.emplace_back(HandlerModule<IOutput>(module, module->getOutput(output_ID)));
            }
        }
        return _module_outputs.back().accessHandler->getID();
    }

    std::string ProcessorBase::addModuleOutput(std::shared_ptr<IModule> module, std::shared_ptr<IOutput> output){
        _module_outputs.emplace_back(HandlerModule<IOutput>(module, output));
        return _module_outputs.back().accessHandler->getID();
    }

    std::vector<std::string> ProcessorBase::getInputs(){
        std::vector<std::string> ret;
        for (auto &input  : _processor_inputs) {
            ret.push_back(input->getType());
        }
        for (auto &input  : _module_inputs) {
            ret.push_back(input.accessHandler->getType());
        }
        return ret;
    }

    std::vector<std::string> ProcessorBase::getOutputs(){
        std::vector<std::string> ret;
        for (auto &output  : _processor_outputs) {
            ret.push_back(output->getType());
        }
        for (auto &output  : _module_outputs) {
            ret.push_back(output.accessHandler->getType());
        }
        return ret;
    }

    std::vector<std::string> ProcessorBase::getModules(){
        std::vector<std::string> ret;
        for (auto &module  : _modules) {
            ret.push_back(module->getType());
        }
        return ret;
    }

    std::vector<std::string> ProcessorBase::getInputIDs(){
        std::vector<std::string> ret;
        for (auto &input  : _processor_inputs) {
            ret.push_back(input->getID());
        }
        for (auto &input  : _module_inputs) {
            ret.push_back(input.accessHandler->getID());
        }
        return ret;
    }

    std::vector<std::string> ProcessorBase::getOutputIDs(){
        std::vector<std::string> ret;
        for (auto &output  : _processor_outputs) {
            ret.push_back(output->getID());
        }
        for (auto &output  : _module_outputs) {
            ret.push_back(output.accessHandler->getID());
        }
        return ret;
    }

    std::vector<std::string> ProcessorBase::getModuleIDs(){
        std::vector<std::string> ret;
        for (auto &module  : _modules) {
            ret.push_back(module->getID());
        }
        return ret;
    }

    std::vector<state> ProcessorBase::getInputStates() {
        std::vector<state> ret;
        for (auto& input : _processor_inputs) {
            ret.push_back(input->getState());
        }
        for (auto& input : _module_inputs) {
            ret.push_back(input.accessHandler->getState());
        }
        return ret;
    }
    
    std::vector<state> ProcessorBase::getOutputStates() {
        std::vector<state> ret;
        for (auto& output : _processor_outputs) {
            ret.push_back(output->getState());
        }
        for (auto& output : _module_outputs) {
            ret.push_back(output.accessHandler->getState());
        }
        return ret;
    }

    std::vector<std::shared_ptr<IOutput>> ProcessorBase::getInputConnections()
    {
        std::vector<std::shared_ptr<IOutput>> ret;
        for (auto& input : _processor_inputs) {
            ret.push_back(input->getConnection());
        }
        for (auto& input : _module_inputs) {
            ret.push_back(input.accessHandler->getConnection());
        }
        return ret;
    }

    std::vector<std::vector<std::shared_ptr<IInput>>> ProcessorBase::getOutputConnections()
    {
        std::vector<std::vector<std::shared_ptr<IInput>>> ret;
        for (auto& output : _processor_outputs) {
            ret.push_back(output->getConnections());
        }
        for (auto& output : _module_outputs) {
            ret.push_back(output.accessHandler->getConnections());
        }
        return ret;
    }

    std::vector <std::shared_ptr<IInput>> ProcessorBase::getOptimizableInputs() 
    {
        std::vector<std::shared_ptr<IInput>> ret;
        for (auto& input : _processor_inputs) {
            if (input->isOptimizable())
            {
                ret.push_back(input);
            }
        }
        for (auto& mod : _modules) {
            auto in = mod->getOptimizableInputs();
            for (size_t i = 0; i < in.size(); i++)
            {
                ret.push_back(in[i]);
            }
        }
        return ret;
    }

    std::vector <std::shared_ptr<IOutput>> ProcessorBase::getOptimizableOutputs()
    {
        std::vector<std::shared_ptr<IOutput>> ret;
        for (auto& output : _processor_outputs) {
            if (output->isOptimizable())
            {
                ret.push_back(output);
            }
        }
        for (auto& mod : _modules) {
            auto out = mod->getOptimizableOutputs();
            for (size_t i = 0; i < out.size(); i++)
            {
                ret.push_back(out[i]);
            }
        }
        
        return ret;
    }

    std::shared_ptr<IOutput> ProcessorBase::getOutput(const std::string& output_id){
        for (auto& output : _processor_outputs) {
            if(output->getID() == output_id){
                return output;
            }
        }
        for (auto& output : _module_outputs) {
            if(output.accessHandler->getID() == output_id){
                return output.accessHandler;
            }
        }
        return nullptr;
    }

    std::shared_ptr<IInput> ProcessorBase::getInput(const std::string& input_id){
        for (auto& input : _processor_inputs) {
            if(input->getID() == input_id){
                return input;
            }
        }
        for (auto& input : _module_inputs) {
            if(input.accessHandler->getID() == input_id){
                return input.accessHandler;
            }
        }
        return nullptr;
    }

    std::shared_ptr<IModule> ProcessorBase::getModule(std::string module_id){
        for (auto& module : _modules) {
            if(module->getID() == module_id){
                return module;
            }
        }
        return nullptr;
    }
}