using System;
using System.Runtime.InteropServices;
using ExcelDna.Integration;


namespace Excel2Ox
{
    class OxObject
    {
        public static void Object2OxVal(IntPtr Oxobj, object Obj)
        {
            if (Obj is int)
            {
                Ox.OxValSetInt(Oxobj, (int)Obj);
            }
            else if (Obj is double)
            {
                Ox.OxValSetDouble(Oxobj, (double)Obj);
            }
            else if (Obj is bool)
            {
				int ival = (bool)Obj ? 1 : 0;
				Ox.OxValSetInt(Oxobj, ival);
            }
            else if (Obj is string)
            {
                string s = Obj.ToString();
                if (s == "<>")
                    Ox.OxValSetMatZero(Oxobj, 0, 0);
                else if (s == "{}")
                    Ox.OxLibValArrayCalloc(Oxobj, 0);
                else if (s == ".NaN")
                {
                    double d = 0.0;
                    Ox.SetNaN(ref d);
                    Ox.OxValSetDouble(Oxobj, d);
                }
                else if (s == ".Inf" || s == "+.Inf")
                {
                    double d = 0.0;
                    Ox.SetInf(ref d);
                    Ox.OxValSetDouble(Oxobj, d);
                }
                else if (s == "-.Inf")
                {
                    double d = 0.0;
                    Ox.SetInf(ref d);
                    Ox.OxValSetDouble(Oxobj, -d);
                }
                else if (s == ".Null")
                    Ox.OxValSetNull(Oxobj);
                else
                    Ox.OxValSetString(Oxobj, s);
            }
            else if (Obj is object[,])
            {
                object[,] a = (object[,])Obj;
                int r = a.GetLength(0), c = a.GetLength(1);
                if (r == 0)
                {
                    Ox.OxValSetMatZero(Oxobj, 0, 0);
                    return;
                }
                else if (r == 1 && c == 1)
                {
                    Object2OxVal(Oxobj, a[0,0]);
                    return;
                }
                bool ismatrix = true;
                for (int i = 0; i < r; ++i)
                    for (int j = 0; j < c; ++j)
                    {
                        if (!(a[i, j] is int || a[i, j] is double))
                        {
                            ismatrix = false;
                            break;
                        }
                    }
                if (ismatrix)
                {
                    Ox.OxValSetMatZero(Oxobj, r, c);
                    double[] vec = new double[r * c];
                    for (int i = 0, k = 0; i < r; ++i)
                        for (int j = 0; j < c; ++j, ++k)
                            vec[k] = (double)a[i,j];
                    Ox.OxValSetVecr(Oxobj, vec, r, c);
                }
                else
                {
                    Ox.OxLibValArrayCalloc(Oxobj, r);
                    for (int i = 0; i < r; ++i)
                    {
                        IntPtr ai = Ox.OxValGetArrayVal(Oxobj, i);
                        Ox.OxLibValArrayCalloc(ai, c);
                        for (int j = 0; j < c; ++j)
                        {
                            if (!(a[i,j] is ExcelEmpty))
                                Object2OxVal(Ox.OxValGetArrayVal(ai, j), a[i,j]);
                        }
                    }
                }
            }
            else if (Obj is ExcelMissing || Obj is ExcelError)
            {
                double d = 0.0;
                Ox.SetNaN(ref d);
                Ox.OxValSetDouble(Oxobj, d);
            }
            else if (Obj is ExcelEmpty)
            {
                Ox.OxValSetString(Oxobj, "");
            }
        }
        public static object OxVal2Object(IntPtr Oxobj)
        {
            Ox.OxTypes type = (Ox.OxTypes)Ox.OxValType(Oxobj);
            
            switch (type)
            {
                case Ox.OxTypes.OX_INT:
                    {
                        int i = 0;
                        Ox.OxValGetInt(Oxobj, ref i);
                        return (object)i;
                    }
                case Ox.OxTypes.OX_DOUBLE:
                    {
                        double d = 0;
                        Ox.OxValGetDouble(Oxobj, ref d);
                        if (Ox.FIsNaN(d) != 0)
                            return ExcelError.ExcelErrorNA;
                        return (object)d;
                    }
                case Ox.OxTypes.OX_STRING:
                    {
                        // we want to copy the string, not steal it
                        string s = Marshal.PtrToStringAnsi(Ox.OxValGetString(Oxobj));
                        return (object)s;
                    }
                case Ox.OxTypes.OX_MATRIX:
                    {
                        int r = Ox.OxValGetMatr(Oxobj);
                        int c = Ox.OxValGetMatc(Oxobj);
                        if (r == 0 || c == 0)
                            return "<>";
                        double[] vx = new double[r * c];
                        Ox.OxValGetVecr(Oxobj, vx);
                        object[,] retval = new object[r, c];
                        for (int i = 0, k = 0; i < r; ++i)
                            for (int j = 0; j < c; ++j, ++k)
                                retval[i, j] = vx[k];
                        return retval;
                    }
                case Ox.OxTypes.OX_ARRAY:
                    {
                        int r = Ox.OxValGetArrayLen(Oxobj);
                        if (r == 0)
                        {
                            return "{}";

                        }
                        IntPtr a0 = Ox.OxValGetArrayVal(Oxobj, 0);
                        object[,] retval;
                        if (Ox.OxValHasType(a0, (int)Ox.OxTypes.OX_ARRAY) == 0)
                        {
                            retval = new object[r, 1];
                            for (int i = 0; i < r; ++i)
                            {
                                IntPtr ai = Ox.OxValGetArrayVal(Oxobj, i);
                                if (Ox.OxValHasFlag(ai, (int)Ox.OxTypes.OX_NULL) != 0)
                                    retval[0, i] = "";
                                else if (Ox.OxValHasType(ai, (int)Ox.OxTypes.OX_ARRAY) == 0)
                                    retval[0, i] = OxVal2Object(ai);
                            }
                        }
                        else
                        {
                            int c = Ox.OxValGetArrayLen(a0);
                            retval = new object[r, c];
                            for (int i = 0; i < r; ++i)
                            {
                                IntPtr ai = Ox.OxValGetArrayVal(Oxobj, i);
                                if (Ox.OxValHasType(ai, (int)Ox.OxTypes.OX_ARRAY) != 0)
                                {
                                    int ci = Ox.OxValGetArrayLen(ai);
                                    for (int j = 0; j < c && j < ci; ++j)
                                    {
                                        IntPtr aij = Ox.OxValGetArrayVal(ai, j);
                                        if (Ox.OxValHasFlag(aij, (int)Ox.OxTypes.OX_NULL) != 0)
                                            retval[i, j] = "";
                                        else if (Ox.OxValHasType(aij, (int)Ox.OxTypes.OX_ARRAY) == 0)
                                            retval[i, j] = OxVal2Object(aij);
                                    }
                                }
                            }
                        }
                        return retval;
                    }
                case Ox.OxTypes.OX_FUNCTION: return (object)"#Function";
                case Ox.OxTypes.OX_CLASS: return (object)"#Class";
                case Ox.OxTypes.OX_VECTOR: return (object)"#Vector";
                case Ox.OxTypes.OX_INTFUNC: return (object)"#Function";
                case Ox.OxTypes.OX_RANGE: return (object)"#Range";
                case Ox.OxTypes.OX_FILE: return (object)"#File";
                case Ox.OxTypes.OX_IMPORT: return (object)"#Import";
                case Ox.OxTypes.OX_LAMBDA: return (object)"#Function";
                case Ox.OxTypes.OX_BLOB: return (object)"#Blob";
            }
            return null;
        }
    }
}
