using MediaBrowser.Common.IO;
using MediaBrowser.Common.Logging;
using MediaBrowser.Model.Dto;
using MediaBrowser.UI.Configuration;
using MediaBrowser.UI.Playback;
using MediaBrowser.UI.Playback.ExternalPlayer;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Threading.Tasks;
namespace MediaBrowser.Plugins.Tmt5
{
    /// 
    /// Class GenericExternalPlayer
    /// 
    [Export(typeof(BaseMediaPlayer))]
    public class Tmt5MediaPlayer : BaseExternalPlayer
    {
        /// 
        /// Gets the name.
        /// 
        /// The name.
        public override string Name
        {
            get { return "TMT5"; }
        }
        /// 
        /// Gets a value indicating whether this instance can pause.
        /// 
        /// true if this instance can pause; otherwise, false.
        public override bool CanPause
        {
            get
            {
                return true;
            }
        }
        /// 
        /// Gets a value indicating whether this instance can close automatically.
        /// 
        /// true if this instance can close automatically; otherwise, false.
        protected override bool CanCloseAutomatically
        {
            get
            {
                return true;
            }
        }
        /// 
        /// Gets the play state directory.
        /// 
        /// The play state directory.
        private string PlayStateDirectory
        {
            get
            {
                return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ArcSoft");
            }
        }
        /// 
        /// The _current position ticks
        /// 
        private long? _currentPositionTicks;
        /// 
        /// Gets the current position ticks.
        /// 
        /// The current position ticks.
        public override long? CurrentPositionTicks
        {
            get
            {
                return _currentPositionTicks;
            }
        }
        /// 
        /// The _current playlist index
        /// 
        private int _currentPlaylistIndex;
        /// 
        /// Gets the index of the current playlist.
        /// 
        /// The index of the current playlist.
        public override int CurrentPlaylistIndex
        {
            get
            {
                return _currentPlaylistIndex;
            }
        }
        /// 
        /// Gets or sets the status file watcher.
        /// 
        /// The status file watcher.
        private FileSystemWatcher StatusFileWatcher { get; set; }
        /// 
        /// Gets or sets a value indicating whether this instance has started playing.
        /// 
        /// true if this instance has started playing; otherwise, false.
        private bool HasStartedPlaying { get; set; }
        /// 
        /// Gets or sets a value indicating whether this instance has stopped playing.
        /// 
        /// true if this instance has stopped playing; otherwise, false.
        private bool HasStoppedPlaying { get; set; }
        /// 
        /// Determines whether this instance can play the specified item.
        /// 
        /// The item.
        /// true if this instance can play the specified item; otherwise, false.
        public override bool CanPlay(BaseItemDto item)
        {
            return item.IsVideo || item.IsAudio;
        }
        /// 
        /// Called when [player stopped internal].
        /// 
        protected override void OnPlayerStoppedInternal()
        {
            DisposeFileSystemWatcher();
            HasStartedPlaying = false;
            HasStoppedPlaying = false;
            _currentPlaylistIndex = 0;
            _currentPositionTicks = 0;
            base.OnPlayerStoppedInternal();
        }
        /// 
        /// Gets the command arguments.
        /// 
        /// The items.
        /// The options.
        /// The player configuration.
        /// System.String.
        protected override string GetCommandArguments(List items, PlayOptions options, PlayerConfiguration playerConfiguration)
        {
            return "\"" + items[0].Path + "\"";
        }
        /// 
        /// Called when [external player launched].
        /// 
        protected override void OnExternalPlayerLaunched()
        {
            base.OnExternalPlayerLaunched();
            // If the playstate directory exists, start watching it
            if (Directory.Exists(PlayStateDirectory))
            {
                ReloadFileSystemWatcher();
            }
        }
        /// 
        /// Pauses the internal.
        /// 
        /// Task.
        protected override Task PauseInternal()
        {
            return SendCommandToMMC("-pause");
        }
        /// 
        /// Uns the pause internal.
        /// 
        /// Task.
        protected override Task UnPauseInternal()
        {
            return SendCommandToMMC("-play");
        }
        /// 
        /// Stops the internal.
        /// 
        /// Task.
        protected override Task StopInternal()
        {
            return SendCommandToMMC("-stop");
        }
        /// 
        /// Closes the player.
        /// 
        /// Task.
        protected Task ClosePlayer()
        {
            return SendCommandToMMC("-close");
        }
        /// 
        /// Seeks the internal.
        /// 
        /// The position ticks.
        /// Task.
        /// No media to seek to
        protected override Task SeekInternal(long positionTicks)
        {
            if (CurrentMedia == null)
            {
                throw new InvalidOperationException("No media to seek to");
            }
            if (CurrentMedia.Chapters == null)
            {
                throw new InvalidOperationException("TMT5 cannot seek without chapter information");
            }
            var chapterIndex = 0;
            for (var i = 0; i < CurrentMedia.Chapters.Count; i++)
            {
                if (CurrentMedia.Chapters[i].StartPositionTicks < positionTicks)
                {
                    chapterIndex = i;
                }
            }
            return JumpToChapter(chapterIndex);
        }
        /// 
        /// Jumps to chapter.
        /// 
        /// The chapter.
        /// Task.
        protected Task JumpToChapter(int chapter)
        {
            return SendCommandToMMC(" -chapter " + chapter);
        }
        /// 
        /// Sends an arbitrary command to the TMT MMC console
        /// 
        /// The command.
        /// Task.
        protected Task SendCommandToMMC(string command)
        {
            return Task.Run(() =>
            {
                var directory = Path.GetDirectoryName(CurrentPlayerConfiguration.Command);
                var processInfo = new ProcessStartInfo
                {
                    FileName = Path.Combine(directory, "MMCEDT5.exe"),
                    Arguments = command,
                    CreateNoWindow = true
                };
                Logger.Debug("{0} {1}", processInfo.FileName, processInfo.Arguments);
                using (var process = Process.Start(processInfo))
                {
                    process.WaitForExit(2000);
                }
            });
        }
        /// 
        /// Reloads the file system watcher.
        /// 
        private void ReloadFileSystemWatcher()
        {
            DisposeFileSystemWatcher();
            Logger.Info("Watching TMT folder: " + PlayStateDirectory);
            StatusFileWatcher = new FileSystemWatcher(PlayStateDirectory, "*.set")
            {
                IncludeSubdirectories = true
            };
            // Need to include subdirectories since there are subfolders undearneath this with the TMT version #.
            StatusFileWatcher.Changed += StatusFileWatcher_Changed;
            StatusFileWatcher.EnableRaisingEvents = true;
        }
        private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
        /// 
        /// Handles the Changed event of the StatusFileWatcher control.
        /// 
        /// The source of the event.
        /// The  instance containing the event data.
        async void StatusFileWatcher_Changed(object sender, FileSystemEventArgs e)
        {
            Logger.Debug("TMT File Watcher reports change type {1} at {0}", e.FullPath, e.ChangeType);
            NameValueCollection values;
            try
            {
                values = FileSystem.ParseIniFile(e.FullPath);
            }
            catch (IOException)
            {
                // This can happen if the file is being written to at the exact moment we're trying to access it
                // Unfortunately we kind of have to just eat it
                return;
            }
            var tmtPlayState = values["State"];
            if (tmtPlayState.Equals("play", StringComparison.OrdinalIgnoreCase))
            {
                PlayState = PlayState.Playing;
                // Playback just started
                HasStartedPlaying = true;
                if (CurrentPlayOptions.StartPositionTicks > 0)
                {
                    SeekInternal(CurrentPlayOptions.StartPositionTicks);
                }
            }
            else if (tmtPlayState.Equals("pause", StringComparison.OrdinalIgnoreCase))
            {
                PlayState = PlayState.Paused;
            }
            // If playback has previously started...
            // First notify the Progress event handler
            // Then check if playback has stopped
            if (HasStartedPlaying)
            {
                TimeSpan currentPosition;
                //TimeSpan.TryParse(values["TotalTime"], out currentDuration);
                if (TimeSpan.TryParse(values["CurTime"], UsCulture, out currentPosition))
                {
                    _currentPositionTicks = currentPosition.Ticks;
                }
                _currentPlaylistIndex = 0;
                // Playback has stopped
                if (tmtPlayState.Equals("stop", StringComparison.OrdinalIgnoreCase))
                {
                    Logger.Info("Playstate changed to stopped");
                    if (!HasStoppedPlaying)
                    {
                        HasStoppedPlaying = true;
                        DisposeFileSystemWatcher();
                        await ClosePlayer().ConfigureAwait(false);
                    }
                }
            }
        }
        /// 
        /// Disposes the file system watcher.
        /// 
        private void DisposeFileSystemWatcher()
        {
            if (StatusFileWatcher != null)
            {
                StatusFileWatcher.EnableRaisingEvents = false;
                StatusFileWatcher.Changed -= StatusFileWatcher_Changed;
                StatusFileWatcher.Dispose();
                StatusFileWatcher = null;
            }
        }
        /// 
        /// Releases unmanaged and - optionally - managed resources.
        /// 
        /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
        protected override void Dispose(bool dispose)
        {
            if (dispose)
            {
                DisposeFileSystemWatcher();
            }
            base.Dispose(dispose);
        }
    }
}