#include "ModuleMath.h"
#include <iostream>
#include <cmath>

namespace mdd {
    ModuleMath::ModuleMath(MathOperation operation) {
        _operation = operation;
        std::vector<double> default_val = {1};
        addInput("Value", default_val);
        addInput("Value", default_val);
        addOutput("Value", default_val);
        setType("Math");
    }

    std::vector<double> ModuleMath::add(const std::vector<double>&val1, const std::vector<double>&val2) {
        std::vector<double> ret; //= json::array();
        size_t length= std::max(val1.size(), val2.size());
        for (int i = 0; i < length; i++) {
            if (val1.size() == val2.size()) {
                ret.push_back(val1[i] + val2[i]);
            }
            else if (1 == val1.size()) {
                ret.push_back(val1[0] + val2[i]);
            }
            else if (1 == val2.size()) {
                ret.push_back(val1[i] + val2[0]);
            }
        }
        return ret;
    }

    std::vector<double> ModuleMath::subtract(const std::vector<double>&val1, const std::vector<double>&val2){
        std::vector<double> ret; //= json::array();
        size_t length = std::max(val1.size(), val2.size());
        for (int i = 0; i < length; i++) {
            if (val1.size() == val2.size()) {
                ret.push_back(val1[i] - val2[i]);
            }
            else if (1 == val1.size()) {
                ret.push_back(val1[0] - val2[i]);
            }
            else if (1 == val2.size()) {
                ret.push_back(val1[i] - val2[0]);
            }
        }
        return ret;
    }

    std::vector<double> ModuleMath::multiply(const std::vector<double>&val1, const std::vector<double> &val2){
        std::vector<double> ret; //= json::array();
        size_t length = std::max(val1.size(), val2.size());
        for (int i = 0; i < length; i++) {
            if (val1.size() == val2.size()) {
                ret.push_back(val1[i] * val2[i]);
            }
            else if (1 == val1.size()) {
                ret.push_back(val1[0] * val2[i]);
            }
            else if (1 == val2.size()) {
                ret.push_back(val1[i] * val2[0]);
            }
        }
        return ret;
    }

    std::vector<double> ModuleMath::divide(const std::vector<double>&val1, const std::vector<double>&val2){
        std::vector<double> ret; //= json::array();
        size_t length = std::max(val1.size(), val2.size());
        for (int i = 0; i < length; i++) {
            if (val1.size() == val2.size()) {
                ret.push_back(val1[i] / val2[i]);
            }
            else if (1 == val1.size()) {
                ret.push_back(val1[0] / val2[i]);
            }
            else if (1 == val2.size()) {
                ret.push_back(val1[i] / val2[0]);
            }
        }
        return ret;
    }

    std::vector<double> ModuleMath::power(const std::vector<double>&val1, const std::vector<double>&val2){
        std::vector<double> ret; //= json::array();
        size_t length = std::max(val1.size(), val2.size());
        for (int i = 0; i < length; i++) {
            if (val1.size() == val2.size()) {
                ret.push_back(pow(val1[i] , val2[i]));
            }
            else if (1 == val1.size()) {
                ret.push_back(pow(val1[0] , val2[i]));
            }
            else if (1 == val2.size()) {
                ret.push_back(pow(val1[i] , val2[0]));
            }
        }
        return ret;
    }

    std::vector<double> ModuleMath::logarithm(const std::vector<double>&val1, const std::vector<double>&val2){
        std::vector<double> ret; //= json::array();
        size_t length = std::max(val1.size(), val2.size());
        for (int i = 0; i < length; i++) {
            if (val1.size() == val2.size()) {
                
                ret.push_back(log(val2[i]) / log(val1[i]));
            }
            else if (1 == val1.size()) {
                ret.push_back(log(val2[i]) / log(val1[0]));
            }
            else if (1 == val2.size()) {
                ret.push_back(log(val2[0]) / log(val1[i]));
            }
        }
        return ret;
    }

