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

namespace mdd {
    ModuleMath::ModuleMath(MathOperation operation) {
        _operation = operation;
        json default_val;
        default_val["value"] = 1;
        addInput("Value", default_val);
        addInput("Value", default_val);
        addOutput("Value", default_val);
        setType("Math");
    }

    json ModuleMath::add(const json &val1, const json &val2) {
        json ret = json::array();
        if (val1.is_array()) {
            for (int i = 0; i < val1.size(); i++) {
                if (val2.is_array()) {
                    if (val1.size() == val2.size()) {
                        ret.push_back(add(val1[i],val2[i]));
                    }
                } else {
                    ret.push_back(add(val1[i],val2));
                }
            }

        } else {
            if (val2.is_array()) {
                for (int i = 0; i < val2.size(); i++) {
                    ret.push_back(add(val1,val2[i]));
                }
            } else {
                if (val1.is_number() && val2.is_number()) {
                    if (val1.is_number_float()) {
                        if (val2.is_number_float()) {
                            ret = val1.get<double>() + val2.get<double>();
                        } else {
                            ret = val1.get<float>() + val2.get<int>();
                        }
                    } else {
                        if (val2.is_number_float()) {
                            ret = val1.get<int>() + val2.get<double>();
                        } else {
                            ret = val1.get<int>() + val2.get<int>();
                        }
                    }

                } else {

                }

            }
        }
        return ret;
    }

    json ModuleMath::subtract(const json &val1, const json &val2){
        json ret = json::array();
        if (val1.is_array()) {
            for (int i = 0; i < val1.size(); i++) {
                if (val2.is_array()) {
                    if (val1.size() == val2.size()) {
                        ret.push_back(subtract(val1[i],val2[i]));
                    }
                } else {
                    ret.push_back(subtract(val1[i],val2));
                }
            }

        } else {
            if (val2.is_array()) {
                for (int i = 0; i < val2.size(); i++) {
                    ret.push_back(subtract(val1,val2[i]));
                }
            } else {
                if (val1.is_number() && val2.is_number()) {
                    if (val1.is_number_float()) {
                        if (val2.is_number_float()) {
                            ret = val1.get<double>() - val2.get<double>();
                        } else {
                            ret = val1.get<double>() - val2.get<int>();
                        }
                    } else {
                        if (val2.is_number_float()) {
                            ret = val1.get<int>() - val2.get<double>();
                        } else {
                            ret = val1.get<int>() - val2.get<int>();
                        }
                    }

                } else {

                }

            }
        }
        return ret;
    }

    json ModuleMath::multiply(const json &val1, const json &val2){
        json ret = json::array();
        if (val1.is_array()) {
            for (int i = 0; i < val1.size(); i++) {
                if (val2.is_array()) {
                    if (val1.size() == val2.size()) {
                        ret.push_back(multiply(val1[i],val2[i]));
                    }
                } else {
                    ret.push_back(multiply(val1[i],val2));
                }
            }

        } else {
            if (val2.is_array()) {
                for (int i = 0; i < val2.size(); i++) {
                    ret.push_back(multiply(val1,val2[i]));
                }
            } else {
                if (val1.is_number() && val2.is_number()) {
                    if (val1.is_number_float()) {
                        if (val2.is_number_float()) {
                            ret = val1.get<double>() * val2.get<double>();
                        } else {
                            ret = val1.get<double>() * val2.get<int>();
                        }
                    } else {
                        if (val2.is_number_float()) {
                            ret = val1.get<int>() * val2.get<float>();
                        } else {
                            ret = val1.get<int>() * val2.get<int>();
                        }
                    }

                } else {

                }

            }
        }
        return ret;
    }

    json ModuleMath::divide(const json &val1, const json &val2){
        json ret = json::array();
        if (val1.is_array()) {
            for (int i = 0; i < val1.size(); i++) {
                if (val2.is_array()) {
                    if (val1.size() == val2.size()) {
                        ret.push_back(divide(val1[i],val2[i]));
                    }
                } else {
                    ret.push_back(divide(val1[i],val2));
                }
            }

        } else {
            if (val2.is_array()) {
                for (int i = 0; i < val2.size(); i++) {
                    ret.push_back(divide(val1,val2[i]));
                }
            } else {
                if (val1.is_number() && val2.is_number()) {
                    if (val1.is_number_float()) {
                        if (val2.is_number_float()) {
                            ret = val1.get<double>() / val2.get<double>();
                        } else {
                            ret = val1.get<double>() / val2.get<int>();
                        }
                    } else {
                        if (val2.is_number_float()) {
                            ret = val1.get<int>() / val2.get<double>();
                        } else {
                            ret = val1.get<int>()*1.0 / val2.get<int>();
                        }
                    }

                } else {

                }

            }
        }
        return ret;
    }

