using System;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
using TweetDuck.Core.Controls;
using TweetDuck.Core.Other;
using TweetDuck.Core.Utils;
using TweetLib.Communication;

namespace TweetDuck.Core.Management{
    sealed class VideoPlayer : IDisposable{
        public bool Running => currentInstance != null && currentInstance.Running;

        public event EventHandler ProcessExited;

        private readonly FormBrowser owner;

        private Instance currentInstance;
        private bool isClosing;

        public VideoPlayer(FormBrowser owner){
            this.owner = owner;
            this.owner.FormClosing += owner_FormClosing;
        }

        public void Launch(string url, string username){
            if (Running){
                Destroy();
                isClosing = false;
            }
            
            try{
                DuplexPipe.Server pipe = DuplexPipe.CreateServer();
                pipe.DataIn += pipe_DataIn;

                Process process;

                if ((process = Process.Start(new ProcessStartInfo{
                    FileName = Path.Combine(Program.ProgramPath, "TweetDuck.Video.exe"),
                    Arguments = $"{owner.Handle} {Program.UserConfig.VideoPlayerVolume} \"{url}\" \"{pipe.GenerateToken()}\"",
                    UseShellExecute = false,
                    RedirectStandardOutput = true
                })) != null){
                    currentInstance = new Instance(process, pipe, url, username);

                    process.EnableRaisingEvents = true;
                    process.Exited += process_Exited;
                    
                    process.BeginOutputReadLine();
                    process.OutputDataReceived += process_OutputDataReceived;

                    pipe.DisposeToken();
                }
                else{
                    pipe.DataIn -= pipe_DataIn;
                    pipe.Dispose();
                }
            }catch(Exception e){
                Program.Reporter.HandleException("Video Playback Error", "Error launching video player.", true, e);
            }
        }

        public void SendKeyEvent(Keys key){
            currentInstance?.Pipe.Write("key", ((int)key).ToString());
        }

        private void pipe_DataIn(object sender, DuplexPipe.PipeReadEventArgs e){
            owner.InvokeSafe(() => {
                switch(e.Key){
                    case "vol":
                        if (int.TryParse(e.Data, out int volume) && volume != Program.UserConfig.VideoPlayerVolume){
                            Program.UserConfig.VideoPlayerVolume = volume;
                            Program.UserConfig.Save();
                        }

                        break;

                    case "download":
                        if (currentInstance != null){
                            owner.AnalyticsFile.DownloadedVideos.Trigger();
                            TwitterUtils.DownloadVideo(currentInstance.Url, currentInstance.Username);
                        }

                        break;

                    case "rip":
                        currentInstance?.Dispose();
                        currentInstance = null;

                        isClosing = false;
                        TriggerProcessExitEventUnsafe();
                        break;
                }
            });
        }

        public void Close(){
            if (currentInstance != null){
                if (isClosing){
                    Destroy();
                    isClosing = false;
                }
                else{
                    isClosing = true;
                    currentInstance.Process.Exited -= process_Exited;
                    currentInstance.Pipe.Write("die");
                }
            }
        }

        public void Dispose(){
            ProcessExited = null;

            isClosing = true;
            Destroy();
        }

        private void Destroy(){
            if (currentInstance != null){
                currentInstance.KillAndDispose();
                currentInstance = null;

                TriggerProcessExitEventUnsafe();
            }
        }

        private void owner_FormClosing(object sender, FormClosingEventArgs e){
            if (currentInstance != null){
                currentInstance.Process.Exited -= process_Exited;
            }
        }

        private void process_OutputDataReceived(object sender, DataReceivedEventArgs e){
            if (!string.IsNullOrEmpty(e.Data)){
                Program.Reporter.Log("[VideoPlayer] "+e.Data);
            }
        }

        private void process_Exited(object sender, EventArgs e){
            if (currentInstance == null){
                return;
            }

            int exitCode = currentInstance.Process.ExitCode;
            string url = currentInstance.Url;

            currentInstance.Dispose();
            currentInstance = null;

            switch(exitCode){
                case 3: // CODE_LAUNCH_FAIL
                    if (FormMessage.Error("Video Playback Error", "Error launching video player, this may be caused by missing Windows Media Player. Do you want to open the video in your browser?", FormMessage.Yes, FormMessage.No)){
                        BrowserUtils.OpenExternalBrowser(url);
                    }

                    break;

                case 4: // CODE_MEDIA_ERROR
                    if (FormMessage.Error("Video Playback Error", "The video could not be loaded, most likely due to unknown format. Do you want to open the video in your browser?", FormMessage.Yes, FormMessage.No)){
                        BrowserUtils.OpenExternalBrowser(url);
                    }

                    break;
            }
            
            owner.InvokeAsyncSafe(TriggerProcessExitEventUnsafe);
        }

        private void TriggerProcessExitEventUnsafe(){
            ProcessExited?.Invoke(this, EventArgs.Empty);
        }

        private sealed class Instance : IDisposable{
            public bool Running{
                get{
                    Process.Refresh();
                    return !Process.HasExited;
                }
            }

            public Process Process { get; }
            public DuplexPipe.Server Pipe { get; }

            public string Url { get; }
            public string Username { get; }

            public Instance(Process process, DuplexPipe.Server pipe, string url, string username){
                this.Process = process;
                this.Pipe = pipe;
                this.Url = url;
                this.Username = username;
            }

            public void KillAndDispose(){
                try{
                    Process.Kill();
                }catch{
                    // kill me instead then
                }

                Dispose();
            }

            public void Dispose(){
                Process.Dispose();
                Pipe.Dispose();
            }
        }
    }
}