using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using NWSPolling;

// ========================================================================
//
//                          WarningManager.cs
//
// Author: Sarah Nimitz, Trevor White           Date: May 4, 2016
//         Duke Forsyth
// Purpose: Controller class for the parsing and polling of warnings. This
//          is used to poll a server for statements at a given interval,
//          divide up the files into usable statements, and call the parser
//          with each individual statement. After that, files are created
//          or updated accordingly in order to keep up warnings up to date
//          and so that they can be referenced later.
//
//          Some functionality is incomplete, namely the generation of
//          warning rendering geometry, catching human error in the severe
//          weather statements, and keeping track of active and relevant
//          warnings.
// 
// ========================================================================

namespace NWSWarnings
{
    public class WarningManager
    {
        //private boolean to see if the thread should be terminated or not.
        private Boolean shouldStop = false;
        private int intervalPoll = 300000; //5 mins in milliseconds
        private StatementPoller poller;
        private StatementParser parser;
        private List<Warning> activeWarnings = new List<Warning>();

        // Default is warnings.cod.edu and 10 seconds, given in milliseconds
        public WarningManager(string PollingURL, int PollingInterval)
        {
            parser = new StatementParser();


            poller = new StatementPoller();
            poller.setWebsiteToPoll(PollingURL);
            intervalPoll = PollingInterval;

            // TODO have to make a thread and sleep it for interval poll time 
            Poll();
        }

        /// <summary>
        /// Polls the entire server and grabs all files on said server. 
        /// To get array of information use poller.getWebsiteContents() 
        /// </summary>
        public void Poll()
        {
            Console.WriteLine("Currently Polling...");
            //should stop starts false
           // while (shouldStop == false)
           // {
                //1st step scrape website for the links
                poller.pollWebsiteForLinks(poller.getWebsiteURL());
                
                //2nd step download all files from website as strings
                poller.DownloadAllFiles();

            Console.WriteLine("Polling of server complete now processing each file...");
                //3rd step pass files one by one to parser to be parsed.
                foreach(String a in poller.getWebsiteContents())
                {
                    this.ProcessInputFile(a);
                }
                

          //  }
            Console.WriteLine("Finished with polling ");
        }

        // This function will kill polling
        public void StopPolling()
        {
            Console.WriteLine("Signal to stop polling sent....");
            //this updates the while loop check but might not kill immediately...
            this.shouldStop = true;
        }

       
        /// <summary>
        /// To process the input file given into different statements
        /// </summary>
        /// <param name="inputFile">The file to be parsed</param>
        public void ProcessInputFile(string inputFile)
        {
            inputFile = Regex.Replace(inputFile, @"\r\n", "\n");
            MatchCollection sections = Regex.Matches(inputFile, @"([^&$]+(\$\$\n*.*(\n{3}|\n*$))|[^&]+\&\&[^$]+(\$\$\n*.*(\n{3}|\n*$)))");
            string tempStr = "";
            
            foreach (Match m in sections)
            {
                tempStr = m.ToString();
                if (tempStr == "15" || tempStr == "" || tempStr == "\n")
                {
                    break;
                }
                this.ProcessStatement(tempStr);
            }
        }