    json ModuleMath::power(const json &val1, const json &val2){
        json ret = json::array();
        if (val1.is_array()) {
            for (int i = 0; i < val1.size(); i++) {
                if (val2.is_array()) {
                    if (val1.size() == val2.size()) {
                        ret.push_back(power(val1[i], val2[i]));
                    }
                }
                else {
                    ret.push_back(power(val1[i], val2));
                }
            }

        }
        else {
            if (val2.is_array()) {
                for (int i = 0; i < val2.size(); i++) {
                    ret.push_back(power(val1, val2[i]));
                }
            }
            else {
                if (val1.is_number() && val2.is_number()) {
                    if (val1.is_number_float()) {
                        if (val2.is_number_float()) {
                            ret = pow(val1.get<double>(), val2.get<double>());
                        }
                        else {
                            ret = pow(val1.get<double>(), val2.get<int>());
                        }
                    }
                    else {
                        if (val2.is_number_float()) {
                            ret = pow(val1.get<int>(), val2.get<double>());
                        }
                        else {
                            ret = pow(val1.get<int>(), val2.get<int>());
                        }
                    }

                }
                else {

                }

            }
        }
        return ret;
    }

    json ModuleMath::logarithm(const json &val1, const json &val2){
        json ret = json::array();
        if (val1.is_array()) {
            for (int i = 0; i < val1.size(); i++) {
                if (val2.is_array()) {
                    if (val1.size() == val2.size()) {
                        ret.push_back(logarithm(val1[i], val2[i]));
                    }
                }
                else {
                    ret.push_back(logarithm(val1[i], val2));
                }
            }

        }
        else {
            if (val2.is_array()) {
                for (int i = 0; i < val2.size(); i++) {
                    ret.push_back(logarithm(val1, val2[i]));
                }
            }
            else {
                if (val1.is_number() && val2.is_number()) {
                    if (val1.is_number_float()) {
                        if (val2.is_number_float()) {
                            ret = log(val2.get<double>())/log(val2.get<double>());
                        }
                        else {
                            ret = log(val2.get<int>()) / log(val1.get<double>());
                        }
                    }
                    else {
                        if (val2.is_number_float()) {
                            ret = log(val2.get<double>()) / log(val1.get<int>());
                        }
                        else {
                            ret = log(val2.get<int>()) / log(val1.get<int>());
                        }
                    }

                }
                else {

                }

            }
        }
        return ret;
    }

    json ModuleMath::minimum(const json &val1, const json &val2){
        json ret = json::array();
        if (val1.is_array()) {
            for (int i = 0; i < val1.size(); i++) {
                if (val2.is_array()) {
                    if (val1.size() == val2.size()) {
                        ret.push_back(minimum(val1[i],val2[i]));
                    }
                } else {
                    ret.push_back(minimum(val1[i],val2));
                }
            }

        } else {
            if (val2.is_array()) {
                for (int i = 0; i < val2.size(); i++) {
                    ret.push_back(minimum(val1,val2[i]));
                }
            } else {
                if (val1.is_number() && val2.is_number()) {
                    if (val1.is_number_float()) {
                        if (val2.is_number_float()) {
                            if(val1.get<double>()<= val2.get<double>()){
                                ret = val1.get<double>();
                            } else{
                                ret = val2.get<double>();
                            }
                        } else {
                            if(val1.get<double>()<= val2.get<int>()*1.0){
                                ret = val1.get<double>();
                            } else{
                                ret = val2.get<int>();
                            }
                        }
                    } else {
                        if (val2.is_number_float()) {
                            if(val1.get<int>()*1.0 <= val2.get<double>()){
                                ret = val1.get<int>();
                            } else{
                                ret = val2.get<double>();
                            }
                        } else {
                            if(val1.get<int>()<= val2.get<int>()){
                                ret = val1.get<int>();
                            } else{
                                ret = val2.get<int>();
                            }
                        }
                    }

                } else {

                }

            }
        }
        return ret;
    }

    json ModuleMath::maximum(const json &val1, const json &val2){
        json ret = json::array();
        if (val1.is_array()) {
            for (int i = 0; i < val1.size(); i++) {
                if (val2.is_array()) {
                    if (val1.size() == val2.size()) {
                        ret.push_back(minimum(val1[i],val2[i]));
                    }
                } else {
                    ret.push_back(minimum(val1[i],val2));
                }
            }

        } else {
            if (val2.is_array()) {
                for (int i = 0; i < val2.size(); i++) {
                    ret.push_back(minimum(val1,val2[i]));
                }
            } else {
                if (val1.is_number() && val2.is_number()) {
                    if (val1.is_number_float()) {
                        if (val2.is_number_float()) {
                            if(val1.get<double>()>= val2.get<double>()){
                                ret = val1.get<double>();
                            } else{
                                ret = val2.get<double>();
                            }
                        } else {
                            if(val1.get<float>()>= val2.get<int>()*1.0){
                                ret = val1.get<double>();
                            } else{
                                ret = val2.get<int>();
                            }
                        }
                    } else {
                        if (val2.is_number_float()) {
                            if(val1.get<int>()*1.0 >= val2.get<double>()){
                                ret = val1.get<int>();
                            } else{
                                ret = val2.get<double>();
                            }
                        } else {
                            if(val1.get<int>()>= val2.get<int>()){
                                ret = val1.get<int>();
                            } else{
                                ret = val2.get<int>();
                            }
                        }
                    }

                } else {

                }

            }
        }
        return ret;
    }

