using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Xml;
using System.Xml.Linq;

namespace MotorSkill
{
    public class ExperimentConfig
    {
        private static string dateTimeStringFormat = "yyyy-MM-dd HH.mm.ss.fff";
        private static CultureInfo dateTimeCultureInfo = CultureInfo.CurrentCulture;
        public bool isRandomized { get; set; }
        public int trialsPerCondition { get; set; }
        public Subject[] subjects { get; set; }
        public Condition[] conditions { get; set; }
        public Trial[] trials { get; set; }

        public void loadTrials()
        {
            int conditionsNumber = conditions.Length;
            int trialsNumber = conditionsNumber * trialsPerCondition;
            trials = new Trial[conditionsNumber * trialsPerCondition];
            for (int i = 0; i < conditionsNumber; i++)
            {
                for (int j = 0; j < trialsPerCondition; j++)
                {
                    trials[i * trialsPerCondition + j] = new Trial();
                    trials[i * trialsPerCondition + j].condition = conditions[i];
                    trials[i * trialsPerCondition + j].trialNumberInCondition = j + 1;
                    trials[i * trialsPerCondition + j].state = Trial.EXPLANATORY;
                    trials[i * trialsPerCondition + j].repetitionCounter = 0;
                }
            }
            if (isRandomized)
            {
                Random random = new Random();
                for (int i = 0; i < trialsPerCondition; i++)
                {
                    // randomize conditions
                    for (int j = 0; j < conditionsNumber * 2; j++)
                    {
                        int first = random.Next(0, conditionsNumber);
                        int second = random.Next(0, conditionsNumber);
                        if (first != second)
                        {
                            Condition aux = conditions[first];
                            conditions[first] = conditions[second];
                            conditions[second] = aux;
                        }
                    }
                    // put conditions into the trials array
                    for (int j = 0; j < conditionsNumber; j++)
                    {
                        trials[conditionsNumber * i + j].condition = conditions[j];
                        trials[conditionsNumber * i + j].trialNumberInCondition = i + 1;
                    }
                }
            }
        }

        public static long getMsDateTime()
        {
            return DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
        }

        public static DateTime StringToDate(string stringDT)
        {
            DateTime dt;
            DateTime.TryParseExact (stringDT, dateTimeStringFormat, dateTimeCultureInfo, DateTimeStyles.None,out dt);
            return dt;
        }

        public static string DateToString(DateTime dt)
        {
            return dt.ToString(dateTimeStringFormat, dateTimeCultureInfo);
        }

        public static string convertObjectToXML(object dataFrame)
        {
            XmlElement element = (new XmlDocument()).CreateElement(dataFrame.GetType().Name);
            PropertyInfo[] properties = dataFrame.GetType().GetProperties();
            foreach (var property in properties)
            {
                string propertyValue = property.GetValue(dataFrame).ToString();
                if (property.PropertyType.ToString().Contains("Double"))
                {
                    propertyValue = (double.Parse(property.GetValue(dataFrame).ToString())).ToString("0.00000");
                }
                element.SetAttribute(property.Name, propertyValue);
            }
            return element.OuterXml;
        }

        public static object convertXMLToObject(string namespaceName, string lineXML)
        {
            // get class name
            var element = XElement.Parse(lineXML);
            string className = element.Name.LocalName;
            Type classType = Type.GetType(namespaceName + "." + className);

            // get fiels name and type
            FieldInfo[] fieldsInfo = classType
                .GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
            int fieldsNumber = fieldsInfo.Count();

            // get attributes from XML
            IEnumerable<XAttribute> attributes = element.Attributes();
            int attributesNumber = attributes.Count();

            // create constructor arguments array
            object[] args = new object[attributesNumber];
            for (int i = 0; i < fieldsNumber; i++)
            {
                TypeConverter typeConverter = TypeDescriptor.GetConverter(fieldsInfo[i].FieldType);
                args[i] = typeConverter.ConvertFromString(attributes.ElementAt(i).Value);
            }

            // create an instance of the object
            return Activator.CreateInstance(classType, args);
        }

        public static String convertXMLToCSV(string lineXML)
        {
            String lineCSV = "";
            var element = XElement.Parse(lineXML);
            IEnumerable<XAttribute> attributes = element.Attributes();
            int attributesNumber = attributes.Count();
            for (int i = 0; i < attributesNumber; i++)
            {
                lineCSV += attributes.ElementAt(i).Value+";";
            }
            return lineCSV;
        }
    }

    public class Subject
    {
        public string name { get; set; }
    }

    public class Condition
    {
        public string name { get; set; }
        public string image { get; set; }
        public string word { get; set; }
    }

    public class Trial
    {
        public static readonly int EXPLANATORY = 0;
        public static readonly int ACTIVE = 1;
        public static readonly int FINISHED = 2;

        public Condition condition { get; set; }
        public int trialNumberInCondition { get; set; }
        public int state { get; set; }
        public int repetitionCounter { get; set; }
    }

    public class Log
    {
        public string action { get; set; }
        public string value { get; set; }
        public long timestamp { get; set; }

        public Log(string action, string value, long timestamp)
        {
            this.action = action;
            this.value = value;
            this.timestamp = timestamp;
        }
    }
}
