using System;
using System.Collections;
using System.Threading;
using System.IO;

namespace Logger
{
    // Represents a log message
    public struct LoggerMessage
    {
        public int level;
        public string level_desc;
        public string tag;
        public string message;
        public long time;
    }

    // Abstract class responsible managing (in the form or a queue) and dispatching log messages
    // to their appropriate source.  The basic premises is that everything from a file writers, 
    // to an e-mailer, to an syslog interface, to a db interface, etc... could be implemented and the only
    // thing the implementer would need to worry about was the interface.
    public abstract class LoggerEventHandler
    {
        private bool alive = false;
        private Queue q = null;
        private Thread dispatch = null;
        protected string[] logLevelDescriptors = null;

        public LoggerEventHandler()
        {
            // q = Queue.Synchronized(new Queue(1000));
            start();
        }

        // Starts the execution of the Handler (e.g. Queue goes live)
        public void start()
        {
            //It's already alive, nothing to do
            if (alive) return;

            //Make sure this is the only place where alive is set to true
            alive = true;
            dispatch = new Thread(new ThreadStart(dispatchMessages));
            dispatch.Start();
        }

        // Stops the execution of the Handler gracefully (e.g. everything in the queue is
        // dispatched, but nothing can be added to it)
        public void shutdown()
        {
            //Nothing to do
            if (!alive) return;

            alive = false;
            Monitor.Enter(q);
            // Monitor.PulseAll(q);
            Monitor.Exit(q);
        }

        // Will immediately shutdown the thread without cleaning up or clearing
        // out the queue.  Consequently this is not the recommended way to terminate.
        public void abort()
        {
            if (!alive) return;

            alive = false;
            dispatch.Abort();
        }

        // This is the worker method where the messages get dispatched
        protected void dispatchMessages()
        {
            //int max = 0;
            while (alive)
            {
                while ((q.Count != 0) && alive)
                {
                    //if(q.Count > max)max = q.Count;
                    log((LoggerMessage)q.Dequeue());
                }

                if ((alive) && (q.Count == 0))
                {
                    Monitor.Enter(q);
                    // if (q.Count == 0) Monitor.Wait(q);
                    Monitor.Exit(q);
                }
            }

            // You will only reach this code if you are exiting the program
            // This block ensures the messages that were queued up will get dumped
            while (q.Count != 0)
            {
                log((LoggerMessage)q.Dequeue());
            }

            // Do any special shutdown you need to do
            onShutdown();

            // Remove thread reference so you don't have object leak
            dispatch = null;
        }

        // Responsible for queuing the log message
        public void log(string tag, int level, string level_desc, string message)
        {
            if (!alive) return;

            LoggerMessage lm = new LoggerMessage();
            lm.message = message;
            lm.tag = tag;
            lm.level = level;
            lm.level_desc = level_desc;
            // lm.time = System.DateTime.Now.ToFileTime();

            q.Enqueue(lm);

            Monitor.Enter(q);
            // Monitor.PulseAll(q);
            Monitor.Exit(q);
        }

        // User implementable log method (e.g. what actions they need to take to perform
        // a "log" operation, like logging to a file for example).
        protected abstract void log(LoggerMessage message);

        // User implementable, meant for cleanup when the thread is stopped (e.g. if you need to
        // close files, db connections, etc...)
        protected abstract void onShutdown();
    }
}
