/*
 * Event.cs --
 *
 * Copyright (c) 2007-2012 by Joe Mistachkin.  All rights reserved.
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: $
 */

using System;
using System.Threading;
using Eagle._Attributes;
using Eagle._Components.Private;
using Eagle._Components.Public.Delegates;
using Eagle._Constants;
using Eagle._Containers.Public;
using Eagle._Interfaces.Public;

namespace Eagle._Components.Public
{
    [ObjectId("d46c23bf-20b3-4f17-8869-126dcbdb265d")]
    public sealed class Event : IEvent, IDisposable
    {
        #region Private Constructors
        internal Event(
            object syncRoot,
            Delegate @delegate,
            EventType type,
            EventFlags flags,
            EventPriority priority,
            Interpreter interpreter,
            string name,
            DateTime dateTime,
            EventCallback callback,
            IClientData clientData
            )
        {
            this.syncRoot = syncRoot;
            this.@delegate = @delegate;
            this.type = type;
            this.flags = flags;
            this.priority = priority;
            this.interpreter = interpreter;
            this.name = name;
            this.dateTime = dateTime;
            this.callback = callback;
            this.clientData = clientData;

            //
            // NOTE: Setup inter-thread communication event.
            //
            doneEventName = FormatOps.EventName(typeof(Event).Name, null,
                GlobalState.NextEventId(interpreter));

            doneEvent = ThreadOps.CreateEvent(doneEventName);

            //
            // NOTE: Setup the initial result state.
            //
            returnCode = ReturnCode.Ok;
            result = null;
            errorLine = 0;
        }
        #endregion

        ///////////////////////////////////////////////////////////////////////

        #region Private Data
        private string doneEventName; /* NOTE: Write-once, then read-only. */
        private EventWaitHandle doneEvent;
        private ReturnCode returnCode;
        private Result result;
        private int errorLine;
        #endregion

        ///////////////////////////////////////////////////////////////////////

        #region IIdentifier Members
        private IdentifierKind kind;
        public IdentifierKind Kind
        {
            get { CheckDisposed(); return kind; }
            set { CheckDisposed(); kind = value; }
        }

        ///////////////////////////////////////////////////////////////////////

        private Guid id;
        public Guid Id
        {
            get { CheckDisposed(); return id; }
            set { CheckDisposed(); id = value; }
        }

        ///////////////////////////////////////////////////////////////////////

        private string name;
        public string Name
        {
            get { CheckDisposed(); return name; }
            set { CheckDisposed(); name = value; }
        }

        ///////////////////////////////////////////////////////////////////////

        private string group;
        public string Group
        {
            get { CheckDisposed(); return group; }
            set { CheckDisposed(); group = value; }
        }

        ///////////////////////////////////////////////////////////////////////

        private string description;
        public string Description
        {
            get { CheckDisposed(); return description; }
            set { CheckDisposed(); description = value; }
        }
        #endregion

        ///////////////////////////////////////////////////////////////////////

        #region IHaveClientData Members
        private IClientData clientData;
        public IClientData ClientData
        {
            get { CheckDisposed(); return clientData; }
            set { CheckDisposed(); clientData = value; }
        }
        #endregion

        ///////////////////////////////////////////////////////////////////////

        #region IGetInterpreter Members
        private Interpreter interpreter;
        public Interpreter Interpreter
        {
            get { CheckDisposed(); return interpreter; }
        }
        #endregion

        ///////////////////////////////////////////////////////////////////////

        #region IEvent Members
        private object syncRoot;
        public object SyncRoot
        {
            get { CheckDisposed(); return syncRoot; }
        }

        ///////////////////////////////////////////////////////////////////////

        private Delegate @delegate;
        public Delegate Delegate
        {
            get { CheckDisposed(); return @delegate; }
        }

        ///////////////////////////////////////////////////////////////////////

        private EventType type;
        public EventType Type
        {
            get { CheckDisposed(); return type; }
        }

        ///////////////////////////////////////////////////////////////////////

        private EventFlags flags;
        public EventFlags Flags
        {
            get { CheckDisposed(); return flags; }
        }

        ///////////////////////////////////////////////////////////////////////

        private EventPriority priority;
        public EventPriority Priority
        {
            get { CheckDisposed(); return priority; }
        }

        ///////////////////////////////////////////////////////////////////////

        private DateTime dateTime;
        public DateTime DateTime
        {
            get { CheckDisposed(); return dateTime; }
        }

        ///////////////////////////////////////////////////////////////////////

        private EventCallback callback;
        public EventCallback Callback
        {
            get { CheckDisposed(); return callback; }
        }

        ///////////////////////////////////////////////////////////////////////

