// Log.cs - Wrapper for message logging functions
//
// Authors:
//	Gonzalo Paniagua Javier (gonzalo@ximian.com)
//	
//
// Copyright (c) 2002 Gonzalo Paniagua
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of version 2 of the Lesser GNU General 
// Public License as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this program; if not, write to the
// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.

//

namespace GLib {

	using System;
	using System.Runtime.InteropServices;

	public delegate void LogFunc (string log_domain, LogLevelFlags log_level, string message);

	public delegate void PrintFunc (string message);

	[Flags]
	public enum LogLevelFlags : int
	{
		/* log flags */
		FlagRecursion          = 1 << 0,
		FlagFatal              = 1 << 1,

		/* GLib log levels */
		Error                  = 1 << 2,       /* always fatal */
		Critical               = 1 << 3,
		Warning                = 1 << 4,
		Message                = 1 << 5,
		Info                   = 1 << 6,
		Debug                  = 1 << 7,

		/* Convenience values */
		AllButFatal            = 253,
		AllButRecursion        = 254,
		All                    = 255,

		FlagMask               = 3,
		LevelMask              = unchecked ((int) 0xFFFFFFFC)
	}

	public class Log {

		[UnmanagedFunctionPointer (CallingConvention.Cdecl)]
		delegate void LogFuncNative (IntPtr log_domain, LogLevelFlags flags, IntPtr message, IntPtr user_data);

		static LogFuncNative native_handler;

		static void NativeCallback (IntPtr log_domain_native, LogLevelFlags flags, IntPtr message_native, IntPtr user_data)
		{
			if (user_data == IntPtr.Zero)
				return;
			string log_domain = Marshaller.Utf8PtrToString (log_domain_native);
			string message = Marshaller.Utf8PtrToString (message_native);
			GCHandle gch = (GCHandle) user_data;
			LogFunc func = gch.Target as LogFunc;
			if (func != null)
				func (log_domain, flags, message);
		}

		[UnmanagedFunctionPointer (CallingConvention.Cdecl)]
		delegate void PrintFuncNative (IntPtr message);

		class PrintHelper {

			PrintFuncNative native;
			PrintFunc managed;

			public PrintHelper (PrintFuncNative native)
			{
				this.native = native;
			}

			public PrintHelper (PrintFunc managed)
			{
				this.managed = managed;
				GCHandle.Alloc (this);
			}

			void Callback (IntPtr nmessage)
			{
				string message = Marshaller.Utf8PtrToString (nmessage);
				managed (message);
			}

			void Invoke (string message)
			{
				IntPtr nmessage = Marshaller.StringToPtrGStrdup (message);
				native (nmessage);
				Marshaller.Free (nmessage);
			}

			public PrintFuncNative Handler {
				get { return new PrintFuncNative (Callback); }
			}

			public PrintFunc Invoker {
				get { return new PrintFunc (Invoke); }
			}
		}

		static System.Collections.Generic.Dictionary<uint, GCHandle> handlers;

		static void EnsureHash ()
		{
			if (handlers == null)
				handlers = new System.Collections.Generic.Dictionary<uint, GCHandle> ();
		}

		[DllImport (Global.GLibNativeDll, CallingConvention = CallingConvention.Cdecl)]
		static extern void g_logv (IntPtr log_domain, LogLevelFlags flags, IntPtr message);
		
		public void WriteLog (string logDomain, LogLevelFlags flags, string format, params object [] args)
		{
			IntPtr ndom = Marshaller.StringToPtrGStrdup (logDomain);
			IntPtr nmessage = Marshaller.StringToPtrGStrdup (String.Format (format, args));
			g_logv (ndom, flags, nmessage);
			Marshaller.Free (ndom);
			Marshaller.Free (nmessage);
		}

		[DllImport (Global.GLibNativeDll, CallingConvention = CallingConvention.Cdecl)]
		static extern uint g_log_set_handler (IntPtr log_domain, LogLevelFlags flags, LogFuncNative log_func, IntPtr user_data);
		
		public static uint SetLogHandler (string logDomain, LogLevelFlags flags, LogFunc logFunc)
		{
			if (native_handler == null)
				native_handler = new LogFuncNative (NativeCallback);

			IntPtr ndom = Marshaller.StringToPtrGStrdup (logDomain);
			GCHandle gch = GCHandle.Alloc (logFunc);
			uint result = g_log_set_handler (ndom, flags, native_handler, (IntPtr) gch);
			Marshaller.Free (ndom);
			EnsureHash ();
			handlers [result] = gch;
			return result;
		}

		[DllImport (Global.GLibNativeDll, CallingConvention = CallingConvention.Cdecl)]
		static extern uint g_log_remove_handler (IntPtr log_domain, uint handler_id);

		public static void RemoveLogHandler (string logDomain, uint handlerID)
		{
			if (handlers != null && handlers.ContainsKey (handlerID)) {
				handlers [handlerID].Free ();
				handlers.Remove (handlerID);
			}
			
			IntPtr ndom = Marshaller.StringToPtrGStrdup (logDomain);
			g_log_remove_handler (ndom, handlerID);
			Marshaller.Free (ndom);
		}

