using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using System.Xml.XPath;
using NLog;

namespace NzbDrone
{
    class IISController
    {
        public static Process IISProcess { get; private set; }
        private static readonly Logger IISLogger = LogManager.GetLogger("IISExpress");
        private static readonly Logger Logger = LogManager.GetLogger("IISController");
        private static readonly string IISFolder = Path.Combine(Config.ProjectRoot, @"IISExpress\");
        private static readonly string IISExe = Path.Combine(IISFolder, @"iisexpress.exe");


        internal static string AppUrl
        {
            get { return string.Format("http://localhost:{0}/", Config.Port); }
        }

        internal static Process StartIIS()
        {
            Logger.Info("Preparing IISExpress Server...");
            IISProcess = new Process();

            IISProcess.StartInfo.FileName = IISExe;
            IISProcess.StartInfo.Arguments = "/config:IISExpress\\Appserver\\applicationhost.config /trace:i";
            IISProcess.StartInfo.WorkingDirectory = Config.ProjectRoot;

            IISProcess.StartInfo.UseShellExecute = false;
            IISProcess.StartInfo.RedirectStandardOutput = true;
            IISProcess.StartInfo.RedirectStandardError = true;
            IISProcess.StartInfo.CreateNoWindow = true;


            IISProcess.OutputDataReceived += (OnDataReceived);

            IISProcess.ErrorDataReceived += ((s, e) => IISLogger.Fatal(e.Data));



            //Set Variables for the config file.
            Environment.SetEnvironmentVariable("NZBDRONE_PATH", Config.ProjectRoot);

            try
            {
                UpdateIISConfig();
            }
            catch (Exception e)
            {
                Logger.Error("An error has occured while trying to update the config file.", e);
            }


            Logger.Info("Starting process. [{0}]", IISProcess.StartInfo.FileName);
            IISProcess.Start();

            IISProcess.BeginErrorReadLine();
            IISProcess.BeginOutputReadLine();
            return IISProcess;
        }

        private static void OnDataReceived(object s, DataReceivedEventArgs e)
        {
            if (e == null || e.Data == null || e.Data.StartsWith("Request started:") || e.Data.StartsWith("Request ended:") || e.Data == ("IncrementMessages called"))
                return;

            IISLogger.Trace(e.Data);
        }

        internal static void StopIIS()
        {
            KillProcess(IISProcess);
        }

        internal static void KillOrphaned()
        {
            Logger.Info("Finding orphaned IIS Processes.");
            foreach (var process in Process.GetProcessesByName("IISExpress"))
            {
                string processPath = process.MainModule.FileName;
                Logger.Info("[{0}]IIS Process found. Path:{1}", process.Id, processPath);
                if (CleanPath(processPath) == CleanPath(IISExe))
                {
                    Logger.Info("[{0}]Process is considered orphaned.", process.Id);
                    KillProcess(process);
                }
                else
                {
                    Logger.Info("[{0}]Process has a different start-up path. skipping.", process.Id);
                }
            }
        }

        private static void KillProcess(Process process)
        {
            if (process == null) return;

            Logger.Info("[{0}]Killing process", process.Id);
            process.Kill();
            Logger.Info("[{0}]Waiting for exit", process.Id);
            process.WaitForExit();
            Logger.Info("[{0}]Process terminated successfully", process.Id);
        }

        private static void UpdateIISConfig()
        {
            string configPath = Path.Combine(IISFolder, @"AppServer\applicationhost.config");

            Logger.Info(@"Server configuration file: {0}", configPath);
            Logger.Info(@"Configuring server to: [http://localhost:{0}]", Config.Port);

            var configXml = XDocument.Load(configPath);

            var bindings = configXml.XPathSelectElement("configuration/system.applicationHost/sites").Elements("site").Where(d => d.Attribute("name").Value.ToLowerInvariant() == "nzbdrone").First().Element("bindings");
            bindings.Descendants().Remove();
            bindings.Add(
            new XElement("binding",
            new XAttribute("protocol", "http"),
            new XAttribute("bindingInformation", String.Format("*:{0}:", Config.Port))
            ));

            configXml.Save(configPath);
        }

        private static string CleanPath(string path)
        {
            return path.ToLower().Replace("\\", "").Replace("//", "//");
        }
    }
}