#include <gtest/gtest.h>
#include <json.hpp>
//#define private public
#include "ProcessorStandard.h"
#include <ModuleMath.h>
#include <ModuleSwitch.h>


using namespace mdd;
TEST(ProcessorStandard, CalculateSimpleFormula){
    //f4:(f3:(f1:(5*3)-f2:(4+5))/2)==(15-9)/2==6/2==3
    std::vector<std::string> inputs;
    std::shared_ptr<ModuleMath> f1 = std::make_shared<ModuleMath>(MathOperation::MULTIPLY);
    inputs = f1->getInputIDs();
    f1->getInput(inputs[0])->setValue() = { 5 };
    f1->getInput(inputs[1])->setValue() = { 3 };

    std::shared_ptr<ModuleMath> f2 = std::make_shared<ModuleMath>(MathOperation::ADD);
    inputs = f2->getInputIDs();
    f2->getInput(inputs[0])->setValue() = { 4 };
    f2->getInput(inputs[1])->setValue() = { 5 };

    std::shared_ptr<ModuleMath> f3 = std::make_shared<ModuleMath>(MathOperation::SUBTRACT);
    inputs = f3->getInputIDs();
    f3->getInput(inputs[0])->connect(f1->getOutput(f1->getOutputIDs()[0]));
    f3->getInput(inputs[1])->connect(f2->getOutput(f2->getOutputIDs()[0]));

    std::shared_ptr<ModuleMath> f4 = std::make_shared<ModuleMath>(MathOperation::DIVIDE);
    inputs = f4->getInputIDs();
    f4->getInput(inputs[0])->connect(f3->getOutput(f3->getOutputIDs()[0]));
    f4->getInput(inputs[1])->setValue() = { 2 };

    std::shared_ptr<ProcessorStandard> processor = std::make_shared<ProcessorStandard>();
    processor->addModule(f1);
    processor->addModule(f2);
    processor->addModule(f3);
    processor->addModule(f4);
    processor->addModuleOutput(f4, f4->getOutput(f4->getOutputIDs()[0]));
    processor->update();

    EXPECT_EQ(f1->getOutput(f1->getOutputIDs()[0])->getValue()[0], 15);

    EXPECT_EQ(f2->getOutput(f2->getOutputIDs()[0])->getValue()[0], 9);

    EXPECT_EQ(f3->getOutput(f3->getOutputIDs()[0])->getValue()[0], 6);

    EXPECT_EQ(f4->getOutput(f4->getOutputIDs()[0])->getValue()[0], 3);

    EXPECT_EQ(processor->getOutput(processor->getOutputIDs()[1])->getValue()[0], 3);
}


TEST(ProcessorStandard, CalculateAdvancedFormula){
    //f4:(f3:(f1:(5*3)-f2:(4+5))/2)==(15-9)/2==6/2==3
    std::vector<std::string> inputs;
    std::shared_ptr<ModuleMath> f1 = std::make_shared<ModuleMath>(MathOperation::MULTIPLY);
    inputs = f1->getInputIDs();
    f1->getInput(inputs[0])->setValue() = { 5 };
    f1->getInput(inputs[1])->setValue() = { 3 };

    std::shared_ptr<ModuleMath> f2 = std::make_shared<ModuleMath>(MathOperation::ADD);
    inputs = f2->getInputIDs();
    f2->getInput(inputs[0])->setValue() = { 4 };
    f2->getInput(inputs[1])->setValue() = { 5 };

    std::shared_ptr<ModuleMath> f3 = std::make_shared<ModuleMath>(MathOperation::SUBTRACT);
    inputs = f3->getInputIDs();
    f3->getInput(inputs[0])->connect(f1->getOutput(f1->getOutputIDs()[0]));
    f3->getInput(inputs[1])->connect(f2->getOutput(f2->getOutputIDs()[0]));

    std::shared_ptr<ModuleMath> f4 = std::make_shared<ModuleMath>(MathOperation::DIVIDE);
    inputs = f4->getInputIDs();
    f4->getInput(inputs[0])->connect(f3->getOutput(f3->getOutputIDs()[0]));
    f4->getInput(inputs[1])->setValue() = { 2 };

    std::shared_ptr<ProcessorStandard> test = std::make_shared<ProcessorStandard>();
    test->addModule(f4);
    test->addModule(f3);
    test->addModule(f2);
    test->addModule(f1);
    test->addModuleOutput(f4, f4->getOutput(f4->getOutputIDs()[0]));
    test->update();
    //std::cout << test->getOutput(test->getOutputIDs()[1])->getValue()["value"].dump() << std::endl;
    EXPECT_EQ(test->getOutput(test->getOutputIDs()[1])->getValue()[0], 3);
}