        /// <summary>
        /// To process a weather statement
        /// </summary>
        /// <param name="inputStatement"></param>
        public void ProcessStatement(string inputStatement)
        {
            SevereWeatherStatement newState = null;
            Warning newWarn = null;
            DateTime stateTime = DateTime.MinValue, warnStart = DateTime.MinValue, warnTime = DateTime.MinValue;
            string stateStation = "";
            int warnETN = -1;

            // Process statements
            newState = parser.ParseStatement(inputStatement);
            
            newWarn = newState.Warning;
            stateTime = newState.PublishTime;
            stateStation = newState.Station;
            
            if (newWarn == null)
            {
                // Creates file for storing the statement.
                string directA = "Warnings\\" + stateTime.Year.ToString("D4"), directB = directA + "\\Unlinked",
                    path = directB + "\\" + stateStation + stateTime.Month.ToString("D2") + stateTime.Day.ToString("D2");

                if (!Directory.Exists(directA))
                    Directory.CreateDirectory(directA);

                if (!Directory.Exists(directB))
                    Directory.CreateDirectory(directB);

                if (!File.Exists(path))
                    File.Create(path).Close();

                // Seperate each element by '=' x10
                if (!File.ReadAllText(path).Contains(inputStatement))
                {
                    string fullContents = File.ReadAllText(path);
                    List<string> contents = Regex.Split(fullContents, "\n==========").ToList(), temp;
                    contents.Add(inputStatement);
                    int count = contents.Count;
                    List<List<string>> brokeState = new List<List<string>>();

                    // After breaking down each statement currently
                    // within the warning file, sorts by its publication
                    // time.
                    foreach (string st in contents)
                    {
                        temp = Regex.Split(st, "\n\n").ToList();
                        if (temp.Count > 1)
                            brokeState.Add(temp);
                    }
                    brokeState.Sort((x, y) => DateTime.Compare(parser.ParsePublish(y[1]), parser.ParsePublish(x[1])));

                    // Write all sorted statements back into the
                    // warning file.
                    string toPrint = "";
                    for (int i = 0; i < brokeState.Count; i++)
                    {
                        for (int j = 0; j < brokeState[i].Count; j++)
                        {
                            toPrint = toPrint + brokeState[i][j];
                            if (j < brokeState[i].Count - 1)
                                toPrint = toPrint + "\n\n";
                        }
                        if (i < brokeState.Count - 1)
                            toPrint = toPrint + "\n==========";
                    }

                    File.WriteAllText(path, toPrint);
                }
            }
            else
            {
                warnETN = newWarn.TrackingNumber;
                warnStart = newWarn.Start;
                warnTime = newWarn.Time;

                // Doesn't work for updates to warnings.
                string direct = "Warnings\\" + newWarn.Time.Year.ToString("D4"), path = direct + "\\" + stateStation + warnETN.ToString("D4")
                    + stateTime.Month.ToString("D2") + stateTime.Day.ToString("D2");

                switch (newWarn.Type)
                {
                    case (WarningType.Tornado):
                    case (WarningType.TornadoEmergency):
                    case (WarningType.TornadoReported):
                        path = path + ".TOR";
                        break;
                    case (WarningType.SevereThunderstorm):
                        path = path + ".SVR";
                        break;
                    case (WarningType.FlashFlood):
                        path = path + ".FFW";
                        break;
                    case (WarningType.SpecialMarine):
                        goto default;
                    case (WarningType.Unknown):
                        goto default;
                    default:
                        break;
                }

                if (!Directory.Exists(direct))
                    Directory.CreateDirectory(direct);

                if (!File.Exists(path))
                    File.Create(path).Close();

                // Seperate each element by '=' x10
                if (!File.ReadAllText(path).Contains(inputStatement))
                {
                    string fullContents = File.ReadAllText(path);
                    List<string> contents = Regex.Split(fullContents, "\n==========").ToList(), temp;
                    contents.Add(inputStatement);
                    int count = contents.Count;
                    List<List<string>> brokeState = new List<List<string>>();

                    // After breaking down each statement currently
                    // within the warning file, sorts by its publication
                    // time.
                    foreach (string st in contents)
                    {
                        temp = Regex.Split(st, "\n\n").ToList();
                        if (temp.Count > 1)
                            brokeState.Add(temp);
                    }
                    brokeState.Sort((x, y) => DateTime.Compare(parser.ParsePublish(y[1]), parser.ParsePublish(x[1])));
                    
                    // Write all sorted statements back into the
                    // warning file.
                    string toPrint = "";
                    for (int i = 0; i < brokeState.Count; i++)
                    {
                        for (int j = 0; j < brokeState[i].Count; j++)
                        {
                            toPrint = toPrint + brokeState[i][j];
                            if (j < brokeState[i].Count - 1)
                                toPrint = toPrint + "\n\n";
                        }
                        if (i < brokeState.Count - 1)
                            toPrint = toPrint + "\n==========";
                    }

                    File.WriteAllText(path, toPrint);
                }

                // ==============================
                //         Writing to Log
                // ==============================
                string logPath = direct + "\\" + newWarn.Time.Year.ToString("D4") + " Warning Log";

                if (!File.Exists(logPath))
                    File.Create(logPath).Close();

                // Go through and put in its proper place but eventually
                List<string> allLines = new List<string>(File.ReadAllLines(logPath));

                // Removes duplicate warning if exists.
                for (int i = 0; i < allLines.Count; i++)
                {
                    if (allLines[i].Contains(stateStation + warnETN.ToString("D4")))
                    {
                        warnStart = DateTime.Parse(Regex.Split(allLines[i], @" \| ")[1]);   // Update warning?
                        allLines.Remove(allLines[i]);
                        i--;
                    }
                }

                // Adds warning to list of logged warnings
                allLines.Add(warnTime + " | " + warnStart + " | " + stateStation + warnETN.ToString("D4"));

                // Sorts strings by end time.
                List<string[]> brokeLines = new List<string[]>();
                foreach (string st in allLines)
                {
                    brokeLines.Add(Regex.Split(st, @" \| "));
                }
                brokeLines.Sort((x, y) => DateTime.Compare(DateTime.Parse(y[0]), DateTime.Parse(x[0])));

                // Writes all warnings to log.
                string output = "";
                foreach (string[] st in brokeLines)
                {
                    if (st.Length > 2)
                        output = output + st[0] + " | " + st[1] + " | " + st[2] + "\n";
                }
                File.WriteAllText(logPath, output);
            }
            // Add activeWarnings/update as needed

            //to check if this is an activewarning or not.
            if (newWarn != null)
            {
                this.addWarningToWatchlist(newWarn);
            }
        }