    json ModuleMath::less(const json &val1, const json &val2){
        json ret = json::array();
        if (val1.is_array()) {
            for (int i = 0; i < val1.size(); i++) {
                if (val2.is_array()) {
                    if (val1.size() == val2.size()) {
                        ret.push_back(less(val1[i], val2[i]));
                    }
                }
                else {
                    ret.push_back(less(val1[i], val2));
                }
            }

        }
        else {
            if (val2.is_array()) {
                for (int i = 0; i < val2.size(); i++) {
                    ret.push_back(less(val1, val2[i]));
                }
            }
            else {
                if (val1.is_number() && val2.is_number()) {
                    if (val1.is_number_float()) {
                        if (val2.is_number_float()) {
                            if (val1.get<double>() < val2.get<double>()) {
                                ret = 0;
                            }
                            else {
                                ret = 1;
                            }
                        }
                        else {
                            if (val1.get<float>() < val2.get<int>() * 1.0) {
                                ret = 0;
                            }
                            else {
                                ret = 1;
                            }
                        }
                    }
                    else {
                        if (val2.is_number_float()) {
                            if (val1.get<int>() * 1.0 < val2.get<double>()) {
                                ret = 0;
                            }
                            else {
                                ret = 1;
                            }
                        }
                        else {
                            if (val1.get<int>() < val2.get<int>()) {
                                ret = 0;
                            }
                            else {
                                ret = 1;
                            }
                        }
                    }

                }
                else {

                }

            }
        }
        return ret;
    }

    json ModuleMath::greater(const json &val1, const json &val2){
        json ret = json::array();
        if (val1.is_array()) {
            for (int i = 0; i < val1.size(); i++) {
                if (val2.is_array()) {
                    if (val1.size() == val2.size()) {
                        ret.push_back(greater(val1[i], val2[i]));
                    }
                }
                else {
                    ret.push_back(greater(val1[i], val2));
                }
            }

        }
        else {
            if (val2.is_array()) {
                for (int i = 0; i < val2.size(); i++) {
                    ret.push_back(greater(val1, val2[i]));
                }
            }
            else {
                if (val1.is_number() && val2.is_number()) {
                    if (val1.is_number_float()) {
                        if (val2.is_number_float()) {
                            if (val1.get<double>() > val2.get<double>()) {
                                ret = 0;
                            }
                            else {
                                ret = 1;
                            }
                        }
                        else {
                            if (val1.get<float>() > val2.get<int>() * 1.0) {
                                ret = 0;
                            }
                            else {
                                ret = 1;
                            }
                        }
                    }
                    else {
                        if (val2.is_number_float()) {
                            if (val1.get<int>() * 1.0 > val2.get<double>()) {
                                ret = 0;
                            }
                            else {
                                ret = 1;
                            }
                        }
                        else {
                            if (val1.get<int>() > val2.get<int>()) {
                                ret = 0;
                            }
                            else {
                                ret = 1;
                            }
                        }
                    }

                }
                else {

                }

            }
        }
        return ret;
    }

    bool ModuleMath::update() {
        json ret = getOutput(0)->getValue()["value"];

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

            case SUBTRACT:
                ret = subtract(getInput(0)->getValue()["value"], getInput(1)->getValue()["value"]);
                break;

            case MULTIPLY:
                ret = multiply(getInput(0)->getValue()["value"], getInput(1)->getValue()["value"]);
                break;

            case DIVIDE:
                ret = divide(getInput(0)->getValue()["value"], getInput(1)->getValue()["value"]);
                break;

            case POWER:
                ret = power(getInput(0)->getValue()["value"], getInput(1)->getValue()["value"]);
                break;

            case LOGARITHM:
                ret = logarithm(getInput(0)->getValue()["value"], getInput(1)->getValue()["value"]);
                break;

            case MINIMUM:
                ret = minimum(getInput(0)->getValue()["value"], getInput(1)->getValue()["value"]);
                break;

            case MAXIMUM:
                ret = maximum(getInput(0)->getValue()["value"], getInput(1)->getValue()["value"]);
                break;

            case LESS_THAN:
                ret = less(getInput(0)->getValue()["value"], getInput(1)->getValue()["value"]);
                break;

            case GREATER_THAN:
                ret = greater(getInput(0)->getValue()["value"], getInput(1)->getValue()["value"]);
                break;
        }

        if (ret.dump() != getOutput(0)->getValue()["value"].dump()) {
            getOutput(0)->getValueInternal()["value"] = ret ;
            return true;
        }
        else{
            return false;
        }
    }


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

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

//gdb
//catch throw
//run