#include <math.h>
//#include "gsl/gsl_math.h"
#include "fits.h"
//#include "dpuser_utils.h"
#include "dpuserAST.h"

dpuserType greaterThanNode::evaluate() {
    dpuserType leftvalue = left->evaluate();
    dpuserType rightvalue = right->evaluate();
    dpuserType result;

    switch (leftvalue.type) {
        case typeCon:
            leftvalue.dvalue = (double)leftvalue.lvalue;
        case typeDbl: switch(rightvalue.type) {
            case typeCon:
                result.type = typeCon;
                result.lvalue = leftvalue.dvalue > rightvalue.lvalue;
                break;
            case typeDbl:
                result.type = typeCon;
                result.lvalue = leftvalue.dvalue > rightvalue.dvalue;
                break;
            case typeCom:
                dp_output("Cannot apply '>' operator to a complex number\n");
                break;
            case typeStr:
                result.type = typeCon;
                result.lvalue = dpString::number(leftvalue.dvalue) > (*rightvalue.svalue);
                break;
            case typeFitsFile:
                if (!rightvalue.fvalue->ReadFITS(rightvalue.ffvalue->c_str())) break;
            case typeFits:
                result.type = typeFits;
                result.fvalue = CreateFits();
                result.fvalue->dpLT(*rightvalue.fvalue, leftvalue.dvalue);
                break;
            case typeStrarr:
                dp_output("Cannot compare a string array to a number\n");
                break;
            default:
                dp_output("invalid operands to '>'\n");
                break;
        }
            break;
        case typeCom:
            dp_output("Cannot apply '>' operator to a complex number\n");
            break;
        case typeStr: switch(rightvalue.type) {
            case typeCon:
                result.type = typeCon;
                result.lvalue = (*leftvalue.svalue) > dpString::number(rightvalue.lvalue);
                break;
            case typeDbl:
                result.type = typeCon;
                result.lvalue = (*leftvalue.svalue) > dpString::number(rightvalue.dvalue);
                break;
            case typeCom:
                dp_output("Cannot apply '>' operator to a complex number\n");
                break;
            case typeStr:
                result.type = typeCon;
                result.lvalue = (*leftvalue.svalue) > (*rightvalue.svalue);
                break;
            case typeFits:
            case typeFitsFile:
                dp_output("Cannot compare a String to a FITS\n");
                break;
            case typeStrarr:
                dp_output("Cannot compare a String to a String Array\n");
                break;
            default:
                dp_output("Cannot add this type to a string\n");
                break;
        }
            break;
        case typeFitsFile:
             if (!leftvalue.fvalue->ReadFITS(leftvalue.ffvalue->c_str())) break;
        case typeFits: switch(rightvalue.type) {
            case typeCon:
                rightvalue.dvalue = (double)rightvalue.lvalue;
            case typeDbl:
                result.type = typeFits;
                result.fvalue = CreateFits();
                result.fvalue->dpGT(*leftvalue.fvalue, rightvalue.dvalue);
                break;
            case typeCom:
                dp_output("Cannot compare a complex number to a FITS\n");
                break;
            case typeStr:
                dp_output("Cannot compare a String to a FITS\n");
                break;
            case typeFitsFile:
                if (!rightvalue.fvalue->ReadFITS(rightvalue.ffvalue->c_str())) break;
            case typeFits:
                result.type = typeFits;
                result.fvalue = CreateFits();
                result.fvalue->dpGT(*leftvalue.fvalue, *rightvalue.fvalue);
                break;
            case typeStrarr:
                dp_output("Cannot compare a String Array to a FITS\n");
                break;
            default:
                dp_output("Cannot compare this type to a matrix\n");
                break;
        }
            break;
        case typeStrarr:
            dp_output("Cannot compare a string array to anything\n");
            break;
        default:
            dp_output("Invalid arguments to operator '>'\n");
            break;
    }
    return result;
}

