using System;
using System.Windows.Forms;
using ExcelDna.Integration;


namespace Excel2Ox
{
    class OxCallBack
    {
        // Do it this way rather than passing Ox_RunMessage etc. directly: otherwise the GC 
        // may decide that Ox_Puts is not needed and remove it, leading to an exception
        static unsafe string s_sError = "";
        static Ox.NewOxRunMessage s_fnOxRunMessage = Ox_RunMessage;
        static unsafe string s_sMessage = "";
        static Ox.NewOxMessage s_fnOxMessage = Ox_Message;

        private static unsafe void Ox_RunMessage(string s)
        {
            s_sError += s + "\n";
        }
        private static void ClearErrorMessage()
        {
            s_sError = "";
        }
        public static bool HaveErrorMessage()
        {
            return s_sError.Length > 0;
        }
        public static string GetErrorMessage()
        {
            return s_sError;
        }
        private static unsafe void Ox_Message(string s)
        {
            s_sMessage += s + "\n";
        }
        private static void ClearMessage()
        {
            s_sMessage = "";
        }
        public static bool HaveMessage()
        {
            return s_sMessage.Length > 0;
        }
        public static string GetMessage()
        {
            return s_sMessage;
        }
        public static int CountArgs(params object[] args)
        {
            int cargs;
            for (cargs = 0; cargs < args.Length; ++cargs)
            {
                if (args[cargs] is ExcelMissing || ((args[cargs] is object[,])
                        && ((object[,])args[cargs]).GetLength(0) == 1 && ((object[,])args[cargs]).GetLength(1) == 1
                        && ((object[,])args[cargs])[0, 0] is ExcelMissing))
                    break;
            }
            return cargs;
        }
        public static object OxCall(string sOxFile, string sFunc, params object[] args)
        {
            Ox.OxMainInit();
            Ox.SetOxRunMessage(s_fnOxRunMessage);
            ClearErrorMessage();
            Ox.SetOxMessage(s_fnOxMessage);
            ClearMessage();

            // compile the Ox file but don't run it
            if (Ox.OxMainCmd("-r- \"" + sOxFile + "\"") <= 1)
            {
                Console.WriteLine("Failed to start: " + sOxFile);
	            if (HaveMessage())
    	            MessageBox.Show(OxCallBack.GetMessage());
				
                return "#OxError: Failed to start: " + sOxFile;
            }
            else
            {
                Console.WriteLine("Started Ox program: " + sOxFile);
            }
            // check argument count: stop at first missing values
            int cargs = CountArgs(args);

            // environment for call; OxStoreCreate initializes to zero
            object retobj = 0;
            
                IntPtr funval = Ox.OxStoreCreate(1);
                IntPtr rtnval = Ox.OxStoreCreate(1);
                IntPtr argval = Ox.OxStoreCreate(1);
                Ox.OxLibValArrayCalloc(argval, cargs);

                OxObject.Object2OxVal(funval, sFunc);
                for (int i = 0; i < cargs; ++i)
                    OxObject.Object2OxVal(Ox.OxValGetArrayVal(argval, i), args[i]);

                if (Ox.FOxCallBack(funval, rtnval, Ox.OxValGetArrayVal(argval, 0), cargs) != 1)
                {
                    Console.WriteLine("Failed to call: " + sFunc);
                    retobj = "#OxError: Failed call to: " + sFunc;
                }
                else
                {
                    retobj = OxObject.OxVal2Object(rtnval);
                }
                Ox.OxStoreDelete(argval, 1);
                Ox.OxStoreDelete(rtnval, 1);
                Ox.OxStoreDelete(funval, 1);
            
            Ox.OxMainExit();

            return retobj;
        }
    }
}
