// ----------------------------------------------------------------------------
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ----------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Build.Framework;

#if ENABLE_INTEROP
using System.Runtime.InteropServices;
using System.Threading;
using Windows;
#endif

namespace Microsoft.Singularity.BuildTasks
{
    public abstract class TaskBase : Microsoft.Build.Framework.ITask
    {
        protected TaskBase()
        {
            _taskname = null;
            _disableMessagePumping = true;
        }

        string _taskname;

        protected string TaskName
        {
            get {
                if (_taskname == null) {
                    _taskname = this.GetType().Name;
                }
                return _taskname;
            }
            set { _taskname = value; }
        }

        protected void LogMessage(string msg)
        {
            BuildMessageEventArgs e = new BuildMessageEventArgs(msg, null, this.TaskName, MessageImportance.Normal);
            _engine.LogMessageEvent(e);
        }

        protected void LogError(string msg)
        {
            BuildErrorEventArgs e = new BuildErrorEventArgs(null, null, null, 0, 0, 0, 0, msg, null, this.TaskName);
            _engine.LogErrorEvent(e);
        }

        protected void LogMessage(MessageImportance importance, string msg)
        {
            BuildMessageEventArgs e = new BuildMessageEventArgs(msg, null, this.TaskName, importance);
            _engine.LogMessageEvent(e);
        }

        protected abstract bool Execute();

        bool ITask.Execute()
        {
            if (_engine == null)
                throw new InvalidOperationException("The BuildEngine property must be set before calling the Execute method.");

#if false
            if (_host == null)
                throw new InvalidOperationException("The Host property must be set before calling the Execute method.");
#endif

            if (_disableMessagePumping)
                return this.Execute();

            bool returnValue = false;
            ExecuteMethodAndPump(delegate() { returnValue = this.Execute(); });
            return returnValue;
        }

        ITaskHost _host;
        IBuildEngine _engine;

        public IBuildEngine BuildEngine
        {
            get { return _engine; }
            set { _engine = value; }
        }

        public ITaskHost HostObject
        {
            get { return _host; }
            set { _host = value; }
        }

        bool _disableMessagePumping;

        public bool DisableMessagePumping
        {
            get { return _disableMessagePumping; }
            set { _disableMessagePumping = value; }
        }

#if ENABLE_INTEROP
        #region Code for pumping messages while executing tasks

        unsafe void ExecuteMethodAndPump(SimpleMethod method)
        {
            if (method == null)
                throw new ArgumentNullException("method");

            if (_disableMessagePumping) {
                LogMessage("Message pumping is disabled - executing method directly");
                method();
                return;
            }

            if (Thread.CurrentThread.GetApartmentState() == ApartmentState.MTA) {
                LogMessage("Message pumping is disabled - this thread is an MTA thread");
                method();
                return;
            }

            bool quit = false;
            uint mainThreadId = Kernel32.GetCurrentThreadId();
            // LogMessage("main thread id = " + mainThreadId);
            Exception worker_exception = null;

            Thread thread = new Thread(delegate()
            {
                try {
                    method();
                    worker_exception = null;
                }
                catch (Exception ex) {
                    worker_exception = ex;
                }
                quit = true;
                User32.PostThreadMessageW(mainThreadId, User32.WM_NULL, IntPtr.Zero, IntPtr.Zero);
            });

            thread.Start();

            for (;;) {
                if (quit) {
                    // LogMessage("Main thread received quit notification.  Quitting.");
                    break;
                }

                MSG msg = new MSG();
                if (Windows.User32.GetMessageW(& msg, IntPtr.Zero, 0, 0)) {
                    User32.TranslateMessage(&msg);
                    User32.DispatchMessageW(&msg);
                }
                else {
                    LogMessage("Received WM_QUIT or GetMessage failed!  Bailing.");
                    break;
                }
            }

            // LogMessage("Waiting for worker thread to finish...");
            thread.Join();
            // LogMessage("Worker thread is done.");

            if (worker_exception != null) {
                throw worker_exception;
            }
        }

        void ExecuteMethodAndPumpThreadMain(object untyped_method)
        {
            SimpleMethod method = (SimpleMethod)untyped_method;
        }

        #endregion
#endif


        #region Helper methods that don't have a better place to live.

        protected bool ValidateCSharpIdentifier(string name, string id, bool allowDots)
        {
            if (String.IsNullOrEmpty(id)) {
                LogError(String.Format("The parameter '{0}' is required, but no value was provided.", name));
                return false;
            }

            if (!Util.ValidateCSharpIdentifier(id, allowDots)) {
                LogError(String.Format("The value '{0}' provided to parameter '{1}' is not a valid C# identifier.", id, name));
                return false;
            }

            return true;
        }

        #endregion


    }

    delegate void SimpleMethod();
}