dpuserType lessThanNode::evaluate() {
    dpuserType leftvalue = left->evaluate();
    dpuserType rightvalue = right->evaluate();
    dpuserType result;

    switch (leftvalue.type) {
        case typeCon:
            leftvalue.dvalue = (double)leftvalue.lvalue;
        case typeDbl: switch(rightvalue.type) {
            case typeCon:
                result.type = typeCon;
                result.lvalue = leftvalue.dvalue < rightvalue.lvalue;
                break;
            case typeDbl:
                result.type = typeCon;
                result.lvalue = leftvalue.dvalue < rightvalue.dvalue;
                break;
            case typeCom:
                dp_output("Cannot apply '<' operator to a complex number\n");
                break;
            case typeStr:
                result.type = typeCon;
                result.lvalue = dpString::number(leftvalue.dvalue) < (*rightvalue.svalue);
                break;
            case typeFitsFile:
                if (!rightvalue.fvalue->ReadFITS(rightvalue.ffvalue->c_str())) break;
            case typeFits:
                result.type = typeFits;
                result.fvalue = CreateFits();
                result.fvalue->dpGT(*rightvalue.fvalue, leftvalue.dvalue);
                break;
            case typeStrarr:
                dp_output("Cannot compare a string array to a number\n");
                break;
            default:
                dp_output("invalid operands to '<'\n");
                break;
        }
            break;
        case typeCom:
            dp_output("Cannot apply '<' operator to a complex number\n");
            break;
        case typeStr: switch(rightvalue.type) {
            case typeCon:
                result.type = typeCon;
                result.lvalue = (*leftvalue.svalue) < dpString::number(rightvalue.lvalue);
                break;
            case typeDbl:
                result.type = typeCon;
                result.lvalue = (*leftvalue.svalue) < dpString::number(rightvalue.dvalue);
                break;
            case typeCom:
                dp_output("Cannot apply '<' operator to a complex number\n");
                break;
            case typeStr:
                result.type = typeCon;
                result.lvalue = (*leftvalue.svalue) < (*rightvalue.svalue);
                break;
            case typeFits:
            case typeFitsFile:
                dp_output("Cannot compare a String to a FITS\n");
                break;
            case typeStrarr:
                dp_output("Cannot compare a String to a String Array\n");
                break;
            default:
                dp_output("Cannot add this type to a string\n");
                break;
        }
            break;
        case typeFitsFile:
             if (!leftvalue.fvalue->ReadFITS(leftvalue.ffvalue->c_str())) break;
        case typeFits: switch(rightvalue.type) {
            case typeCon:
                rightvalue.dvalue = (double)rightvalue.lvalue;
            case typeDbl:
                result.type = typeFits;
                result.fvalue = CreateFits();
                result.fvalue->dpLT(*leftvalue.fvalue, rightvalue.dvalue);
                break;
            case typeCom:
                dp_output("Cannot compare a complex number to a FITS\n");
                break;
            case typeStr:
                dp_output("Cannot compare a String to a FITS\n");
                break;
            case typeFitsFile:
                if (!rightvalue.fvalue->ReadFITS(rightvalue.ffvalue->c_str())) break;
            case typeFits:
                result.type = typeFits;
                result.fvalue = CreateFits();
                result.fvalue->dpLT(*leftvalue.fvalue, *rightvalue.fvalue);
                break;
            case typeStrarr:
                dp_output("Cannot compare a String Array to a FITS\n");
                break;
            default:
                dp_output("Cannot compare this type to a matrix\n");
                break;
        }
            break;
        case typeStrarr:
            dp_output("Cannot compare a string array to anything\n");
            break;
        default:
            dp_output("Invalid arguments to operator '<'\n");
            break;
    }
    return result;
}

