#ifdef WIN
#pragma warning (disable: 4018) // disable warning for comparing signed and unsigned
#endif /* WIN */

#include <dpstring.h>
#include <dpstringlist.h>
#include <fits.h>

#include "dpuserType.h"
#include "../dpuser_utils.h"
//#include "utils.h"
//#include "functions.h"

dpint64 numberOfdpuserTypes = 0;

int variableErrors[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

dpuserTypeException::dpuserTypeException(char *why) {
    cause = why;
}

const char* dpuserTypeException::reason() {
    return cause.c_str();
}

std::ostream& operator<< (std::ostream &out, const dpuserType &value) {
    switch (value.type) {
    case typeCon: out << value.lvalue; break;
    case typeDbl: out << value.dvalue; break;
    case typeCom: out << FormatComplexQString(*value.cvalue); break;
    case typeStr: out << *value.svalue; break;
    case typeFitsFile: out << "Fits file with name " << *value.ffvalue; break;
    case typeDpArr: for (auto el:(*value.dparrvalue)) out << *el;
        break;
    default: break;
    }
    return out;
}

dpuserType::dpuserType() {
    numberOfdpuserTypes++;
    initialize();
}

dpuserType::dpuserType(const dpuserType &v) {
    numberOfdpuserTypes++;
    copy(v);
}

dpuserType::dpuserType(const int &l) {
    initialize();
    type = typeCon;
    lvalue = l;
}

dpuserType::dpuserType(const char *s) {
    initialize();
    type = typeStr;
    svalue = CreateString(s);
}

dpuserType::dpuserType(const double &d) {
    initialize();
    type = typeDbl;
    dvalue = d;
}

void dpuserType::fromRI(const double &r, const double &i) {
    initialize();
    type = typeCom;
    cvalue = CreateComplex(r, i);
}

void dpuserType::initialize() {
    type = typeUnknown;
    variable = false;

    lvalue = 0;
    dvalue = 0.0;
    cvalue = NULL;
    svalue = NULL;
    arrvalue = NULL;
    fvalue = NULL;
    ffvalue = NULL;
    dparrvalue = NULL;
}

void dpuserType::copy(const dpuserType &value) {
    type = value.type;
    variable = value.variable;

    lvalue = value.lvalue;
    dvalue = value.dvalue;
    cvalue = value.cvalue;
    svalue = value.svalue;
    arrvalue = value.arrvalue;
    fvalue = value.fvalue;
    ffvalue = value.ffvalue;
    dparrvalue = value.dparrvalue;

/*
    initialize();
    type = value.type;
    switch (value.type) {
        case typeCon: lvalue = value.lvalue; break;
        case typeDbl: dvalue = value.dvalue; break;
        case typeFitsFile: ffvalue = value.ffvalue; break;
        case typeStr:
            svalue = CreateString(*value.svalue);
            break;
        case typeFits:
            fvalue = CreateFits();
            if (!fvalue->copy(*value.fvalue)) type = typeUnknown;
            break;
        case typeCom:
            cvalue = CreateComplex(*value.cvalue);
            break;
        case typeStrarr:
            arrvalue = CreateStringArray(*value.arrvalue);
            break;
//        case typeFitsarr:
//            dparrvalue = CreateDpList(*value.dparrvalue);
//            break;
        default: break;
    }
*/
}

bool dpuserType::deep_copy(const dpuserType &value) {
    initialize();
    type = value.type;
    variable = false;

    switch (value.type) {
        case typeCon: lvalue = value.lvalue; break;
        case typeDbl: dvalue = value.dvalue; break;
        case typeFitsFile:
            ffvalue = new dpString(*value.ffvalue);
            break;
        case typeStr:
            svalue = new dpString(*value.svalue);
            break;
        case typeFits:
            fvalue = new Fits();
            if (!fvalue->copy(*value.fvalue)) type = typeUnknown;
            break;
        case typeCom:
            cvalue = new dpComplex(*value.cvalue);
            break;
        case typeStrarr:
            arrvalue = new dpStringList(*value.arrvalue);
            break;
        case typeDpArr:
            dparrvalue = new dpuserTypeList(*value.dparrvalue);
            break;
        default:
            type = typeUnknown;
            break;
    }
    if (type == typeUnknown) return FALSE;
    return TRUE;
}

void dpuserType::clone(const dpuserType &value) {
//    copy(value);

    initialize();
    type = value.type;
    variable = false;
    switch (value.type) {
        case typeCon: lvalue = value.lvalue; break;
        case typeDbl: dvalue = value.dvalue; break;
        case typeFitsFile: ffvalue = value.ffvalue; break;
        case typeStr:
            if (isVariable(value.svalue)) {
                if (!value.variable) variableErrors[0]++;
//                if (!value.variable) dp_output("VARIABLE MISMATCH - svalue is variable, but dpuserType says no\n\n\n");
                svalue = CreateString(*value.svalue);
            } else {
                if (value.variable) variableErrors[1]++;
//                if (value.variable) dp_output("\n\n\nVARIABLE MISMATCH - svalue is no variable, but dpuserType says yes\n\n\n");
                svalue = value.svalue;
            }
            break;
        case typeFits:
            if (isVariable(value.fvalue) == 1) {
                if (!value.variable) variableErrors[2]++;
//                if (!value.variable) dp_output("VARIABLE MISMATCH - fvalue is variable, but dpuserType says no\n\n\n");
                fvalue = CreateFits();
                if (!fvalue->copy(*value.fvalue)) type = typeUnknown;
            } else {
                if (value.variable) variableErrors[3]++;
//                if (value.variable) dp_output("\n\n\nVARIABLE MISMATCH - fvalue is no variable, but dpuserType says yes\n\n\n");
                fvalue = value.fvalue;
            }
            break;
        case typeCom:
            if (isVariable(value.cvalue)) {
                if (!value.variable) variableErrors[4]++;
//                if (!value.variable) dp_output("VARIABLE MISMATCH - cvalue is variable, but dpuserType says no\n\n\n");
                cvalue = CreateComplex(*value.cvalue);
            } else {
                if (value.variable) variableErrors[5]++;
//                if (value.variable) dp_output("\n\n\nVARIABLE MISMATCH - cvalue is no variable, but dpuserType says yes\n\n\n");
                cvalue = value.cvalue;
            }
            break;
        case typeStrarr:
            if (isVariable(value.arrvalue)) {
                if (!value.variable) variableErrors[6]++;
//                if (!value.variable) dp_output("VARIABLE MISMATCH - arrvalue is variable, but dpuserType says no\n\n\n");
                arrvalue = CreateStringArray(*value.arrvalue);
            } else {
                if (value.variable) variableErrors[7]++;
//                if (value.variable) dp_output("\n\n\nVARIABLE MISMATCH - arrvalue is no variable, but dpuserType says yes\n\n\n");
                arrvalue = value.arrvalue;
            }
            break;
        case typeDpArr:
            if (isVariable(value.dparrvalue)) {
                if (!value.variable) variableErrors[8]++;
//                if (!value.variable) dp_output("VARIABLE MISMATCH - dparrvalue is variable, but dpuserType says no\n\n\n");
                dparrvalue = new dpuserTypeList(*value.dparrvalue);
            } else {
                if (value.variable) variableErrors[9]++;
//                if (value.variable) dp_output("\n\n\nVARIABLE MISMATCH - dparrvalue is no variable, but dpuserType says yes\n\n\n");
                dparrvalue = value.dparrvalue;
            }
            break;
            default: break;
    }
}

double dpuserType::toDouble() const {
    switch (type) {
        case typeCon: return (double)lvalue; break;
        case typeDbl: return dvalue; break;
        default: return 0.0;
    }
    return 0.0;
}

float dpuserType::toFloat() {
    switch (type) {
        case typeCon: return (float)lvalue; break;
        case typeDbl: return (float)dvalue; break;
        default: return 0.0;
    }
    return 0.0;
}

long dpuserType::toInt() const {
    switch (type) {
        case typeCon: return lvalue; break;
        case typeDbl: return (long)dvalue; break;
        default: return 0;
    }
    return 0;
}

double dpuserType::toReal() const {
    switch (type) {
        case typeCon: return (double)lvalue; break;
        case typeDbl: return dvalue; break;
        case typeCom: return cvalue->real(); break;
        default: return 0.0;
    }
    return 0.0;
}

double dpuserType::toImag() const {
    switch (type) {
        case typeCom: return cvalue->imag(); break;
        default: return 0.0;
    }
    return 0.0;
}

dpString dpuserType::toString() {
    if (type == typeStr) return *svalue;
    else return dpString("");
}

const char *dpuserType::c_str() const {
    if (type == typeStr) return svalue->c_str();
    else return NULL;
}

bool dpuserType::isReal() const {
    return (type == typeCon || type == typeDbl);
}

bool dpuserType::isInt() const {
    if (type == typeCon) return TRUE;
    if (type == typeDbl && dvalue == (int)dvalue) return TRUE;
    return FALSE;
}

dpuserType &dpuserType::operator =(const int &i) {
    type = typeCon;
    lvalue = i;
    variable = false;
    return *this;
}

dpuserType &dpuserType::operator =(const long &l) {
    type = typeCon;
    lvalue = l;
    variable = false;
    return *this;
}

dpuserType &dpuserType::operator =(const double &d) {
    type = typeDbl;
    dvalue = d;
    variable = false;
    return *this;
}

dpuserType &dpuserType::operator =(const dpComplex &c) {
    type = typeCom;
    cvalue = CreateComplex(c);
    variable = false;
    return *this;
}

dpuserType &dpuserType::operator =(const Fits &f) {
    type = typeFits;
    fvalue = CreateFits();
    fvalue->copy(f);
    variable = false;
    return *this;
}

dpuserType &dpuserType::operator =(const dpString &s) {
    type = typeStr;
    svalue = CreateString(s);
    variable = false;
    return *this;
}

dpuserType &dpuserType::operator =(const dpStringList &a) {
    type = typeStr;
    arrvalue = CreateStringArray(a);
    variable = false;
    return *this;
}

dpuserType &dpuserType::operator =(const dpuserType &a) {
    copy(a);
    return *this;
}

dpuserType dpuserType::operator -() {
    dpuserType result;
    result.clone(*this);

    switch (type) {
        case typeCon:
            result.lvalue *= -1;
            break;
        case typeDbl:
            result.dvalue *= -1.0;
            break;
        case typeCom:
            *result.cvalue *= -1.0;
            break;
        case typeFitsFile:
            result.type = typeFits;
            result.fvalue = CreateFits();
            if (!result.fvalue->ReadFITS(ffvalue->c_str())) {
//				success = 0;
                break;
            }
        case typeFits:
            *result.fvalue *= -1.0; break;
    }
    return result;
}

dpuserType dpuserType::operator ++(int) {
    dpuserType result(*this);

    switch (type) {
        case typeCon: lvalue++; break;
        case typeDbl: dvalue++; break;
        case typeCom: *cvalue += 1.0; break;
        case typeFits: *fvalue += 1.0; break;
        default: throw dpuserTypeException("Cannot increment this type of variable\n"); break;
    }

    return result;
}

dpuserType &dpuserType::operator ++() {
    switch (type) {
        case typeCon: lvalue++; break;
        case typeDbl: dvalue++; break;
        case typeCom: *cvalue += 1.0; break;
        case typeFits: {
            *fvalue += 1.0;
        }
        break;
        default: throw dpuserTypeException("Cannot increment this type of variable\n"); break;
    }

    return *this;
}

dpuserType dpuserType::operator --(int) {
    dpuserType result(*this);

    switch (type) {
        case typeCon: lvalue--; break;
        case typeDbl: dvalue--; break;
        case typeCom: *cvalue -= 1.0; break;
        case typeFits: *fvalue -= 1.0; break;
        default: throw dpuserTypeException("Cannot decrement this type of variable\n"); break;
    }

    return result;
}

dpuserType &dpuserType::operator --() {
    switch (type) {
        case typeCon: lvalue--; break;
        case typeDbl: dvalue--; break;
        case typeCom: *cvalue -= 1.0; break;
        case typeFits: {
            *fvalue -= 1.0;
        }
        break;
        default: throw dpuserTypeException("Cannot decrement this type of variable\n"); break;
    }

    return *this;
}

dpuserType &dpuserType::operator +=(const dpuserType &arg) {
    int n;
    double d;
    dpString *s;

    switch (type) {
        case typeCon: switch(arg.type) {
            case typeCon:
                type = typeCon;
                lvalue = lvalue + arg.lvalue;
                break;
            case typeDbl:
                type = typeDbl;
                dvalue = lvalue + arg.dvalue;
                break;
            case typeCom:
                d = (double)lvalue;
                copy(arg);
                *cvalue += d;
                break;
            case typeStr:
                type = typeStr;
                svalue = CreateString(lvalue);
                *svalue += *arg.svalue;
                break;
            case typeFits:
                d = (double)lvalue;
                copy(arg);
                *fvalue += d;
                break;
            case typeFitsFile:
                type = typeFits;
                fvalue = CreateFits();
                if (!fvalue->ReadFITS(arg.ffvalue->c_str())) type = typeUnknown;
                else *fvalue += (double)arg.lvalue;
                break;
            case typeStrarr:
                throw dpuserTypeException("Cannot add a string array to a number\n");
                break;
            default:
                throw dpuserTypeException("The arguments do not match\n");
                break;
        }
    break;

        case typeDbl: switch(arg.type) {
            case typeCon:
                type = typeDbl;
                dvalue = dvalue + arg.lvalue;
                break;
            case typeDbl:
                type = typeDbl;
                dvalue = dvalue + arg.dvalue;
                break;
            case typeCom:
                d = dvalue;
                copy(arg);
                *cvalue += d;
                break;
            case typeStr:
                type = typeStr;
                svalue = CreateString(dvalue);
                *svalue += *arg.svalue;
                break;
            case typeFits:
                d = dvalue;
                copy(arg);
                *fvalue += d;
                break;
            case typeFitsFile:
                type = typeFits;
                fvalue = CreateFits();
                if (!fvalue->ReadFITS(arg.ffvalue->c_str())) type = typeUnknown;
                else *fvalue += dvalue;
                break;
            case typeStrarr:
                throw dpuserTypeException("Cannot add a string array to a real number\n");
                break;
            default:
                throw dpuserTypeException("Cannot add this type to a real number\n");
                break;
        }
    break;

        case typeCom: switch(arg.type) {
            case typeCon:
                *cvalue += arg.lvalue;
                break;
            case typeDbl:
                *cvalue += arg.dvalue;
                break;
            case typeCom:
                *cvalue += *arg.cvalue;
                break;
            case typeStr:
                type = typeStr;
                svalue = CreateString(*cvalue);
                *svalue += *arg.svalue;
                break;
            case typeFits:
//					CloneValue(*arg2, result);
//					*result.fvalue += *arg1->cvalue;
                throw dpuserTypeException("Cannot add a matrix to a complex number\n");
                break;
            case typeFitsFile:
//					result.type = typeFits;
//					result.fvalue = new Fits();
//					result.fvalue->ReadFITS(arg2->ffvalue->c_str());
//					*result.fvalue += *arg1->cvalue;
                throw dpuserTypeException("Cannot add a FITS file to a complex number\n");
                break;
            case typeStrarr:
                throw dpuserTypeException("Cannot add a string array to a complex number\n");
                break;
            default:
                throw dpuserTypeException("Cannot add this type to a complex number\n");
                break;
        }
    break;

        case typeStr: switch(arg.type) {
            case typeCon:
                *svalue += *CreateString(arg.lvalue);
                break;
            case typeDbl:
                *svalue += *CreateString(arg.dvalue);
                break;
            case typeCom:
                *svalue += FormatComplexQString(*arg.cvalue);
                break;
            case typeStr:
                *svalue += *arg.svalue;
                break;
            case typeFits:
//					CloneValue(*arg2, result);
//					*result.fvalue += *arg1->cvalue;
                throw dpuserTypeException("Cannot add a matrix to a string\n");
                break;
            case typeFitsFile:
//					result.type = typeFits;
//					result.fvalue = new Fits();
//					result.fvalue->ReadFITS(arg2->ffvalue->c_str());
//					*result.fvalue += *arg1->cvalue;
                throw dpuserTypeException("Cannot add a FITS file to a string\n");
                break;
            case typeStrarr:
                s = svalue;
                copy(arg);
                arrvalue->prepend(*s);
                break;
            default:
                throw dpuserTypeException("Cannot add this type to a string\n");
                break;
        }
    break;

        case typeFits: switch(arg.type) {
            case typeCon:
                *fvalue += (double)arg.lvalue;
                break;
            case typeDbl:
                *fvalue += arg.dvalue;
                break;
            case typeCom:
                throw dpuserTypeException("Cannot add a complex number to a matrix\n");
//					CloneValue(*arg1, &result);
//					*result.fvalue += *arg2->cvalue;
                break;
            case typeStr:
                throw dpuserTypeException("Cannot add a string to a matrix\n");
                break;
            case typeFits:
                *fvalue += *arg.fvalue;
                break;
            case typeFitsFile: {
                Fits a;
                if (!a.ReadFITS(arg.ffvalue->c_str())) type = typeUnknown;
                else *fvalue += a;
            }
                break;
            case typeStrarr:
                throw dpuserTypeException("Cannot add a string array to a matrix\n");
                break;
            default:
                throw dpuserTypeException("Cannot add this type to a matrix\n");
                break;
        }
    break;

        case typeStrarr: switch(arg.type) {
            case typeCon:
                throw dpuserTypeException("Cannot add an integer number to a string array\n");
                break;
            case typeDbl:
                throw dpuserTypeException("Cannot add a real number to a string array\n");
                break;
            case typeCom:
                throw dpuserTypeException("Cannot add a complex number to a string array\n");
                break;
            case typeStr:
                arrvalue->append(*arg.svalue);
                break;
            case typeFits:
                throw dpuserTypeException("Cannot add a matrix to a string array\n");
                break;
            case typeFitsFile:
                throw dpuserTypeException("Cannot add a FITS file to a string array\n");
                break;
            case typeStrarr:
                for (n = 0; n < arg.arrvalue->count(); n++) arrvalue->append((*arg.arrvalue)[n]);
                break;
            default:
                throw dpuserTypeException("Cannot add this type to a string array\n");
                break;
        }
    break;

        default:
            throw dpuserTypeException("Invalid arguments to operator '+='\n");
            break;
    }
    return *this;
}

dpuserType &dpuserType::operator -=(const dpuserType &arg) {
    double d;
//	dpString *s;

    switch (type) {
        case typeCon: switch(arg.type) {
            case typeCon:
                type = typeCon;
                lvalue = lvalue - arg.lvalue;
                break;
            case typeDbl:
                type = typeDbl;
                dvalue = lvalue - arg.dvalue;
                break;
            case typeCom:
                d = (double)lvalue;
                copy(arg);
                *cvalue *= -1.;
                *cvalue += d;
                break;
            case typeStr:
                throw dpuserTypeException("Cannot subtract a string from an integer number\n");
                break;
            case typeFits:
                d = (double)lvalue;
                copy(arg);
                *fvalue *= -1.;
                *fvalue += d;
                break;
            case typeFitsFile:
                type = typeFits;
                fvalue = CreateFits();
                if (!fvalue->ReadFITS(arg.ffvalue->c_str())) type = typeUnknown;
                else {
                    *fvalue *= -1.;
                    *fvalue += (double)lvalue;
                }
                break;
            case typeStrarr:
                throw dpuserTypeException("Cannot subtract a string array from an integer number\n");
                break;
            default:
                throw dpuserTypeException("Cannot subtract this type from an integer number\n");
                break;
        }
            break;
        case typeDbl: switch(arg.type) {
            case typeCon:
                type = typeDbl;
                dvalue = dvalue - arg.lvalue;
                break;
            case typeDbl:
                type = typeDbl;
                dvalue = dvalue - arg.dvalue;
                break;
            case typeCom:
                d = dvalue;
                copy(arg);
                *cvalue *= -1.;
                *cvalue += d;
                break;
            case typeStr:
                throw dpuserTypeException("Cannot subtract a string from a real number\n");
                break;
            case typeFits:
                d = dvalue;
                copy(arg);
                *fvalue *= -1.;
                *fvalue += dvalue;
                break;
            case typeFitsFile:
                type = typeFits;
                fvalue = CreateFits();
                if (!fvalue->ReadFITS(arg.ffvalue->c_str())) type = typeUnknown;
                else {
                    *fvalue *= -1.;
                    *fvalue += dvalue;
                }
                break;
            case typeStrarr:
                throw dpuserTypeException("Cannot subtract a string array from a real number\n");
                break;
            default:
                throw dpuserTypeException("Cannot subtract this type from a real number\n");
                break;
        }
            break;
        case typeCom: switch(arg.type) {
            case typeCon:
                *cvalue -= arg.lvalue;
                break;
            case typeDbl:
                *cvalue -= arg.dvalue;
                break;
            case typeCom:
                *cvalue -= *arg.cvalue;
                break;
            case typeStr:
                throw dpuserTypeException("Cannot subtract a string from a complex number\n");
                break;
            case typeFits:
//					CloneValue(*arg2, result);
//					*result.fvalue += -*arg1->cvalue;
                throw dpuserTypeException("Cannot subtract a matrix from a complex number\n");
                break;
            case typeFitsFile:
//					result.type = typeFits;
//					result.fvalue = new Fits();
//					result.fvalue->ReadFITS(arg2->ffvalue->c_str());
//					*result.fvalue += -*arg1->cvalue;
                throw dpuserTypeException("Cannot subtract a FITS file from a complex number\n");
                break;
            case typeStrarr:
                throw dpuserTypeException("Cannot subtract a string array from a complex number\n");
                break;
            default:
                throw dpuserTypeException("Cannot subtract this type from a complex number\n");
                break;
        }
            break;
        case typeStr:
            throw dpuserTypeException("Cannot subtract anything from a string\n");
            break;
        case typeFits: switch(arg.type) {
            case typeCon:
                *fvalue -= (double)arg.lvalue;
                break;
            case typeDbl:
                *fvalue -= arg.dvalue;
                break;
            case typeCom:
                throw dpuserTypeException("Cannot subtract a complex number from a matrix\n");
//					CloneValue(*arg1, &result);
//					*result.fvalue -= *arg2->cvalue;
                break;
            case typeStr:
                throw dpuserTypeException("Cannot subtract a string from a matrix\n");
                break;
            case typeFits:
                *fvalue -= *arg.fvalue;
                break;
            case typeFitsFile: {
                Fits a;
                if (!a.ReadFITS(arg.ffvalue->c_str())) type = typeUnknown;
                else *fvalue -= a;
            }
                break;
            case typeStrarr:
                throw dpuserTypeException("Cannot subtract a string array from a matrix\n");
                break;
            default:
                throw dpuserTypeException("Cannot subtract this type from a matrix\n");
                break;
        }
            break;
        case typeStrarr:
            throw dpuserTypeException("Cannot subtract anything from a string array\n");
            break;
        default:
            throw dpuserTypeException("Invalid arguments to operator '-='\n");
            break;
    }

    return *this;
}

dpuserType &dpuserType::operator *=(const dpuserType &arg) {
    double d;

    switch (type) {
        case typeCon: switch(arg.type) {
            case typeCon:
                type = typeCon;
                lvalue = lvalue * arg.lvalue;
                break;
            case typeDbl:
                type = typeDbl;
                dvalue = lvalue * arg.dvalue;
                break;
            case typeCom:
                d = (double)lvalue;
                copy(arg);
                *cvalue *= d;
                break;
            case typeStr:
                throw dpuserTypeException("Cannot multiply a string to an integer number\n");
                break;
            case typeFits:
                d = (double)lvalue;
                copy(arg);
                *fvalue *= d;
                break;
            case typeFitsFile:
                type = typeFits;
                fvalue = CreateFits();
                if (!fvalue->ReadFITS(arg.ffvalue->c_str())) type = typeUnknown;
                else *fvalue *= (double)lvalue;
                break;
            case typeStrarr:
                throw dpuserTypeException("Cannot multiply a string array to an integer number\n");
                break;
            default:
                throw dpuserTypeException("Cannot multiply this type to an integer number\n");
                break;
        }
            break;
        case typeDbl: switch(arg.type) {
            case typeCon:
                type = typeDbl;
                dvalue = dvalue * arg.lvalue;
                break;
            case typeDbl:
                type = typeDbl;
                dvalue = dvalue * arg.dvalue;
                break;
            case typeCom:
                d = dvalue;
                copy(arg);
                *cvalue *= dvalue;
                break;
            case typeStr:
                throw dpuserTypeException("Cannot multiply a string to a real number\n");
                break;
            case typeFits:
                d = dvalue;
                copy(arg);
                *fvalue *= d;
                break;
            case typeFitsFile:
                type = typeFits;
                fvalue = CreateFits();
                if (!fvalue->ReadFITS(arg.ffvalue->c_str())) type = typeUnknown;
                else *fvalue *= dvalue;
                break;
            case typeStrarr:
                throw dpuserTypeException("Cannot multiply a string array to a real number\n");
                break;
            default:
                throw dpuserTypeException("Cannot multiply this type to a real number\n");
                break;
        }
            break;
        case typeCom: switch(arg.type) {
            case typeCon:
                *cvalue *= arg.lvalue;
                break;
            case typeDbl:
                *cvalue *= arg.dvalue;
                break;
            case typeCom:
                *cvalue *= *arg.cvalue;
                break;
            case typeStr:
                throw dpuserTypeException("Cannot multiply a string to a complex number\n");
                break;
            case typeFits:
//					CloneValue(*arg2, result);
//					*result.fvalue *= *arg1->cvalue;
                throw dpuserTypeException("Cannot multiply a matrix to a complex number\n");
                break;
            case typeFitsFile:
//					result.type = typeFits;
//					result.fvalue = new Fits();
//					result.fvalue->ReadFITS(arg2->ffvalue->c_str());
//					*result.fvalue *= *arg1->cvalue;
                throw dpuserTypeException("Cannot multiply a FITS file to a complex number\n");
                break;
            case typeStrarr:
                throw dpuserTypeException("Cannot multiply a string array to a complex number\n");
                break;
            default:
                throw dpuserTypeException("Cannot multiply this type to a complex number\n");
                break;
        }
            break;
        case typeStr:
            throw dpuserTypeException("Cannot multiply anything to a string\n");
            break;
        case typeFits: switch(arg.type) {
            case typeCon:
                *fvalue *= (double)arg.lvalue;
                break;
            case typeDbl:
                *fvalue *= arg.dvalue;
                break;
            case typeCom:
                throw dpuserTypeException("Cannot multiply a complex number to a matrix\n");
//					CloneValue(*arg1, &result);
//					*result.fvalue *= *arg2->cvalue;
                break;
            case typeStr:
                throw dpuserTypeException("Cannot multiply a string to a matrix\n");
                break;
            case typeFits:
                *fvalue *= *arg.fvalue;
                break;
            case typeFitsFile: {
                Fits a;
                if (!a.ReadFITS(arg.ffvalue->c_str())) type = typeUnknown;
                else *fvalue *= a;
            }
                break;
            case typeStrarr:
                throw dpuserTypeException("Cannot multiply a string array to a matrix\n");
                break;
            default:
                throw dpuserTypeException("Cannot multiply this type to a matrix\n");
                break;
        }
            break;
        case typeStrarr:
            throw dpuserTypeException("Cannot multiply anything to a string array\n");
            break;
        default:
            throw dpuserTypeException("Invalid arguments to operator '*='\n");
            break;
    }
    return *this;
}

dpuserType &dpuserType::operator /=(const dpuserType &arg) {
    double d;

    switch (type) {
        case typeCon: switch(arg.type) {
            case typeCon:
                type = typeDbl;
                dvalue = (double)lvalue / (double)arg.lvalue;
                break;
            case typeDbl:
                type = typeDbl;
                dvalue = lvalue / arg.dvalue;
                break;
            case typeCom:
                type = typeCom;
                cvalue = CreateComplex();
                *cvalue = lvalue / *arg.cvalue;
                break;
            case typeStr:
                throw dpuserTypeException("Cannot divide an integer number by a string\n");
                break;
            case typeFits:
                d = dvalue;
                copy(arg);
                fvalue->invert();
                *fvalue *= d;
                break;
            case typeFitsFile:
                type = typeFits;
                fvalue = CreateFits();
                if (!fvalue->ReadFITS(arg.ffvalue->c_str())) type = typeUnknown;
                else {
                    fvalue->invert();
                    *fvalue *= (double)lvalue;
                }
                break;
            case typeStrarr:
                throw dpuserTypeException("Cannot divide an integer number by a string array\n");
                break;
            default:
                throw dpuserTypeException("Cannot divide an integer number by this type\n");
                break;
        }
            break;
        case typeDbl: switch(arg.type) {
            case typeCon:
                type = typeDbl;
                dvalue = dvalue / arg.lvalue;
                break;
            case typeDbl:
                type = typeDbl;
                dvalue = dvalue / arg.dvalue;
                break;
            case typeCom:
                d = dvalue;
                copy(arg);
                *cvalue = d / *arg.cvalue;
                break;
            case typeStr:
                throw dpuserTypeException("Cannot divide a real number by a string\n");
                break;
            case typeFits:
                d = dvalue;
                copy(arg);
                fvalue->invert();
                *fvalue *= d;
                break;
            case typeFitsFile:
                type = typeFits;
                fvalue = CreateFits();
                if (!fvalue->ReadFITS(arg.ffvalue->c_str())) type = typeUnknown;
                else {
                    fvalue->invert();
                    *fvalue *= dvalue;
                }
                break;
            case typeStrarr:
                throw dpuserTypeException("Cannot divide a real number by a string array\n");
                break;
            default:
                throw dpuserTypeException("Cannot divide a real number by this type\n");
                break;
        }
            break;
        case typeCom: switch(arg.type) {
            case typeCon:
                *cvalue /= arg.lvalue;
                break;
            case typeDbl:
                *cvalue /= arg.dvalue;
                break;
            case typeCom:
                *cvalue /= *arg.cvalue;
                break;
            case typeStr:
                throw dpuserTypeException("Cannot divide a complex number by a string\n");
                break;
            case typeFits:
//					CloneValue(*arg2, result);
//					result.fvalue->invert();
//					*result.fvalue *= *arg1->cvalue;
                throw dpuserTypeException("Cannot divide a complex number by a matrix\n");
                break;
            case typeFitsFile:
//					result.type = typeFits;
//					result.fvalue = new Fits();
//					result.fvalue->ReadFITS(arg2->ffvalue->c_str());
//					*result.fvalue += -*arg1->cvalue;
                throw dpuserTypeException("Cannot divide a complex number by a FITS file\n");
                break;
            case typeStrarr:
                throw dpuserTypeException("Cannot divide a complex number by a string array\n");
                break;
            default:
                throw dpuserTypeException("Cannot divide a complex number by this type\n");
                break;
        }
            break;
        case typeStr:
            throw dpuserTypeException("Cannot divide a string by anything\n");
            break;
        case typeFits: switch(arg.type) {
            case typeCon:
                *fvalue /= (double)arg.lvalue;
                break;
            case typeDbl:
                *fvalue /= arg.dvalue;
                break;
            case typeCom:
                throw dpuserTypeException("Cannot divide a matrix by a complex number\n");
//					CloneValue(*arg1, &result);
//					*result.fvalue /= *arg2->cvalue;
                break;
            case typeStr:
                throw dpuserTypeException("Cannot divide a matrix by a string\n");
                break;
            case typeFits:
                *fvalue /= *arg.fvalue;
                break;
            case typeFitsFile: {
                Fits a;
                if (a.ReadFITS(arg.ffvalue->c_str())) type = typeUnknown;
                else *fvalue /= a;
            }
                break;
            case typeStrarr:
                throw dpuserTypeException("Cannot divide a matrix by a string array\n");
                break;
            default:
                throw dpuserTypeException("Cannot divide a matrix by this type\n");
                break;
        }
            break;
        case typeStrarr:
            throw dpuserTypeException("Cannot divide a string array by anything\n");
            break;
        default:
            throw dpuserTypeException("Invalid arguments to operator '/='\n");
            break;
    }
    return *this;
}

bool dpuserType::operator<(dpuserType arg) {
    switch (type) {
        case typeCon:
            switch (arg.type) {
                case typeCon:
                    return lvalue < arg.lvalue;
                    break;
                case typeDbl:
                    return lvalue < arg.dvalue;
                    break;
                default:
                    throw dpuserTypeException("Wrong arguments to < operator");
                    break;
            }

        case typeDbl:
            switch (arg.type) {
                case typeCon:
                    return dvalue < arg.lvalue;
                    break;
                case typeDbl:
                    return dvalue < arg.dvalue;
                    break;
                default:
                    throw dpuserTypeException("Wrong arguments to < operator");
                    break;
            }
        default:
            throw dpuserTypeException("Wrong arguments to < operator");
            break;
    }
    return FALSE;
}

bool dpuserType::operator<=(dpuserType arg) {
    switch (type) {
        case typeCon:
            switch (arg.type) {
                case typeCon:
                    return lvalue <= arg.lvalue;
                    break;
                case typeDbl:
                    return lvalue <= arg.dvalue;
                    break;
                default:
                    throw dpuserTypeException("Wrong arguments to <= operator");
                    break;
            }

        case typeDbl:
            switch (arg.type) {
                case typeCon:
                    return dvalue <= arg.lvalue;
                    break;
                case typeDbl:
                    return dvalue <= arg.dvalue;
                    break;
                default:
                    throw dpuserTypeException("Wrong arguments to <= operator");
                    break;
            }
        default:
            throw dpuserTypeException("Wrong arguments to <= operator");
            break;
    }
    return FALSE;
}

bool dpuserType::operator>(dpuserType arg) {
    switch (type) {
        case typeCon:
            switch (arg.type) {
                case typeCon:
                    return lvalue > arg.lvalue;
                    break;
                case typeDbl:
                    return lvalue > arg.dvalue;
                    break;
                default:
                    throw dpuserTypeException("Wrong arguments to > operator");
                    break;
            }

        case typeDbl:
            switch (arg.type) {
                case typeCon:
                    return dvalue > arg.lvalue;
                    break;
                case typeDbl:
                    return dvalue > arg.dvalue;
                    break;
                default:
                    throw dpuserTypeException("Wrong arguments to > operator");
                    break;
            }
        default:
            throw dpuserTypeException("Wrong arguments to > operator");
            break;
    }
    return FALSE;
}

bool dpuserType::operator>=(dpuserType arg) {
    switch (type) {
        case typeCon:
            switch (arg.type) {
                case typeCon:
                    return lvalue >= arg.lvalue;
                    break;
                case typeDbl:
                    return lvalue >= arg.dvalue;
                    break;
                default:
                    throw dpuserTypeException("Wrong arguments to > operator");
                    break;
            }

        case typeDbl:
            switch (arg.type) {
                case typeCon:
                    return dvalue >= arg.lvalue;
                    break;
                case typeDbl:
                    return dvalue >= arg.dvalue;
                    break;
                default:
                    throw dpuserTypeException("Wrong arguments to > operator");
                    break;
            }
        default:
            throw dpuserTypeException("Wrong arguments to > operator");
            break;
    }
    return FALSE;
}

bool dpuserType::operator==(dpuserType arg) {
    switch (type) {
        case typeCon:
            switch (arg.type) {
                case typeCon:
                    return lvalue == arg.lvalue;
                    break;
                case typeDbl:
                    return lvalue == arg.dvalue;
                    break;
                default:
                    throw dpuserTypeException("Wrong argument for '==' operator\n");
                    break;
            }

        case typeDbl:
            switch (arg.type) {
                case typeCon:
                    return dvalue == arg.lvalue;
                    break;
                case typeDbl:
                    return dvalue == arg.dvalue;
                    break;
                default:
                    throw dpuserTypeException("Wrong argument for '==' operator\n");
                    break;
            }
        default:
            throw dpuserTypeException("Wrong argument for '==' operator\n");
            break;
    }
    return FALSE;
}

bool dpuserType::operator!=(dpuserType arg) {
    switch (type) {
        case typeCon:
            switch (arg.type) {
                case typeCon:
                    return lvalue != arg.lvalue;
                    break;
                case typeDbl:
                    return lvalue != arg.dvalue;
                    break;
                default:
                    throw dpuserTypeException("Wrong argument for '!=' operator\n");
                    break;
            }

        case typeDbl:
            switch (arg.type) {
                case typeCon:
                    return dvalue != arg.lvalue;
                    break;
                case typeDbl:
                    return dvalue != arg.dvalue;
                    break;
                default:
                    throw dpuserTypeException("Wrong argument for '!=' operator\n");
                    break;
            }
        default:
            throw dpuserTypeException("Wrong argument for '!=' operator\n");
            break;
    }
    return FALSE;
}

const char *dpuserType::getFileName(void) const {
    if (type == typeFits) {
        return fvalue->getFileName();
    } else if (type == typeDpArr) {
        return dparrvalue->at(0)->getFileName();
    } else if (type == typeStrarr) {
        return arrvalue->getFileName();
    }
    return "";
}

const char* dpuserType::getColumnName() const {
    if (type == typeFits) {
        return fvalue->getColumnName();
    } else if (type == typeStrarr) {
        return arrvalue->getColumnName();
    }
    return NULL;
}

bool dpuserType::showAsTable() {
    if (type == typeFits) {
        switch (fvalue->extensionType) {
        case BINTABLE:
        case TABLE:
            return true;
            break;
        case UNKNOWN:
        case EMPTY:
        case IMAGE:
        case BINTABLEIMAGE:
        default:
            return false;
            break;
        }
    } else if (type == typeStrarr) {
        return true;
    } else if (type == typeDpArr) {
        bool ret = true;
        for (int i = 0; i < dparrvalue->size(); i++ ) {
            ret = ret && dparrvalue->at(i)->showAsTable();
            if (!ret) {
                break;
            }
        }
        return ret;
    } else {
        return false;
    }
}

// Read a FITS binary table. This is not part of the FITS class, since the returned value
// can either be a FITS or a String Array
dpuserType ReadFITSBinTable(const char *fname, dpuserType ext, dpuserType col) {
    dpuserType rv;

    if (ext.type != typeCon && ext.type != typeStr) return rv;
    if (col.type != typeCon && col.type != typeStr) return rv;

    try {
        int extension = 0;
        Fits _tmp;

        // check for valid extension
        if (ext.type == typeStr) {
            extension = _tmp.FindExtensionByName(fname, ext.toString().c_str());
        } else {
            extension = ext.toInt();
        }
        if (extension < 1) {
            throw dpuserTypeException("readfitsbintable: Invalid extension");
        }
        if (!_tmp.ReadFITSExtension(fname, extension)) {
            throw dpuserTypeException("readfitsbintable: Could not read extension");
        }

        // check for number of columns and which to load (or all)
        bool readList = false;
        int  count    = 0,
             column   = 0;
        if (col.type == typeStr) {
            column = _tmp.FindColumnByName(col.toString().c_str());
        } else {
            column = col.toInt();
            count = _tmp.ListTableColumns(fname, extension).size();
            if (count == -1) {
                throw dpuserTypeException("readfitsbintable: Unable to open file!");
            } else if (column > count) {
                throw dpuserTypeException("readfitsbintable: The specified extension does not exist!");
            } else if ((column == 0) && (count > 1)) {
                // read FITS table with extensions
                readList = true;
            }
            if ((column == 0) && (count == 1)) {
                // this is needed for reading complete GRAV-files containing single bintables in an extension
                column++;
            }
        }
        if (column < 0) {
            throw dpuserTypeException("readfitsbintable: Invalid column");
        }


        dpString name;
        name.sprintf("%s, ext #%d, col #%d", fname, extension, column);

        if (!readList) {
            // create single Fits or dpStringList
            nodeEnum type = _tmp.GetBintableType(column);
            if (type == typeFits) {
                // column contains values --> Fits
                rv.type = typeFits;
                rv.fvalue = CreateFits();
                rv.fvalue->setFileName(name.c_str());
                rv.fvalue->setExtensionNumber(extension);
                rv.fvalue->setColumnNumber(column);
                if (!_tmp.GetBintableColumn(column, *rv.fvalue)) {
                    throw dpuserTypeException("readfitsbintable: Could not read column!");
                }
            } else if (type == typeStrarr) {
                // column contains characters --> String array
                rv.type = typeStrarr;
                rv.arrvalue = CreateStringArray();
                rv.arrvalue->setFileName(name.c_str());
                if (!_tmp.GetBintableColumn(column, *rv.arrvalue)) {
                    throw dpuserTypeException("readfitsbintable: Could not read column!");
                }
            }
        } else {
            // create dpuserTypeList
            rv.type = typeDpArr;
            rv.dparrvalue = CreateDpList();

            for (int i = 1; i <= count; i++) {
                dpuserType *dpt = new dpuserType;
                dpt->type = _tmp.GetBintableType(i);
                if (dpt->type == typeFits) {
                    // column contains values --> Fits

                    dpt->fvalue = new Fits;
                    dpt->fvalue->setFileName(name.c_str());
                    dpt->fvalue->setExtensionNumber(extension);
                    dpt->fvalue->setColumnNumber(column);
                    if (i == 1) {
                        // doesn't work since gfd has to be rewinded first
                        // the same if has to be applied for tmpStrarr....
//                        dpt->fvalue->ReadFitsExtensionHeader(_tmp.gfd, extension);
                        // this should work if header wasn't private...
//                        dpt->fvalue->header = (char *)malloc(_tmp.HeaderLength * sizeof(char));
//                        dpt->fvalue->HeaderLength = _tmp.HeaderLength;
//                        strcpy(dpt->fvalue->header, _tmp.header);
                    }
                    if (_tmp.GetBintableColumn(i, *dpt->fvalue)) {
//                        dpt->fvalue->extensionType = BINTABLE;
                        rv.dparrvalue->push_back(dpt);
                    } else {
                        delete dpt;
                    }
                } else if (dpt->type == typeStrarr) {
                    // column contains characters --> String array
                    dpt->type = typeStrarr;
                    dpt->arrvalue = new dpStringList;
                    dpt->arrvalue->setFileName(name.c_str());

                    if (_tmp.GetBintableColumn(i, *dpt->arrvalue)) {
                        rv.dparrvalue->push_back(dpt);
                    } else {
                        delete dpt;
                    }
                } else {
                    delete dpt;
                }
            }
        }
    } catch (std::exception &e) {

    }

    return rv;
}

// ////////////////////////////////////////////////////////////////////////////////
// dpuserTypeList
// ////////////////////////////////////////////////////////////////////////////////

dpuserTypeList::dpuserTypeList(const dpuserTypeList &source) {
    int i;
    dpuserType *f;

    for (i = 0; i < source.size(); i++) {
        f = new dpuserType;
        if (f->deep_copy(*(source[i]))) {
            push_back(f);
        } else {
            delete f;
        }
    }
}

dpuserTypeList::~dpuserTypeList() {
    for (int i = 0; i < size(); i++) {
        delete (*this)[i];
    }

}

bool dpuserTypeList::ReadFITS(const dpString &fname) {
    dpuserType* f = new dpuserType;

    f->type = typeFits;
    f->fvalue = new Fits;

    bool rv = f->fvalue->ReadFITS(fname.c_str());

    if (rv) {
        push_back(f);

        Fits _tmp;
        int count = _tmp.CountExtensions(fname.c_str());
         _tmp.OpenFITS(fname.c_str());

        for (int i = 1; i <= count; i++) {
            if (_tmp.ReadFitsExtensionHeader(i, true) > 0) {
                _tmp.getHeaderInformation();
//                char keyValue[81];
//                if (_tmp.GetStringKey("EXTNAME", keyValue)) {
                    switch (_tmp.extensionType) {
                    case TABLE:
                    case BINTABLE: {
                        dpuserType rrv;
                        rrv = ReadFITSBinTable(fname.c_str(), i, 0);
                        switch (rrv.type) {
                           case typeFits: deleteFromListOfFits(rrv.fvalue);
                            break;
                        case typeStrarr: deleteFromListOfdpStringArrays(rrv.arrvalue);
                            break;
                        case typeDpArr: deleteFromListOfDpLists(rrv.dparrvalue);
                            break;
                        default:
                            break;
                        }

                        push_back(new dpuserType(rrv));
                        }
                        break;
                    default:
                        // IMAGE
                        f = new dpuserType;
                        f->type = typeFits;
                        f->fvalue = new Fits;

                        rv = f->fvalue->ReadFITSExtension(fname.c_str(), i);
                        if (rv) {
                            push_back(f);
                        } else {
                            delete f;
                        }
                        break;
                    }
//                }
            }
        }
    } else {
        delete f;
        return FALSE;
    }
    return TRUE;
}

bool dpuserTypeList::showAsTable() {
    bool ret = true;
    for (int i = 0; i < size(); i++) {
        ret &= at(i)->showAsTable();
    }
    return ret;
}
