﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;
using System.Linq.Expressions;

namespace SoftMod.Framework
{
    public class TomeEnvironment : IOracleEnvironment
    {
        /// <summary>
        /// Environment knowledge tome, stores and retrieves information relevant to environment object
        /// </summary>

        /// <summary>
        /// Request list object, holds all requests for the specific instianted module, MCP, Environment etc
        /// </summary>
        IRequestList RequestList;
        /// <summary>
        /// Dictionary collection variable to hold all stored data / knowledge in environment tome that has been requested
        /// </summary>
        private Dictionary<string, Object[]> tomeData = new Dictionary<string, Object[]>();
        /// <summary>
        /// Constructor
        /// </summary>
        public TomeEnvironment()
        {
            // Initialise environment knowledge tomes request list
            RequestList = new RequestListEnv(this);
        }
        /// <summary>
        /// Create a delegate request
        /// </summary>
        public Delegate CreateDelegateRequest(string type, string methodName, RequestInfo reqInfo)
        {
            // Set type
            Type myType = Type.GetType(type);
            // Set method info
            MethodInfo myMethodInfo = myType.GetMethod(methodName); // may need to include param data incase we call overloaded method
            // Set arguments found in method
            List<Type> myMethodArgs = new List<Type>(myMethodInfo.GetParameters().Select(p => p.ParameterType));
            // Declare variable to hold our delegate type
            Type delegateType;
            // Using method info check whether our method is an action or function
            if (myMethodInfo.ReturnType == typeof(void))
            {
                // Method contains no return type
                delegateType = Expression.GetActionType(myMethodArgs.ToArray());
            }
            else
            {
                // Method contains a return type, add to method info
                myMethodArgs.Add(myMethodInfo.ReturnType);
                delegateType = Expression.GetFuncType(myMethodArgs.ToArray());
            }
            // Create a delegate of our method
            Delegate myDelegate = Delegate.CreateDelegate(delegateType, reqInfo.getReference(), myMethodInfo, true);
            // Return command
            return myDelegate;
        }
        /// <summary>
        /// Request information from environment tome
        /// </summary>
        public object RequestInformation(string requestName, RequestInfo reqInfo)
        {
            // Check information is stored in knowledge tome
            if (tomeData.ContainsKey(requestName))
            {
                // Information stored within environment knowledge tome, simply retrieve and pass to caller
                return tomeData[requestName].GetValue(1); // Pass second array value which is our data
            }
            else
            {
                // Execute request, look through request list, call request, invoke delegate and return data
                return InvokeRequest(RequestList.RequestInformation(requestName, reqInfo), reqInfo);
            }
        }
        /// <summary>
        /// Invoke request
        /// </summary>
        public object InvokeRequest(Delegate request, RequestInfo reqInfo)
        {
            // Define our arguments array
            object[] _args = reqInfo.getParams();
            // Invoke request info delegate
            return request.DynamicInvoke(_args);
        }
        /// <summary>
        /// Add data to environment tome
        /// </summary>
        public void AddData(string requestName, object[] data)
        {
            // Check information is stored in knowledge tome
            if (tomeData.ContainsKey(requestName))
            {
                // Information already stored within environment knowledge tome, check time stamp for data, if more recent then add otherwise throw error
                if ((float)tomeData[requestName].GetValue(0) < (float)data.GetValue(0)) // Remove float conversion to correct time stamp variable
                {
                    // Add data to tome, this includes both data and new time stamp
                    tomeData[requestName] = data;
                }
                else
                {
                    throw new ArgumentException("Added data is the same or older and therefore should not be added, check that it is new data and whether a correct time stamp is being added");
                }
            }
            else
            {
                // Information not present in tome, add to dictionary
                tomeData.Add(requestName, data);
            }
        }
    }
}