dpuserType greaterEqualNode::evaluate() {
    dpuserType leftvalue = left->evaluate();
    dpuserType rightvalue = right->evaluate();
    dpuserType result;

    switch (leftvalue.type) {
        case typeCon:
            leftvalue.dvalue = (double)leftvalue.lvalue;
        case typeDbl: switch(rightvalue.type) {
            case typeCon:
                result.type = typeCon;
                result.lvalue = leftvalue.dvalue >= rightvalue.lvalue;
                break;
            case typeDbl:
                result.type = typeCon;
                result.lvalue = leftvalue.dvalue >= rightvalue.dvalue;
                break;
            case typeCom:
                dp_output("Cannot apply '>=' operator to a complex number\n");
                break;
            case typeStr:
                result.type = typeCon;
                result.lvalue = dpString::number(leftvalue.dvalue) >= (*rightvalue.svalue);
                break;
            case typeFitsFile:
                if (!rightvalue.fvalue->ReadFITS(rightvalue.ffvalue->c_str())) break;
            case typeFits:
                result.type = typeFits;
                result.fvalue = CreateFits();
                result.fvalue->dpLE(*rightvalue.fvalue, leftvalue.dvalue);
                break;
            case typeStrarr:
                dp_output("Cannot compare a string array to a number\n");
                break;
            default:
                dp_output("invalid operands to '>='\n");
                break;
        }
            break;
        case typeCom:
            dp_output("Cannot apply '>=' operator to a complex number\n");
            break;
        case typeStr: switch(rightvalue.type) {
            case typeCon:
                result.type = typeCon;
                result.lvalue = (*leftvalue.svalue) >= dpString::number(rightvalue.lvalue);
                break;
            case typeDbl:
                result.type = typeCon;
                result.lvalue = (*leftvalue.svalue) >= dpString::number(rightvalue.dvalue);
                break;
            case typeCom:
                dp_output("Cannot apply '>=' operator to a complex number\n");
                break;
            case typeStr:
                result.type = typeCon;
                result.lvalue = (*leftvalue.svalue) >= (*rightvalue.svalue);
                break;
            case typeFits:
            case typeFitsFile:
                dp_output("Cannot compare a String to a FITS\n");
                break;
            case typeStrarr:
                dp_output("Cannot compare a String to a String Array\n");
                break;
            default:
                dp_output("Cannot add this type to a string\n");
                break;
        }
            break;
        case typeFitsFile:
             if (!leftvalue.fvalue->ReadFITS(leftvalue.ffvalue->c_str())) break;
        case typeFits: switch(rightvalue.type) {
            case typeCon:
                rightvalue.dvalue = (double)rightvalue.lvalue;
            case typeDbl:
                result.type = typeFits;
                result.fvalue = CreateFits();
                result.fvalue->dpGE(*leftvalue.fvalue, rightvalue.dvalue);
                break;
            case typeCom:
                dp_output("Cannot compare a complex number to a FITS\n");
                break;
            case typeStr:
                dp_output("Cannot compare a String to a FITS\n");
                break;
            case typeFitsFile:
                if (!rightvalue.fvalue->ReadFITS(rightvalue.ffvalue->c_str())) break;
            case typeFits:
                result.type = typeFits;
                result.fvalue = CreateFits();
                result.fvalue->dpGE(*leftvalue.fvalue, *rightvalue.fvalue);
                break;
            case typeStrarr:
                dp_output("Cannot compare a String Array to a FITS\n");
                break;
            default:
                dp_output("Cannot compare this type to a matrix\n");
                break;
        }
            break;
        case typeStrarr:
            dp_output("Cannot compare a string array to anything\n");
            break;
        default:
            dp_output("Invalid arguments to operator '>='\n");
            break;
    }
    return result;
}

dpuserType lessEqualNode::evaluate() {
    dpuserType leftvalue = left->evaluate();
    dpuserType rightvalue = right->evaluate();
    dpuserType result;

    switch (leftvalue.type) {
        case typeCon:
            leftvalue.dvalue = (double)leftvalue.lvalue;
        case typeDbl: switch(rightvalue.type) {
            case typeCon:
                result.type = typeCon;
                result.lvalue = leftvalue.dvalue <= rightvalue.lvalue;
                break;
            case typeDbl:
                result.type = typeCon;
                result.lvalue = leftvalue.dvalue <= rightvalue.dvalue;
                break;
            case typeCom:
                dp_output("Cannot apply '<=' operator to a complex number\n");
                break;
            case typeStr:
                result.type = typeCon;
                result.lvalue = dpString::number(leftvalue.dvalue) <= (*rightvalue.svalue);
                break;
            case typeFitsFile:
                if (!rightvalue.fvalue->ReadFITS(rightvalue.ffvalue->c_str())) break;
            case typeFits:
                result.type = typeFits;
                result.fvalue = CreateFits();
                result.fvalue->dpGE(*rightvalue.fvalue, leftvalue.dvalue);
                break;
            case typeStrarr:
                dp_output("Cannot compare a string array to a number\n");
                break;
            default:
                dp_output("invalid operands to '<='\n");
                break;
        }
            break;
        case typeCom:
            dp_output("Cannot apply '<=' operator to a complex number\n");
            break;
        case typeStr: switch(rightvalue.type) {
            case typeCon:
                result.type = typeCon;
                result.lvalue = (*leftvalue.svalue) <= dpString::number(rightvalue.lvalue);
                break;
            case typeDbl:
                result.type = typeCon;
                result.lvalue = (*leftvalue.svalue) <= dpString::number(rightvalue.dvalue);
                break;
            case typeCom:
                dp_output("Cannot apply '<=' operator to a complex number\n");
                break;
            case typeStr:
                result.type = typeCon;
                result.lvalue = (*leftvalue.svalue) <= (*rightvalue.svalue);
                break;
            case typeFits:
            case typeFitsFile:
                dp_output("Cannot compare a String to a FITS\n");
                break;
            case typeStrarr:
                dp_output("Cannot compare a String to a String Array\n");
                break;
            default:
                dp_output("Cannot add this type to a string\n");
                break;
        }
            break;
        case typeFitsFile:
             if (!leftvalue.fvalue->ReadFITS(leftvalue.ffvalue->c_str())) break;
        case typeFits: switch(rightvalue.type) {
            case typeCon:
                rightvalue.dvalue = (double)rightvalue.lvalue;
            case typeDbl:
                result.type = typeFits;
                result.fvalue = CreateFits();
                result.fvalue->dpLE(*leftvalue.fvalue, rightvalue.dvalue);
                break;
            case typeCom:
                dp_output("Cannot compare a complex number to a FITS\n");
                break;
            case typeStr:
                dp_output("Cannot compare a String to a FITS\n");
                break;
            case typeFitsFile:
                if (!rightvalue.fvalue->ReadFITS(rightvalue.ffvalue->c_str())) break;
            case typeFits:
                result.type = typeFits;
                result.fvalue = CreateFits();
                result.fvalue->dpLE(*leftvalue.fvalue, *rightvalue.fvalue);
                break;
            case typeStrarr:
                dp_output("Cannot compare a String Array to a FITS\n");
                break;
            default:
                dp_output("Cannot compare this type to a matrix\n");
                break;
        }
            break;
        case typeStrarr:
            dp_output("Cannot compare a string array to anything\n");
            break;
        default:
            dp_output("Invalid arguments to operator '<='\n");
            break;
    }
    return result;
}

