using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Data;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Configuration.Install;
using SimioAPI;

namespace RunSimioSchedule
{
    class Program : ServiceBase
    {
        static ISimioProject _simioProject;
        static String _eventPathAndFile;
        static String _projectPathAndFile;
        static void Main(string[] args)
        {
            if (!Environment.UserInteractive)
            {
                // running as service
                using (var service = new Program())
                    ServiceBase.Run(service);
            }
            else
            {
                using (var service = new Program())
                {
                    // running as console app
                    service.OnStart(args);
                    Console.WriteLine("Hit any key to stop...");
                    do { } while (Console.ReadLine() != "q");
                    service.OnStop();
                }

            }
        }
        protected override void OnStart(string[] args)
        {
            ShutdownHost();
            SimioProjectFactory.SetExtensionsPath(System.AppDomain.CurrentDomain.BaseDirectory);
            // set project file
            _projectPathAndFile = Properties.Settings.Default.SimioModel;

            // Open project
            string[] warnings;
            try
            {
                // Set event file.
                _eventPathAndFile = Properties.Settings.Default.EventFile;

                // If File Not Exist, Throw Exeption
                if (File.Exists(_projectPathAndFile) == false) throw new Exception("Project Not Found : " + _projectPathAndFile);

                // Open project file.
                _simioProject = SimioProjectFactory.LoadProject(_projectPathAndFile, out warnings);

                // Run schedule and save for existing events.
                runScheduleExportResultsAndSaveProject();
                saveProject();
            }
            catch (Exception ex)
            {
                logStatus(ex.Message);
            }
            

            FileSystemWatcher watcher = new FileSystemWatcher();
            watcher.Path = Path.GetDirectoryName(_eventPathAndFile);
            watcher.Filter = Path.GetFileName(_eventPathAndFile);
            watcher.Created += watcher_Created;
            watcher.Changed += watcher_Changed;
            watcher.EnableRaisingEvents = true;
            base.OnStart(args);
        }

        private void ShutdownHost()
        {

        }

        protected override void OnStop()
        {
            ShutdownHost();
            base.OnStop();
        }

        static void watcher_Changed(object sender, FileSystemEventArgs e)
        {
            try
            {
                runScheduleExportResultsAndSaveProject();
            }
            catch (Exception ex)
            {
                logStatus(ex.Message);
            }
        }

        static void watcher_Created(object sender, FileSystemEventArgs e)
        {
            try
            {
                runScheduleExportResultsAndSaveProject();
            }
            catch (Exception ex)
            {
                logStatus(ex.Message);
            }
        }

        static void runScheduleExportResultsAndSaveProject()
        {
            if (System.IO.File.Exists(_eventPathAndFile))
            {
                var exceptionsTable = new DataTable();
                try
                {
                    exceptionsTable = ReadCsvFileIntoDataTable(_eventPathAndFile);
                }
                catch { }

                File.Delete(_eventPathAndFile);

                var model = _simioProject.Models["Model"];
                if (model == null)
                {
                    logStatus("Model Not Found In Project");
                }
                else
                {                   
                    // Delete Status
                    if (Properties.Settings.Default.DeleteStatusBeforeEachRun)
                    {
                        DeleteStatus();
                    }

                    if (exceptionsTable != null)
                    {
                        logStatus("Reading Resource Exceptions");
                        try
                        {
                            importDowntime(model, exceptionsTable); 
                        }
                        catch(Exception ex)
                        {   
                            logStatus("Exception Reading ResourceExceptions " + ex.Message);
                        }
                        // Save Projects
                        if (Properties.Settings.Default.SaveProject)
                        {
                            logStatus("Save Project Prior To Run Plan");
                            saveProject();
                        }
                    }

                    // Start Plan
                    logStatus("Starting Plan");
                    model.Plan.RunPlan();
                    logStatus("Plan Finished...Starting Analyze Risk");
                    model.Plan.RunRiskAnalysis();
                    logStatus("Analyze Risk Finished...Export Schedule");
                    exportSchedule(model);

                    // Save Projects
                    if (Properties.Settings.Default.SaveProject)
                    {
                        logStatus("Save Project After Schedule Run");
                        saveProject();
                    }
                }
            }       
        }