TEST(ProcessorStandard, CalculateExtremeFormula){
    //x_0=8, x_i=x_{i-1}/2
    std::shared_ptr<ModuleSwitch> switchModule =  std::make_shared<ModuleSwitch>();
    std::shared_ptr<ModuleMath> calcModule = std::make_shared<ModuleMath>(MathOperation::DIVIDE);
    std::shared_ptr<ProcessorStandard> processor = std::make_shared<ProcessorStandard>();
    processor->addModule(switchModule);
    processor->addModule(calcModule);

    bool connect;
    connect =switchModule->getInput(switchModule->getInputIDs()[0])->connect(processor->getIteration());
    switchModule->getInput(switchModule->getInputIDs()[1])->setValue() = { 8.0 };
    connect = switchModule->getInput(switchModule->getInputIDs()[2])->connect(calcModule->getOutput(calcModule->getOutputIDs()[0]));

    calcModule->getInput(calcModule->getInputIDs()[0])->connect(switchModule->getOutput(switchModule->getOutputIDs()[0]));
    calcModule->getInput(calcModule->getInputIDs()[1])->setValue() = { 2.0 };

    processor->addModuleOutput(calcModule,calcModule->getOutput(calcModule->getOutputIDs()[0]));
    processor->update();

    EXPECT_FLOAT_EQ(processor->getOutput(processor->getOutputIDs()[1])->getValue()[0], 0.0);
}

TEST(ProcessorStandard, CalculateAdvancedFormulaWithSTATIC) {
    //f4:(f3:(f1:(5*3)-f2:(4+5))/2)==(15-9)/2==6/2==3
    std::vector<std::string> inputs;
    std::shared_ptr<ModuleMath> f1 = std::make_shared<ModuleMath>(MathOperation::MULTIPLY);
    inputs = f1->getInputIDs();
    f1->getInput(inputs[0])->setValue() = { 5 };
    f1->getInput(inputs[1])->setValue() = { 3 };

    std::shared_ptr<ModuleMath> f2 = std::make_shared<ModuleMath>(MathOperation::ADD);
    inputs = f2->getInputIDs();
    f2->getInput(inputs[0])->setValue() = { 4 };
    f2->getInput(inputs[1])->setValue() = { 5 };

    std::shared_ptr<ModuleMath> f3 = std::make_shared<ModuleMath>(MathOperation::SUBTRACT);
    inputs = f3->getInputIDs();
    f3->getInput(inputs[0])->connect(f1->getOutput(f1->getOutputIDs()[0]));
    f3->getInput(inputs[1])->connect(f2->getOutput(f2->getOutputIDs()[0]));

    std::shared_ptr<ModuleMath> f4 = std::make_shared<ModuleMath>(MathOperation::DIVIDE);
    inputs = f4->getInputIDs();
    f4->getInput(inputs[0])->connect(f3->getOutput(f3->getOutputIDs()[0]));
    f4->getInput(inputs[1])->setValue() = { 2 };

    std::shared_ptr<ProcessorStandard> process_static = std::make_shared<ProcessorStandard>(STATIC);
    process_static->addModule(f4);
    process_static->addModule(f3);
    process_static->addModule(f2);
    process_static->addModule(f1);
    process_static->addModuleOutput(f4, f4->getOutput(f4->getOutputIDs()[0]));
    process_static->update();
    //std::cout << test->getOutput(test->getOutputIDs()[1])->getValue()["value"].dump() << std::endl;
    EXPECT_EQ(process_static->getOutput(process_static->getOutputIDs()[1])->getValue()[0], 3);
}

TEST(ProcessorStandard, PrioritySTATIC) {
    //f4:(f3:(f1:(5*3)-f2:(4+5))/2)==(15-9)/2==6/2==3
    std::vector<std::string> inputs;
    std::shared_ptr<ModuleMath> f1 = std::make_shared<ModuleMath>(MathOperation::MULTIPLY);
    inputs = f1->getInputIDs();
    f1->getInput(inputs[0])->setValue() = { 5 };
    f1->getInput(inputs[1])->setValue() = { 3 };

    std::shared_ptr<ModuleMath> f2 = std::make_shared<ModuleMath>(MathOperation::ADD);
    inputs = f2->getInputIDs();
    f2->getInput(inputs[0])->setValue() = { 4 };
    f2->getInput(inputs[1])->setValue() = { 5 };

    std::shared_ptr<ModuleMath> f3 = std::make_shared<ModuleMath>(MathOperation::SUBTRACT);
    inputs = f3->getInputIDs();
    f3->getInput(inputs[0])->connect(f1->getOutput(f1->getOutputIDs()[0]));
    f3->getInput(inputs[1])->connect(f2->getOutput(f2->getOutputIDs()[0]));

    std::shared_ptr<ModuleMath> f4 = std::make_shared<ModuleMath>(MathOperation::DIVIDE);
    inputs = f4->getInputIDs();
    f4->getInput(inputs[0])->connect(f3->getOutput(f3->getOutputIDs()[0]));
    f4->getInput(inputs[1])->setValue() = { 2 };

    std::shared_ptr<ProcessorStandard> process_manual = std::make_shared<ProcessorStandard>();
    process_manual->addModule(f1);
    process_manual->addModule(f2);
    process_manual->addModule(f3);
    process_manual->addModule(f4);
    process_manual->addModuleOutput(f4, f4->getOutput(f4->getOutputIDs()[0]));
    process_manual->update();

    std::shared_ptr<ProcessorStandard> process_static = std::make_shared<ProcessorStandard>(STATIC);
    process_static->addModule(f4);
    process_static->addModule(f3);
    process_static->addModule(f2);
    process_static->addModule(f1);
    process_static->addModuleOutput(f4, f4->getOutput(f4->getOutputIDs()[0]));
    process_static->update();
    //std::cout << process_static->getOutput(process_static->getOutputIDs()[1])->getValue()["value"].dump() << std::endl;
    EXPECT_TRUE(process_static->getIteration()->getValue()[0] <= process_manual->getIteration()->getValue()[0]);
}
//*/