#include <gtest/gtest.h>
#include <json.hpp>
#include <Registration.h>
#include "ProcessorStandard.h"

//#define private public


using namespace mdd;
namespace TEST_PROCESSOR_STANDARD {

    auto regi = Registration();

TEST(ProcessorStandard, CalculateSimpleFormula){
    //f4:(f3:(f1:(5*3)-f2:(4+5))/2)==(15-9)/2==6/2==3
    auto f1 = regi.generateModule("ModuleMath");
    json config = f1->getConfiguration();
    config["configure"]["operation"]["value"] = "multiply";
    f1->configure(config);
    f1->getInput(0)->setValue() = { 5 };
    f1->getInput(1)->setValue() = { 3 };

    auto f2 = regi.generateModule("ModuleMath");
    config = f2->getConfiguration();
    config["configure"]["operation"]["value"] = "add";
    f2->configure(config);
    f2->getInput(0)->setValue() = { 4 };
    f2->getInput(1)->setValue() = { 5 };

    auto f3 = regi.generateModule("ModuleMath");
    config = f3->getConfiguration();
    config["configure"]["operation"]["value"] = "subtract";
    f3->configure(config);
    f3->getInput(0)->connect(f1->getOutput(0));
    f3->getInput(1)->connect(f2->getOutput(0));

    auto f4 = regi.generateModule("ModuleMath");
    config = f4->getConfiguration();
    config["configure"]["operation"]["value"] = "divide";
    f4->configure(config.dump());
    f4->getInput(0)->connect(f3->getOutput(0));
    f4->getInput(1)->setValue() = { 2 };

    auto processor = std::make_shared<ProcessorStandard>();
    //regi.generateModule("ProcessorStandard");
    processor->addModule(f1);
    processor->addModule(f2);
    processor->addModule(f3);
    processor->addModule(f4);

    processor->getOutputParams().push_back(std::make_shared<Parameter>());
    processor->getOutputParams().at(0)->getInput(0)->connect(f4->getOutput(0));
   
    processor->update();

    EXPECT_EQ(f1->getOutput(0)->getValue()[0], 15);
    EXPECT_EQ(f2->getOutput(0)->getValue()[0], 9);
    EXPECT_EQ(f3->getOutput(0)->getValue()[0], 6);
    EXPECT_EQ(f4->getOutput(0)->getValue()[0], 3);

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

TEST(ProcessorStandard, CalculateAdvancedFormula){
    //f4:(f3:(f1:(5*3)-f2:(4+5))/2)==(15-9)/2==6/2==3
    auto f1 = regi.generateModule("ModuleMath");
    json config = f1->getConfiguration();
    config["configure"]["operation"]["value"] = "multiply";
    f1->configure(config);
    f1->getInput(0)->setValue() = { 5 };
    f1->getInput(1)->setValue() = { 3 };

    auto f2 = regi.generateModule("ModuleMath");
    config = f2->getConfiguration();
    config["configure"]["operation"]["value"] = "add";
    f2->configure(config);
    f2->getInput(0)->setValue() = { 4 };
    f2->getInput(1)->setValue() = { 5 };

    auto f3 = regi.generateModule("ModuleMath");
    config = f3->getConfiguration();
    config["configure"]["operation"]["value"] = "subtract";
    f3->configure(config);
    f3->getInput(0)->connect(f1->getOutput(0));
    f3->getInput(1)->connect(f2->getOutput(0));

    auto f4 = regi.generateModule("ModuleMath");
    config = f4->getConfiguration();
    config["configure"]["operation"]["value"] = "divide";
    f4->configure(config);
    f4->getInput(0)->connect(f3->getOutput(0));
    f4->getInput(1)->setValue() = { 2 };

    auto processor = std::make_shared<ProcessorStandard>();

    processor->addModule(f4);
    processor->addModule(f3);
    processor->addModule(f2);
    processor->addModule(f1);
    processor->getOutputParams().push_back(std::make_shared<Parameter>());
    processor->getOutputParams().at(0)->getInput(0)->connect(f4->getOutput(0));
    processor->update();
    //std::cout << test->getOutput(test->getOutputIDs()[1])->getValue()["value"].dump() << std::endl;
    EXPECT_EQ(processor->getOutput(1)->getValue()[0], 3);
}

TEST(ProcessorStandard, CalculateExtremeFormula){
    //x_0=8, x_i=x_{i-1}/2
    auto switchModule = regi.generateModule("ModuleSwitch");
    json config = switchModule->getConfiguration();
    config["configure"]["size"]["value"] = 2;
    switchModule->configure(config.dump());  

    auto calcModule = regi.generateModule("ModuleMath");
    config = calcModule->getConfiguration();
    config["configure"]["operation"]["value"] = "divide";
    calcModule->configure(config.dump());

    std::shared_ptr<ProcessorStandard> processor = std::make_shared<ProcessorStandard>();
    processor->addModule(switchModule);
    processor->addModule(calcModule);

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

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

    processor->getOutputParams().push_back(std::make_shared<Parameter>());
    processor->getOutputParams().at(0)->getInput(0)->connect(calcModule->getOutput(0));
    processor->update();

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

TEST(ProcessorStandard, CalculateAdvancedFormulaWithSTATIC) {
    //f4:(f3:(f1:(5*3)-f2:(4+5))/2)==(15-9)/2==6/2==3
    auto f1 = regi.generateModule("ModuleMath");
    json config = f1->getConfiguration();
    config["configure"]["operation"]["value"] = "multiply";
    f1->configure(config);
    f1->getInput(0)->setValue() = { 5 };
    f1->getInput(1)->setValue() = { 3 };

    auto f2 = regi.generateModule("ModuleMath");
    config = f2->getConfiguration();
    config["configure"]["operation"]["value"] = "add";
    f2->configure(config);
    f2->getInput(0)->setValue() = { 4 };
    f2->getInput(1)->setValue() = { 5 };

    auto f3 = regi.generateModule("ModuleMath");
    config = f3->getConfiguration();
    config["configure"]["operation"]["value"] = "subtract";
    f3->configure(config);
    f3->getInput(0)->connect(f1->getOutput(0));
    f3->getInput(1)->connect(f2->getOutput(0));

    auto f4 = regi.generateModule("ModuleMath");
    config = f4->getConfiguration();
    config["configure"]["operation"]["value"] = "divide";
    f4->configure(config);
    f4->getInput(0)->connect(f3->getOutput(0));
    f4->getInput(1)->setValue() = { 2 };

    auto processor = std::make_shared<ProcessorStandard>();
    config = processor->getConfiguration();
    config["configure"]["priority"]["value"] = "static";
    processor->configure(config);

    processor->addModule(f4);
    processor->addModule(f3);
    processor->addModule(f2);
    processor->addModule(f1);
    processor->getOutputParams().push_back(std::make_shared<Parameter>());
    processor->getOutputParams().at(0)->getInput(0)->connect(f4->getOutput(0));
    processor->update();
    //std::cout << test->getOutput(test->getOutputIDs()[1])->getValue()["value"].dump() << std::endl;
    EXPECT_EQ(processor->getOutput(1)->getValue()[0], 3);
}

TEST(ProcessorStandard, PrioritySTATIC) {
    //f4:(f3:(f1:(5*3)-f2:(4+5))/2)==(15-9)/2==6/2==3
    auto f1 = regi.generateModule("ModuleMath");
    json config = f1->getConfiguration();
    config["configure"]["operation"]["value"] = "multiply";
    f1->configure(config);
    f1->getInput(0)->setValue() = { 5 };
    f1->getInput(1)->setValue() = { 3 };

    auto f2 = regi.generateModule("ModuleMath");
    config = f2->getConfiguration();
    config["configure"]["operation"]["value"] = "add";
    f2->configure(config);
    f2->getInput(0)->setValue() = { 4 };
    f2->getInput(1)->setValue() = { 5 };

    auto f3 = regi.generateModule("ModuleMath");
    config = f3->getConfiguration();
    config["configure"]["operation"]["value"] = "subtract";
    f3->configure(config);
    f3->getInput(0)->connect(f1->getOutput(0));
    f3->getInput(1)->connect(f2->getOutput(0));

    auto f4 = regi.generateModule("ModuleMath");
    config = f4->getConfiguration();
    config["configure"]["operation"]["value"] = "divide";
    f4->configure(config);
    f4->getInput(0)->connect(f3->getOutput(0));
    f4->getInput(1)->setValue() = { 2 };

    auto process_manual = std::make_shared<ProcessorStandard>();
    auto process_static = std::make_shared<ProcessorStandard>();
    config = process_static->getConfiguration();
    config["configure"]["priority"]["value"] = "static";
    process_static->configure(config);

    process_manual->addModule(f1);
    process_manual->addModule(f2);
    process_manual->addModule(f3);
    process_manual->addModule(f4);
    process_manual->getOutputParams().push_back(std::make_shared<Parameter>());
    process_manual->getOutputParams().at(0)->getInput(0)->connect(f4->getOutput(0));
    process_manual->update();

    process_static->addModule(f4);
    process_static->addModule(f3);
    process_static->addModule(f2);
    process_static->addModule(f1);
    process_static->getOutputParams().push_back(std::make_shared<Parameter>());
    process_static->getOutputParams().at(0)->getInput(0)->connect(f4->getOutput(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]);
}
//*/
}