		[DllImport (Global.GLibNativeDll, CallingConvention = CallingConvention.Cdecl)]
		static extern PrintFuncNative g_set_print_handler (PrintFuncNative handler);

		public static PrintFunc SetPrintHandler (PrintFunc handler)
		{
			PrintHelper helper = new PrintHelper (handler);
			PrintFuncNative prev = g_set_print_handler (helper.Handler);
			helper = new PrintHelper (prev);
			return helper.Invoker;
		}
		
		[DllImport (Global.GLibNativeDll, CallingConvention = CallingConvention.Cdecl)]
		static extern PrintFuncNative g_set_printerr_handler (PrintFuncNative handler);

		public static PrintFunc SetPrintErrorHandler (PrintFunc handler)
		{
			PrintHelper helper = new PrintHelper (handler);
			PrintFuncNative prev = g_set_printerr_handler (helper.Handler);
			helper = new PrintHelper (prev);
			return helper.Invoker;
		}
		
		[DllImport (Global.GLibNativeDll, CallingConvention = CallingConvention.Cdecl)]
		static extern void g_log_default_handler (IntPtr log_domain, LogLevelFlags log_level, IntPtr message, IntPtr unused_data);

		public static void DefaultHandler (string logDomain, LogLevelFlags logLevel, string message)
						   
		{
			IntPtr ndom = Marshaller.StringToPtrGStrdup (logDomain);
			IntPtr nmess = Marshaller.StringToPtrGStrdup (message);
			g_log_default_handler (ndom, logLevel, nmess, IntPtr.Zero);
			Marshaller.Free (ndom);
			Marshaller.Free (nmess);
		}

		[DllImport (Global.GLibNativeDll, CallingConvention = CallingConvention.Cdecl)]
		extern static LogLevelFlags g_log_set_always_fatal (LogLevelFlags fatal_mask);
		
		public static LogLevelFlags SetAlwaysFatal (LogLevelFlags fatalMask)
		{
			return g_log_set_always_fatal (fatalMask);
		}

		[DllImport (Global.GLibNativeDll, CallingConvention = CallingConvention.Cdecl)]
		extern static LogLevelFlags g_log_set_fatal_mask (IntPtr log_domain, LogLevelFlags fatal_mask);
		
		public static LogLevelFlags SetAlwaysFatal (string logDomain, LogLevelFlags fatalMask)
		{
			IntPtr ndom = Marshaller.StringToPtrGStrdup (logDomain);
			LogLevelFlags result = g_log_set_fatal_mask (ndom, fatalMask);
			Marshaller.Free (ndom);
			return result;
		}

		class Invoker {

			LogFuncNative native;

			public Invoker (LogFuncNative native)
			{
				this.native = native;
			}

			void Invoke (string log_domain, LogLevelFlags flags, string message)
			{
				IntPtr ndom = Marshaller.StringToPtrGStrdup (log_domain);
				IntPtr nmess = Marshaller.StringToPtrGStrdup (message);
				native (ndom, flags, nmess, IntPtr.Zero);
				Marshaller.Free (ndom);
				Marshaller.Free (nmess);
			}

			public LogFunc Handler {
				get { return new LogFunc (Invoke); }
			}
		}

		[DllImport (Global.GLibNativeDll, CallingConvention = CallingConvention.Cdecl)]
		extern static LogFuncNative g_log_set_default_handler (LogFuncNative log_func, IntPtr user_data);
		
		public static LogFunc SetDefaultHandler (LogFunc log_func)
		{
			if (native_handler == null)
				native_handler = new LogFuncNative (NativeCallback);

			LogFuncNative prev = g_log_set_default_handler (native_handler, (IntPtr) GCHandle.Alloc (log_func));
			if (prev == null)
				return null;
			Invoker invoker = new Invoker (prev);
			return invoker.Handler;
		}

		/*
		 * Some common logging methods.
		 *
		 * Sample usage:
		 *
		 *	// Print the messages for the NULL domain
		 *	LogFunc logFunc = new LogFunc (Log.PrintLogFunction);
		 *	Log.SetLogHandler (null, LogLevelFlags.All, logFunc);
		 *
		 *	// Print messages and stack trace for Gtk critical messages
		 *	logFunc = new LogFunc (Log.PrintTraceLogFunction);
		 *	Log.SetLogHandler ("Gtk", LogLevelFlags.Critical, logFunc);
		 *
		 */

		public static void PrintLogFunction (string domain, LogLevelFlags level, string message)
		{
			Console.WriteLine ("Domain: '{0}' Level: {1}", domain, level);
			Console.WriteLine ("Message: {0}", message);
		}

		public static void PrintTraceLogFunction (string domain, LogLevelFlags level, string message)
		{
			PrintLogFunction (domain, level, message);
			Console.WriteLine ("Trace follows:\n{0}", new System.Diagnostics.StackTrace ());
		}
	}
}