        private static Int32 importDowntime(IModel model, DataTable exceptionsTable )
        {
            Int32 numberOfUpdates = 0;
   
            var resorucesTable = model.Tables["Resources"];
            if (resorucesTable == null) throw new Exception("Resources table does not exist in model");            

            foreach (var resourcesRow in resorucesTable.Rows)
            {
                var intellObj = model.Facility.IntelligentObjects[resourcesRow.Properties["ResourceName"].Value];

                if (intellObj != null)
                {
                    foreach (IProperty prop in intellObj.Properties)
                    {
                        if (prop.Name == "WorkPeriodExceptions")
                        {
                            IRepeatingProperty repeatProp = prop as IRepeatingProperty;

                            if (repeatProp != null)
                            {
                                repeatProp.Rows.Clear();
                                foreach (System.Data.DataRow dr in exceptionsTable.Rows)
                                {
                                    if (intellObj.ObjectName == dr[0].ToString())
                                    {
                                        var repeatRow = repeatProp.Rows.Create();
                                        repeatRow.Properties[0].Value = Convert.ToDateTime(dr[1]).ToString();
                                        repeatRow.Properties[1].Value = Convert.ToDateTime(dr[2]).ToString();
                                        repeatRow.Properties[3].Value = dr[3].ToString();
                                        numberOfUpdates++;
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return numberOfUpdates;
        }

        public static DataTable ReadCsvFileIntoDataTable(string fileNameAndPath)
        {
            var dtCsv = new DataTable();
            string Fulltext;

            using (StreamReader sr = new StreamReader(fileNameAndPath))
            {
                while (!sr.EndOfStream)
                {
                    Fulltext = sr.ReadToEnd().ToString(); //read full file text  
                    string[] rows = Fulltext.Split('\n'); //split full file text into rows  
                    for (int i = 0; i < rows.Count() - 1; i++)
                    {
                        string[] rowValues = rows[i].Split(','); //split each row with comma to get individual values  
                        {
                            if (i == 0)
                            {
                                for (int j = 0; j < rowValues.Count(); j++)
                                {
                                    dtCsv.Columns.Add(rowValues[j]); //add headers  
                                }
                            }
                            else
                            {
                                DataRow dr = dtCsv.NewRow();
                                for (int k = 0; k < rowValues.Count(); k++)
                                {
                                    dr[k] = rowValues[k].ToString();
                                }
                                dtCsv.Rows.Add(dr); //add other rows  
                            }
                        }
                    }
                }
            }
            return dtCsv;
        }

        private static void exportSchedule(IModel model)
        {
            // Export Schedule      
            try
            {
                if (System.IO.File.Exists(Properties.Settings.Default.ExportScheduleFile))
                {
                    File.Delete(Properties.Settings.Default.ExportScheduleFile);
                }

                var dataSet = new DataSet();
                var dataTable = ConvertResourceUsageLogAndTargetResultToDataTable(model);
                dataTable.TableName = "ResourceUsageLog";
                dataSet.Tables.Add(dataTable);
                dataSet.WriteXml(Properties.Settings.Default.ExportScheduleFile);             
            }
            catch { }
        }

        private static void saveProject()
        {
            // Save Project            
            string[] warnings;
            SimioProjectFactory.SaveProject(_simioProject, _projectPathAndFile, out warnings);
        }

        private static void logStatus(string msg, bool clear)
        {
            try
            {
                using (System.IO.StreamWriter file = new System.IO.StreamWriter(Properties.Settings.Default.StatusFile, !clear))
                {
                    msg = System.DateTime.Now.ToString() + "-" + msg; 
                    if (msg.Length > 32000) msg = msg.Substring(0, 32000);
                    file.WriteLine(msg);
                }
            }
            catch { }
        }

        private static void logStatus(string msg)
        {
            logStatus(msg, false);
        }

        private static void DeleteStatus()
        {
            try
            {
                if (System.IO.File.Exists(Properties.Settings.Default.StatusFile))
                {
                    File.Delete(Properties.Settings.Default.StatusFile);
                }
            }
            catch { }
        }

        private static DataTable ConvertResourceUsageLogAndTargetResultToDataTable(IModel model)
        {           

            List<string[]> logList = new List<string[]>();

            // get all column names
            List<string> colNames = new List<string>();
            colNames.Add("OwnerName");
            colNames.Add("Probability");
            colNames.Add("ResourceName");
            colNames.Add("StartTime");
            colNames.Add("EndTime");
            logList.Add(colNames.ToArray());

            // each row in resource usage log
            foreach (var row in model.Plan.ResourceUsageLog)
            {          
                List<string> thisRow = new List<string>();
                // get data
                thisRow.Add(row.OwnerName);
                Boolean foundFlag = false;
                if (row.OwnerId != null)
                {
                    foreach (var item in model.Plan.TargetResults)
                    {
                        if (item.Owner == row.OwnerId)
                        {
                            thisRow.Add(item.RiskWithinBoundsProbability.ToString());
                            foundFlag = true;
                            break;
                        }
                    }
                }
                if (foundFlag == false) thisRow.Add(""); 
                if (row.ResourceName != null) thisRow.Add(row.ResourceName);
                else thisRow.Add("");
                if (row.StartTime != null) thisRow.Add(row.StartTime.ToString("s"));
                else thisRow.Add("");
                if (row.EndTime != null) thisRow.Add(row.EndTime.ToString("s"));
                else thisRow.Add("");
                logList.Add(thisRow.ToArray());
            }

            // New table.
            var dataTable = new DataTable();

            // Get max columns.
            int columns = 0;
            foreach (var array in logList)
            {
                if (array.Length > columns)
                {
                    columns = array.Length;
                }
            }

            // Add columns.
            for (int i = 0; i < columns; i++)
            {
                var array = logList[0];
                dataTable.Columns.Add(array[i]);
            }

            // Remove Column Headings
            if (logList.Count > 0)
            {
                logList.RemoveAt(0);
            }

            // Add rows.
            foreach (var array in logList)
            {
                dataTable.Rows.Add(array);
            }

            return dataTable;
        }

        private static DataTable ConvertTableToDataTable(ITable table)
        {
            List<string[]> tableList = new List<string[]>();
            int rowNumber = 0;

            // get all column names
            List<string> colNames = new List<string>();

            // get property column names
            List<string> propColNames = new List<string>();
            foreach (var col in table.Columns)
            {
                colNames.Add(col.Name);
                propColNames.Add(col.Name);
            }
            // get state column names
            List<string> stateColNames = new List<string>();
            foreach (var stateCol in table.StateColumns)
            {
                colNames.Add(stateCol.Name);
                stateColNames.Add(stateCol.Name);
            }
            tableList.Add(colNames.ToArray());

            // Get Row Data
            foreach (var row in table.Rows)
            {
                rowNumber++;
                List<string> thisRow = new List<string>();
                // get properties
                foreach (var array in propColNames)
                {
                    if (row.Properties[array.ToString()].Value != null) thisRow.Add(row.Properties[array.ToString()].Value);
                    else thisRow.Add("");
                }
                // get states
                foreach (var array in stateColNames)
                {
                    if (table.StateRows[rowNumber - 1].StateValues[array.ToString()].PlanValue != null) thisRow.Add(table.StateRows[rowNumber - 1].StateValues[array.ToString()].PlanValue.ToString());
                    else thisRow.Add("");
                }
                tableList.Add(thisRow.ToArray());
            }

            // New table.
            var dataTable = new DataTable();

            // Get max columns.
            int columns = 0;
            foreach (var array in tableList)
            {
                if (array.Length > columns)
                {
                    columns = array.Length;
                }
            }

            // Add columns.
            for (int i = 0; i < columns; i++)
            {
                var array = tableList[0];
                dataTable.Columns.Add(array[i]);
            }

            // Remove Column Headings
            if (tableList.Count > 0)
            {
                tableList.RemoveAt(0);
            }

            // sort rows
            //var sortedList = list.OrderBy(x => x[0]).ThenBy(x => x[3]).ToList();

            // Add rows.
            foreach (var array in tableList)
            {
                dataTable.Rows.Add(array);
            }

            return dataTable;
        }
    }

    // Provide the ProjectInstaller class which allows

    // the service to be installed by the Installutil.exe tool
    [System.ComponentModel.RunInstaller(true)]
    public class ProjectInstaller : Installer
    {
        private ServiceProcessInstaller process;
        private ServiceInstaller service;
        public ProjectInstaller()
        {
            process = new ServiceProcessInstaller();
            process.Account = ServiceAccount.LocalSystem;
            service = new ServiceInstaller();
            service.ServiceName = "RunSimioSchedule";
            service.Description = "This Service Will Import Data, Run Schedule, Export Schedule and Save Simio Scheduled Based On The Creation Of A File";
            Installers.Add(process);
            Installers.Add(service);
        }
    }
}
