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

namespace Daramkun.Log
{
	/// <summary>
	/// Message Logging Method
	/// </summary>
	public enum LoggingMethod
	{
		/// <summary>
		/// Direct Writing
		/// </summary>
		Direct,
		/// <summary>
		/// Await Writing
		/// </summary>
		Await,
	}

	/// <summary>
	/// Log Writer class
	/// </summary>
    public static class Log
    {
		static List<ILogWriter> loggingWriters = new List<ILogWriter> ();
		static LoggingMethod loggingMethod = LoggingMethod.Await;
		static Dictionary<uint, string> levelNickname = new Dictionary<uint, string> ();
		static uint enableLevel = 0;

		/// <summary>
		/// Message Logging Method
		/// </summary>
		public static LoggingMethod LoggingMethod
		{
			get { return loggingMethod; }
			set { loggingMethod = value; }
		}

		/// <summary>
		/// Write Enabled Level
		/// (Higher or Equal is Enabled)
		/// </summary>
		public static uint WriteEnableLevel
		{
			get { return enableLevel; }
			set { enableLevel = value; }
		}

		/// <summary>
		/// Add Writer
		/// </summary>
		/// <param name="writer">Log Writer</param>
		public static void AddWriter ( ILogWriter writer )
		{
			lock ( loggingWriters )
			{
				loggingWriters.Add ( writer );
			}
		}

		/// <summary>
		/// Remove Writer
		/// </summary>
		/// <param name="writer">Log Writer</param>
		public static void RemoveWriter ( ILogWriter writer )
		{
			lock ( loggingWriters )
			{
				loggingWriters.Remove ( writer );
			}
		}

		/// <summary>
		/// Add Level Nickname
		/// </summary>
		/// <param name="level">Level</param>
		/// <param name="nickname">Nickname</param>
		public static void AddLevelNickname ( uint level, string nickname )
		{
			levelNickname.Add ( level, nickname );
		}

		/// <summary>
		/// Remove Level Nickname
		/// </summary>
		/// <param name="level">Level</param>
		public static void RemoveLevelNickname ( uint level )
		{
			levelNickname.Remove ( level );
		}

		/// <summary>
		/// Write Log Message to Log Writer
		/// </summary>
		/// <param name="message">Log Message</param>
		/// <param name="level">Log Level</param>
		public static async void WriteLine ( string message, uint level = 0 )
		{
			int threadId = Thread.CurrentThread.ManagedThreadId;
			DateTime currentTime = DateTime.Now;
			switch(loggingMethod)
			{
				case LoggingMethod.Await:
					await TaskEx.Run ( () => { WriteToStream ( message, level, threadId, currentTime ); } );
					break;
				case LoggingMethod.Direct:
					WriteToStream ( message, level, threadId, currentTime );
					break;
			}
		}

		private static void WriteToStream ( string message, uint level, int threadId, DateTime dateTime )
		{
			if ( level >= enableLevel ) return;

			string levelParam;
			if ( !levelNickname.TryGetValue ( level, out levelParam ) ) levelParam = level.ToString ();
			string completedMessage = String.Format ( "[{0}][{1}][{2}]{3}", threadId, dateTime.ToString ( "yyyy-MM-dd hh:mm:ss" ), levelParam, message );

			lock ( loggingWriters )
			{
				foreach ( ILogWriter logWriter in loggingWriters )
					logWriter.WriteMessage ( completedMessage );
			}
		}
    }
}