        public bool GetResult(
            bool wait,                  /* in */
            ref ReturnCode returnCode,  /* out */
            ref Result result,          /* out */
            ref int errorLine,          /* out */
            ref Result error            /* out */
            )
        {
            CheckDisposed();

            try
            {
                if (wait)
                {
                    EventWaitHandle doneEvent =
                        ThreadOps.OpenEvent(doneEventName);

                    try
                    {
                        if (doneEvent != null)
                            ThreadOps.WaitEvent(doneEvent);
                    }
                    finally
                    {
                        ThreadOps.CloseEvent(ref doneEvent);
                    }
                }

                object syncRoot = this.syncRoot;

                if (syncRoot != null)
                    Monitor.Enter(syncRoot);

                try
                {
                    returnCode = this.returnCode;
                    result = this.result;
                    errorLine = this.errorLine;
                }
                finally
                {
                    if (syncRoot != null)
                        Monitor.Exit(syncRoot);
                }

                return true;
            }
            catch (Exception e)
            {
                error = e;
            }

            return false;
        }

        ///////////////////////////////////////////////////////////////////////

        public bool ResetResult(
            bool signal,     /* in */
            ref Result error /* out */
            )
        {
            CheckDisposed();

            try
            {
                object syncRoot = this.syncRoot;

                if (syncRoot != null)
                    Monitor.Enter(syncRoot);

                try
                {
                    this.returnCode = ReturnCode.Ok;
                    this.result = null;
                    this.errorLine = 0;
                }
                finally
                {
                    if (syncRoot != null)
                        Monitor.Exit(syncRoot);
                }

                if (signal)
                {
                    EventWaitHandle doneEvent =
                        ThreadOps.OpenEvent(doneEventName);

                    try
                    {
                        if (doneEvent != null)
                            ThreadOps.ResetEvent(doneEvent);
                    }
                    finally
                    {
                        ThreadOps.CloseEvent(ref doneEvent);
                    }
                }

                return true;
            }
            catch (Exception e)
            {
                error = e;
            }

            return false;
        }

        ///////////////////////////////////////////////////////////////////////

        public bool SetResult(
            bool signal,            /* in */
            ReturnCode returnCode,  /* in */
            Result result,          /* in */
            int errorLine,          /* in */
            ref Result error        /* out */
            )
        {
            CheckDisposed();

            try
            {
                object syncRoot = this.syncRoot;

                if (syncRoot != null)
                    Monitor.Enter(syncRoot);

                try
                {
                    this.returnCode = returnCode;
                    this.result = result;
                    this.errorLine = errorLine;
                }
                finally
                {
                    if (syncRoot != null)
                        Monitor.Exit(syncRoot);
                }

                if (signal)
                {
                    EventWaitHandle doneEvent =
                        ThreadOps.OpenEvent(doneEventName);

                    try
                    {
                        if (doneEvent != null)
                            ThreadOps.SetEvent(doneEvent);
                    }
                    finally
                    {
                        ThreadOps.CloseEvent(ref doneEvent);
                    }
                }

                return true;
            }
            catch (Exception e)
            {
                error = e;
            }

            return false;
        }

        ///////////////////////////////////////////////////////////////////////

        public StringPairList ToList()
        {
            CheckDisposed();

            StringPairList list = new StringPairList();

            list.Add("name", (name != null) ? name : _String.Null);

            list.Add("interpreter", (interpreter != null) ?
                interpreter.ToString() : _String.Null);

            if (!id.Equals(Guid.Empty))
                list.Add("id", id.ToString());

            list.Add("type", type.ToString());
            list.Add("flags", flags.ToString());
            list.Add("priority", priority.ToString());
            list.Add("dateTime", dateTime.ToString());

            list.Add("callback", (callback != null) ?
                callback.ToString() : _String.Null);

            list.Add("clientData",
                ((clientData != null) && (clientData.Data != null)) ?
                    StringList.MakeList(clientData.Data.GetType().Name,
                        clientData.Data) :
                    _String.Null);

            return list;
        }
        #endregion

        ///////////////////////////////////////////////////////////////////////

        #region System.Object Overrides
        public override string ToString()
        {
            CheckDisposed();

            return (name != null) ? name : String.Empty;
        }
        #endregion

        ///////////////////////////////////////////////////////////////////////

        #region IDisposable "Pattern" Members
        private bool disposed;
        private void CheckDisposed() /* throw */
        {
#if THROW_ON_DISPOSED
            if (disposed && Engine.IsThrowOnDisposed(interpreter, true))
                throw new ObjectDisposedException(typeof(Event).Name);
#endif
        }

        ///////////////////////////////////////////////////////////////////////

        private /* protected virtual */ void Dispose(bool disposing) /* throw */
        {
            if (!disposed)
            {
                if (disposing)
                {
                    ////////////////////////////////////
                    // dispose managed resources here...
                    ////////////////////////////////////

                    //
                    // NOTE: Dispose of the "done" inter-thread communication
                    //       event.
                    //
                    ThreadOps.CloseEvent(ref doneEvent);
                }

                //////////////////////////////////////
                // release unmanaged resources here...
                //////////////////////////////////////

                disposed = true;
            }
        }
        #endregion

        ///////////////////////////////////////////////////////////////////////

        #region IDisposable Members
        public void Dispose() /* throw */
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        #endregion

        ///////////////////////////////////////////////////////////////////////

        #region Destructor
        ~Event()
        {
            Dispose(false);
        }
        #endregion
    }
}