dpuserType equalNode::evaluate() {
    dpuserType leftvalue = left->evaluate();
    dpuserType rightvalue = right->evaluate();
    dpuserType result;

    switch (leftvalue.type) {
        case typeCon: switch(rightvalue.type) {
            case typeCon:
                result.type = typeCon;
                result.lvalue = leftvalue.lvalue == rightvalue.lvalue;
                break;
            case typeDbl:
                result.type = typeCon;
                result.lvalue = leftvalue.lvalue == rightvalue.dvalue;
                break;
            case typeCom:
                result.type = typeCon;
                result.lvalue = (leftvalue.lvalue == rightvalue.cvalue->real() && (rightvalue.cvalue->imag() == 0.0));
                break;
            case typeStr:
                result.type = typeCon;
                result.lvalue = dpString::number(leftvalue.dvalue) == (*rightvalue.svalue);
                break;
            case typeFitsFile:
                if (!rightvalue.fvalue->ReadFITS(rightvalue.ffvalue->c_str())) break;
            case typeFits:
                result.type = typeFits;
                result.fvalue = CreateFits();
                result.fvalue->dpEQ(*rightvalue.fvalue, leftvalue.lvalue);
                break;
            case typeStrarr:
                dp_output("Cannot compare a string array to a number\n");
                break;
            default:
                dp_output("invalid operands to '=='\n");
                break;
        }
            break;
        case typeDbl: switch(rightvalue.type) {
            case typeCon:
                result.type = typeCon;
                result.lvalue = leftvalue.dvalue == rightvalue.lvalue;
                break;
            case typeDbl:
                result.type = typeCon;
                result.lvalue = leftvalue.dvalue == rightvalue.dvalue;
                break;
            case typeCom:
                result.type = typeCon;
                result.lvalue = (leftvalue.dvalue = rightvalue.cvalue->real() && (rightvalue.cvalue->imag() == 0.0));
                break;
            case typeStr:
                result.type = typeCon;
                result.lvalue = dpString::number(leftvalue.dvalue) == (*rightvalue.svalue);
                break;
            case typeFitsFile:
                if (!rightvalue.fvalue->ReadFITS(rightvalue.ffvalue->c_str())) break;
            case typeFits:
                result.type = typeFits;
                result.fvalue = CreateFits();
                result.fvalue->dpEQ(*rightvalue.fvalue, leftvalue.dvalue);
                break;
            case typeStrarr:
                dp_output("Cannot compare a string array to a number\n");
                break;
            default:
                dp_output("invalid operands to '=='\n");
                break;
        }
            break;
        case typeCom:
            switch(rightvalue.type) {
               case typeCon:
                   result.type = typeCon;
                   result.lvalue = (leftvalue.cvalue->real() == rightvalue.lvalue && (leftvalue.cvalue->imag() == 0.0));
                   break;
               case typeDbl:
                   result.type = typeCon;
                   result.lvalue = (leftvalue.cvalue->real() == rightvalue.dvalue && (leftvalue.cvalue->imag() == 0.0));
                   break;
               case typeCom:
                   result.type = typeCon;
                   result.lvalue = (*leftvalue.cvalue == *rightvalue.cvalue);
                   break;
               case typeStr:
                   dp_output("Cannot compare a string to a complex number\n");
                   break;
               case typeFitsFile:
               case typeFits:
                   dp_output("Cannot compare a FITS to a complex number\n");
                   break;
               case typeStrarr:
                   dp_output("Cannot compare a string array to a complex number\n");
                   break;
               default:
                   dp_output("invalid operands to '=='\n");
                   break;
           }
            break;
        case typeStr: switch(rightvalue.type) {
            case typeCon:
                result.type = typeCon;
                result.lvalue = (*leftvalue.svalue) == dpString::number(rightvalue.lvalue);
                break;
            case typeDbl:
                result.type = typeCon;
                result.lvalue = (*leftvalue.svalue) == dpString::number(rightvalue.dvalue);
                break;
            case typeCom:
                dp_output("Cannot compare a string to a complex number\n");
                break;
            case typeStr:
                result.type = typeCon;
                result.lvalue = (*leftvalue.svalue) == (*rightvalue.svalue);
                break;
            case typeFits:
            case typeFitsFile:
                dp_output("Cannot compare a String to a FITS\n");
                break;
            case typeStrarr:
                dp_output("Cannot compare a String to a String Array\n");
                break;
            default:
                dp_output("Cannot compare this type to a string\n");
                break;
        }
            break;
        case typeFitsFile:
             if (!leftvalue.fvalue->ReadFITS(leftvalue.ffvalue->c_str())) break;
        case typeFits: switch(rightvalue.type) {
            case typeCon:
                rightvalue.dvalue = (double)rightvalue.lvalue;
            case typeDbl:
                result.type = typeFits;
                result.fvalue = CreateFits();
                result.fvalue->dpEQ(*leftvalue.fvalue, rightvalue.dvalue);
                break;
            case typeCom:
                dp_output("Cannot compare a complex number to a FITS\n");
                break;
            case typeStr:
                dp_output("Cannot compare a String to a FITS\n");
                break;
            case typeFitsFile:
                if (!rightvalue.fvalue->ReadFITS(rightvalue.ffvalue->c_str())) break;
            case typeFits:
                result.type = typeFits;
                result.fvalue = CreateFits();
                result.fvalue->dpEQ(*leftvalue.fvalue, *rightvalue.fvalue);
                break;
            case typeStrarr:
                dp_output("Cannot compare a String Array to a FITS\n");
                break;
            default:
                dp_output("Cannot compare this type to a matrix\n");
                break;
        }
            break;
        case typeStrarr:
            dp_output("Cannot compare a string array to anything\n");
            break;
        default:
            dp_output("Invalid arguments to operator '=='\n");
            break;
    }
    return result;
}

