using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace SystemX.Net.Platform.Common.Event
{
    public interface ISysXEventField : ISynchronizeInvoke
    {
        void ProcessEvent(IObservableEvent evt);
    }

    public class OSCAREventHandler : ISubscribable
    {
        public Subject<IObservableEvent> ApplicationSubject = new Subject<IObservableEvent>();
        protected List<IDisposable> _applicationSubscriptions = new List<IDisposable>();
        public ISysXEventField TheApplicationTarget { get; set; }
        public static OSCAREventHandler TheApplication { get; private set; }

        public OSCAREventHandler(ISysXEventField eventField)
        {
            Contract.Requires(eventField is Form);
            Contract.Requires(TheApplication == null);

            TheApplication = this;
            TheApplicationTarget = eventField;

            var observable = Observable
                 .Interval(TimeSpan.FromSeconds(1))
                 .Take(5);

            var observer = System.Reactive.Observer.Create<long>(
                x => Console.WriteLine("Value published to subject #1: {0}", x),
                () => Console.WriteLine("Sequence Completed."));

            AddSubscription(ApplicationSubject.Subscribe(evt => OnApplicationEvent(evt)));
        }

        /// <summary>
        /// Applicatino event 처리.
        /// Note: ObservableEventTask{Starting, Finished} 는 별도 subscription 을 통해서 처리 됨.
        /// </summary>
        /// <param name="evt"></param>
        private void OnApplicationEvent(IObservableEvent evt)
        {
            // 샘플 코드
            bool onMainThread = !TheApplicationTarget.InvokeRequired;

            //System.Media.SystemSounds.Beep.Play();

            // thread 상에서 호출되었다면, main UI thread 상에서 수행되도록 한다.
            //TheApplicationForm.Do(() =>
            //{
            //logger.Info($"Application got event : {evt}");
            //((IOSCAREventField)TheApplicationForm).HandleStationEvent(evt);
            
            ProcessObservableEventHandler(evt);
            //});
        }

        protected delegate void ObservableEventHandler(IObservableEvent evt);
        private void ProcessObservableEventHandler(IObservableEvent evt)
        {
            if (TheApplicationTarget.InvokeRequired)
            {
                ObservableEventHandler delState = new ObservableEventHandler(ProcessObservableEventHandler);
                (TheApplicationTarget as Control).Invoke(delState, evt);
            }
            else
            {
                TheApplicationTarget.ProcessEvent(evt);
            }
        }

        public void AddSubscription(IDisposable subscription)
        {
            _applicationSubscriptions.Add(subscription);
        }

        public void Dispose()
        {
            Dispose(true);      // true : 명시적 dispose 호출

            GC.SuppressFinalize(this);  // 사용자에 의해서 명시적으로 dispose 되었으므로, GC 는 이 객체에 대해서 손대지 말것을 알림.
        }

        private bool _disposed;
        protected virtual void Dispose(bool disposing)
        {
            if (_disposed)
                return;

            if (disposing)
            {
                // AwaitTermination() call hangs.  Need to check Akka document.
                // CommonApplication.ActorSystem.AwaitTermination();
                _applicationSubscriptions.ForEach(s => s.Dispose());
            }

            _disposed = true;
        }
    }

    public class TestEvent : IObservableEvent
    {
        public string Process { private set; get; }
        public string Path { set; get; }

        public TestEvent(string process, string strPath)
        {
            Process = process;
            Path = strPath;
        }
    }

    /// <summary>
    /// Rx event 에 대한 subscribe 가 가능한 객체가 구현해야 할 interface
    /// Dispose 시에 모든 subscription 을 dispose 해야 한다.
    /// </summary>
    public interface ISubscribable : IDisposable
    {
        /* Usage sample
            private List<IDisposable> _subscriptions = new List<IDisposable>();
            public void AddSubscription(IDisposable subscription) {  _subscriptions.Add(subscription); }

            // Requires disposal of subscriptions on Dispose() method 
         * 
         */
        void AddSubscription(IDisposable subscription);
    }

    /// <summary> Configuration interface </summary>
    [Guid("8501C86A-61E7-4F92-B90A-1779CCF6AAF3")]
    [InterfaceType(ComInterfaceType.InterfaceIsDual)]
    [ComVisible(true)]
    public interface IConfiguration : ICloneable
    {
        /// <summary> Save configuration to file </summary>
        void QSave(string fileName);
        /// <summary> Load configuration from file </summary>
        void QLoad(string fileName);
    }

    /// <summary>
    /// Rx: System.Reactive.Subjects.Subject 에 대한 base interface
    /// </summary>
    [ComVisible(false)]
    public interface IObservableEvent
    {
    }

    public interface IObservableUIEvent : IObservableEvent { }
}
