using NamedPipeWrapper;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Security.AccessControl;
using System.Text;

namespace InABox.Logging
{

    public enum LogType
    {
        Information,
        Query,
        Update,
        Error
    }

    public static class Logger
    {

        private static object logfileLock = new object();

        private static NamedPipeServer<String> _pipe;
        private static String _name = "";

        public static String LogFolder = "";
        private static String TmpFolder = "";

        private static bool _started = false;

        private static bool _logtoconsole = false;

        private static LogType[] _logtypes = new LogType[] { LogType.Information, LogType.Query, LogType.Update, LogType.Error };

        public static void Start(String folder, bool logtoconsole = false, LogType[] logtypes = null)
        {

            _logtoconsole = logtoconsole;

            if (logtypes != null)
                _logtypes = logtypes;

            LogFolder = folder;

            TmpFolder = Path.Combine(LogFolder, "Temp");
            if (!Directory.Exists(TmpFolder))
                Directory.CreateDirectory(TmpFolder);
            String[] files = Directory.GetFiles(TmpFolder);
            foreach (var file in files)
                File.Delete(file);

            _name = System.Diagnostics.Process.GetCurrentProcess().ProcessName;
            PipeSecurity sec = new System.IO.Pipes.PipeSecurity();
            sec.SetAccessRule(new PipeAccessRule("Everyone", PipeAccessRights.ReadWrite, AccessControlType.Allow));
            _pipe = new NamedPipeServer<String>(_name, sec);

            _pipe.ClientConnected += delegate (NamedPipeConnection<String, String> conn)
            {
                conn.PushMessage("Connected to " + _name);
            };

            _pipe.Start();

            _started = true;

        }

        public static void Send(LogType logtype, String user, String message)
        {
            if (!_started)
                return;

            if (!_logtypes.Any(x => x == logtype))
                return;

            String type = logtype == LogType.Information ? "INFO" : logtype == LogType.Query ? "READ" : logtype == LogType.Update ? "UPDATE" : "ERROR";
            String msg = String.Format("{0:HH:mm:ss.fff} {1} {2} {3}",
                DateTime.Now,
                type?.PadRight(6),
                user?.PadRight(12),
                message
            );

            if (_logtoconsole) //System.Environment.UserInteractive)
                Console.WriteLine(msg);

            if (_pipe != null)
                _pipe.PushMessage(msg);

            if (!String.IsNullOrEmpty(LogFolder))
            {
                if ((logtype == LogType.Information) || (logtype == LogType.Error) || (logtype == LogType.Query))
                    UpdateLogFile(msg);
                else if (logtype == LogType.Update)
                    UpdateJournal(message);
            }

            //if (logtype == LogType.Error)
            //{
            //    using (EventLog eventLog = new EventLog("Application"))
            //    {
            //        eventLog.Source = "Application";
            //        eventLog.WriteEntry(msg, EventLogEntryType.Error);
            //    }
            //}
        }

        private static void UpdateLogFile(string msg)
        {
            String filename = Path.Combine(LogFolder, String.Format("{0:yyyy-MM-dd}.log", DateTime.Today));
            try
            {
                lock (logfileLock)
                {
                    using (StreamWriter sw = new StreamWriter(filename, true, Encoding.UTF8, 65536))
                    {
                        sw.WriteLine(msg);
                        sw.Close();
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("*** Failed to Update Log: " + e.Message);
                Console.WriteLine("*** Message: " + msg);
            }
        }

        private static void UpdateJournal(string sql)
        {
            String filename = Path.Combine(LogFolder, String.Format("{0:yyyy-MM-dd}.sql", DateTime.Today));
            try
            {
                lock (logfileLock)
                {
                    using (StreamWriter sw = new StreamWriter(filename, true, Encoding.UTF8, 65536))
                    {
                        sw.WriteLine(sql);
                        sw.Close();
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("*** Failed to Update SQL Journal: " + e.Message);
                Console.WriteLine("*** Message: " + sql);
            }
        }


        public static void Dump(String filename, byte[] bytes)
        {
            if (!_started)
                return;

            String file = Path.Combine(TmpFolder, String.Format("{0:yyyy-MM-dd HH-mm-ss-fff} {1}", DateTime.Now, filename));
            File.WriteAllBytes(file, bytes);
        }

        //public static void Save(String data, String tag, String suffix)
        //{

        //    String filename = Path.Combine( TmpFolder, String.Format("{0:yyyy-MM-dd-HH-mm-ss-ffff} {1}.{2}", DateTime.Now,tag,suffix) );
        //    try
        //    {
        //        File.WriteAllText(filename,data);
        //    }
        //    catch (Exception e)
        //    {
        //        Console.WriteLine("*** Failed to Save Dumpt File: " + e.Message);
        //        Console.WriteLine("*** Message: " + data);
        //    }
        //}

    }
}