        /// <summary>
        /// To check if a time should be added to the activeWarnings list
        /// Assumes that the warning is correctly made as in time is added or subtracked vs 
        /// the clients timezone
        /// </summary>
        /// <param name="timeToTest">The time to compare against the current time</param>
        public void addWarningToWatchlist(Warning timeToTest)
        {
            DateTime clientTime = DateTime.Now;//to get the current time

            //means statement time is earlier than current thus not part of the active warnings
            if (DateTime.Compare(timeToTest.Time, clientTime) < 0)
                {
                    //remove from the list
                    if(this.activeWarnings.Contains(timeToTest))
                    {
                        this.activeWarnings.Remove(timeToTest);
                    }
                
            }
            //means the time have met and now we must get rid of this time if it is on the list
            if (DateTime.Compare(timeToTest.Time, clientTime) == 0)
            {
                //remove from list if it is on it.
                if(this.activeWarnings.Contains(timeToTest))
                {
                    this.activeWarnings.Remove(timeToTest);
                }
            }
            //means statement time is later than the current time thus warning should be on the list.
            if(DateTime.Compare(timeToTest.Time,clientTime) > 0)
            {
                this.activeWarnings.Add(timeToTest);
                
            }
        }


        // Returns the list of all warnings currently active.
        public List<Warning> GetWarnings()
        {
            // Should be within the activeWarnings List
            return activeWarnings;
        }

        // This should return a set of warnings valid at a given time.
        // Will usually be called with DateTime.UtcNow as an argument to get current warnings.
        // This is also fine returned as an array... whatever floats your goat.
        public List<Warning> GetWarnings(DateTime ValidTime)
        {

            //to get a current time client side to work against
            DateTime clientTime = DateTime.Now;
            
            // End time from log            is after ValidTime
            // Start time in warning file   is before ValidTime



            return null;
        }

        // This should return a set of warnings valid during a given time range.
        // Should use the most recent warning updates valid for this time range.
        // This is also fine returned as an array... whatever floats your goat.
        public List<Warning> GetWarnings(DateTime ValidTimeBegin, DateTime ValidTimeEnd)
        {
            // End time from log            is after ValidTimeBegin
            // Start time in warning file   is before ValidTimeEnd
            return null;
        }
    }
}