dpuserType notEqualNode::evaluate() {

//    equalNode tmpNode(left, right);
    dpuserType result = equalNode::evaluate();

    if (result.type == typeFits) {
        result.fvalue->dpNOT();
    } else if (result.type == typeCon) {
        if (result.lvalue == 0) result.lvalue = 1;
        else result.lvalue = 0;
    }
    return result;
}

dpuserType notNode::evaluate() {
    dpuserType result = right->evaluate();

    if (result.type == typeCon) {
        if (result.lvalue == 0) result.lvalue = 1;
        else result.lvalue = 0;
    } else if (result.type == typeFits) {
        result.fvalue->dpNOT();
    }
    return result;
}

dpuserType andNode::evaluate() {
    dpuserType leftvalue = left->evaluate();
    dpuserType rightvalue = right->evaluate();
    dpuserType result;

    if (leftvalue.type == typeFits && rightvalue.type == typeFits) {
        result.type = typeFits;
        result.fvalue = CreateFits();
        result.fvalue->dpAND(*(leftvalue.fvalue), *(rightvalue.fvalue));
    } else if (leftvalue.type == typeCon && rightvalue.type == typeCon) {
        result.type = typeCon;
        result.lvalue = leftvalue.lvalue && rightvalue.lvalue;
    } else {
        dp_output("Invalid operands to operator '&&'\n");
    }

    return result;
}

dpuserType orNode::evaluate() {
    dpuserType leftvalue = left->evaluate();
    dpuserType rightvalue = right->evaluate();
    dpuserType result;

    if (leftvalue.type == typeFits && rightvalue.type == typeFits) {
        result.type = typeFits;
        result.fvalue = CreateFits();
        result.fvalue->dpOR(*(leftvalue.fvalue), *(rightvalue.fvalue));
    } else if (leftvalue.type == typeCon && rightvalue.type == typeCon) {
        result.type = typeCon;
        result.lvalue = leftvalue.lvalue || rightvalue.lvalue;
    } else {
        dp_output("Invalid operands to operator '||'\n");
    }

    return result;
}