    std::vector<double> ModuleMath::minimum(const std::vector<double>&val1, const std::vector<double>&val2){
            std::vector<double> ret; //= json::array();
            size_t length = std::max(val1.size(), val2.size());
            for (int i = 0; i < length; i++) {
                if (val1.size() == val2.size()) {
                    ret.push_back(std::min(val1[i], val2[i]));
                }
                else if (1 == val1.size()) {
                    ret.push_back(std::min(val1[0], val2[i]));
                }
                else if (1 == val2.size()) {
                    ret.push_back(std::min(val1[i], val2[0]));
                }
            }
            return ret;
    }

    std::vector<double> ModuleMath::maximum(const std::vector<double>&val1, const std::vector<double>&val2){
        std::vector<double> ret; //= json::array();
        size_t length = std::max(val1.size(), val2.size());
        for (int i = 0; i < length; i++) {
            if (val1.size() == val2.size()) {
                ret.push_back(std::max(val1[i], val2[i]));
            }
            else if (1 == val1.size()) {
                ret.push_back(std::max(val1[0], val2[i]));
            }
            else if (1 == val2.size()) {
                ret.push_back(std::max(val1[i], val2[0]));
            }
        }
        return ret;
    }

    std::vector<double> ModuleMath::less(const std::vector<double>&val1, const std::vector<double>&val2){
        std::vector<double> ret; //= json::array();
        size_t length = std::max(val1.size(), val2.size());
        for (int i = 0; i < length; i++) {
            if (val1.size() == val2.size()) {
                ret.push_back(val1[i] > val2[i]);
            }
            else if (1 == val1.size()) {
                ret.push_back(val1[0] > val2[i]);
            }
            else if (1 == val2.size()) {
                ret.push_back(val1[i] > val2[0]);
            }
        }
        return ret;
    }

    std::vector<double> ModuleMath::greater(const std::vector<double>&val1, const std::vector<double>&val2){
        std::vector<double> ret; //= json::array();
        size_t length = std::max(val1.size(), val2.size());
        for (int i = 0; i < length; i++) {
            if (val1.size() == val2.size()) {
                ret.push_back(val1[i] < val2[i]);
            }
            else if (1 == val1.size()) {
                ret.push_back(val1[0] < val2[i]);
            }
            else if (1 == val2.size()) {
                ret.push_back(val1[i] < val2[0]);
            }
        }
        return ret;
    }

    state ModuleMath::update() {
        std::vector<double> ret = getOutput(0)->getValue();

        switch (_operation) {
        case MathOperation::ADD:
                ret = add(getInput(0)->getValue(), getInput(1)->getValue());
                break;

            case MathOperation::SUBTRACT:
                ret = subtract(getInput(0)->getValue(), getInput(1)->getValue());
                break;

            case MathOperation::MULTIPLY:
                ret = multiply(getInput(0)->getValue(), getInput(1)->getValue());
                break;

            case MathOperation::DIVIDE:
                ret = divide(getInput(0)->getValue(), getInput(1)->getValue());
                break;

            case MathOperation::POWER:
                ret = power(getInput(0)->getValue(), getInput(1)->getValue());
                break;

            case MathOperation::LOGARITHM:
                ret = logarithm(getInput(0)->getValue(), getInput(1)->getValue());
                break;

            case MathOperation::MINIMUM:
                ret = minimum(getInput(0)->getValue(), getInput(1)->getValue());
                break;

            case MathOperation::MAXIMUM:
                ret = maximum(getInput(0)->getValue(), getInput(1)->getValue());
                break;

            case MathOperation::LESS_THAN:
                ret = less(getInput(0)->getValue(), getInput(1)->getValue());
                break;

            case MathOperation::GREATER_THAN:
                ret = greater(getInput(0)->getValue(), getInput(1)->getValue());
                break;
        }
        
        return getOutput(0)->setValue(ret);
    }


    void ModuleMath::setMathOperation(MathOperation operation){
        _operation = operation;
    }

    MathOperation ModuleMath::getMathOperation(){
        return _operation;
    }
}

//gdb
//catch throw
//run