using MediaBrowser.ApiInteraction;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Kernel;
using MediaBrowser.Common.Logging;
using MediaBrowser.Common.UI;
using MediaBrowser.IsoMounter;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Weather;
using MediaBrowser.UI.Controller;
using MediaBrowser.UI.Controls;
using MediaBrowser.UI.Pages;
using MediaBrowser.UI.Uninstall;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
namespace MediaBrowser.UI
{
    /// 
    /// Interaction logic for App.xaml
    /// 
    public partial class App : BaseApplication, IApplication
    {
        /// 
        /// Gets or sets the clock timer.
        /// 
        /// The clock timer.
        private Timer ClockTimer { get; set; }
        /// 
        /// Gets or sets the server configuration timer.
        /// 
        /// The server configuration timer.
        private Timer ServerConfigurationTimer { get; set; }
        /// 
        /// Gets the name of the product.
        /// 
        /// The name of the product.
        protected override string ProductName
        {
            get { return Globals.ProductName; }
        }
        /// 
        /// Gets the name of the publisher.
        /// 
        /// The name of the publisher.
        protected override string PublisherName
        {
            get { return Globals.PublisherName; }
        }
        /// 
        /// Gets the name of the suite.
        /// 
        /// The name of the suite.
        protected override string SuiteName
        {
            get { return Globals.SuiteName; }
        }
        /// 
        /// Gets the name of the uninstaller file.
        /// 
        /// The name of the uninstaller file.
        protected override string UninstallerFileName
        {
            get { return "MediaBrowser.UI.Uninstall.exe"; }
        }
        /// 
        /// Gets the instance.
        /// 
        /// The instance.
        public static App Instance
        {
            get
            {
                return Current as App;
            }
        }
        /// 
        /// Gets the API client.
        /// 
        /// The API client.
        public ApiClient ApiClient
        {
            get { return UIKernel.Instance.ApiClient; }
        }
        /// 
        /// Gets the application window.
        /// 
        /// The application window.
        public MainWindow ApplicationWindow { get; private set; }
        /// 
        /// Gets the hidden window.
        /// 
        /// The hidden window.
        public HiddenWindow HiddenWindow { get; private set; }
        /// 
        /// The _current user
        /// 
        private UserDto _currentUser;
        /// 
        /// Gets or sets the current user.
        /// 
        /// The current user.
        public UserDto CurrentUser
        {
            get
            {
                return _currentUser;
            }
            set
            {
                _currentUser = value;
                if (UIKernel.Instance.ApiClient != null)
                {
                    if (value == null)
                    {
                        UIKernel.Instance.ApiClient.CurrentUserId = null;
                    }
                    else
                    {
                        UIKernel.Instance.ApiClient.CurrentUserId = value.Id;
                    }
                }
                OnPropertyChanged("CurrentUser");
            }
        }
        /// 
        /// The _server configuration
        /// 
        private ServerConfiguration _serverConfiguration;
        /// 
        /// Gets or sets the server configuration.
        /// 
        /// The server configuration.
        public ServerConfiguration ServerConfiguration
        {
            get
            {
                return _serverConfiguration;
            }
            set
            {
                _serverConfiguration = value;
                OnPropertyChanged("ServerConfiguration");
            }
        }
        /// 
        /// The _current time
        /// 
        private DateTime _currentTime = DateTime.Now;
        /// 
        /// Gets the current time.
        /// 
        /// The current time.
        public DateTime CurrentTime
        {
            get
            {
                return _currentTime;
            }
            private set
            {
                _currentTime = value;
                OnPropertyChanged("CurrentTime");
            }
        }
        /// 
        /// The _current weather
        /// 
        private WeatherInfo _currentWeather;
        /// 
        /// Gets the current weather.
        /// 
        /// The current weather.
        public WeatherInfo CurrentWeather
        {
            get
            {
                return _currentWeather;
            }
            private set
            {
                _currentWeather = value;
                OnPropertyChanged("CurrentWeather");
            }
        }
        /// 
        /// The _current theme
        /// 
        private BaseTheme _currentTheme;
        /// 
        /// Gets the current theme.
        /// 
        /// The current theme.
        public BaseTheme CurrentTheme
        {
            get
            {
                return _currentTheme;
            }
            private set
            {
                _currentTheme = value;
                OnPropertyChanged("CurrentTheme");
            }
        }
        /// 
        /// Defines the entry point of the application.
        /// 
        [STAThread]
        public static void Main()
        {
            RunApplication("MediaBrowserUI");
        }
        /// 
        /// Instantiates the kernel.
        /// 
        /// IKernel.
        protected override IKernel InstantiateKernel()
        {
            return new UIKernel(new PismoIsoManager(LogManager.GetLogger("PismoIsoManager")));
        }
        /// 
        /// Instantiates the main window.
        /// 
        /// Window.
        protected override Window InstantiateMainWindow()
        {
            HiddenWindow = new HiddenWindow { };
            return HiddenWindow;
        }
        /// 
        /// Shows the application window.
        /// 
        private void ShowApplicationWindow()
        {
            var win = new MainWindow { };
            var config = UIKernel.Instance.Configuration;
            // Restore window position/size
            if (config.WindowState.HasValue)
            {
                // Set window state
                win.WindowState = config.WindowState.Value;
                // Set position if not maximized
                if (config.WindowState.Value != WindowState.Maximized)
                {
                    double left = 0;
                    double top = 0;
                    // Set left
                    if (config.WindowLeft.HasValue)
                    {
                        win.WindowStartupLocation = WindowStartupLocation.Manual;
                        win.Left = left = Math.Max(config.WindowLeft.Value, 0);
                    }
                    // Set top
                    if (config.WindowTop.HasValue)
                    {
                        win.WindowStartupLocation = WindowStartupLocation.Manual;
                        win.Top = top = Math.Max(config.WindowTop.Value, 0);
                    }
                    // Set width
                    if (config.WindowWidth.HasValue)
                    {
                        win.Width = Math.Min(config.WindowWidth.Value, SystemParameters.VirtualScreenWidth - left);
                    }
                    // Set height
                    if (config.WindowHeight.HasValue)
                    {
                        win.Height = Math.Min(config.WindowHeight.Value, SystemParameters.VirtualScreenHeight - top);
                    }
                }
            }
            win.LocationChanged += ApplicationWindow_LocationChanged;
            win.StateChanged += ApplicationWindow_LocationChanged;
            win.SizeChanged += ApplicationWindow_LocationChanged;
            ApplicationWindow = win;
            ApplicationWindow.Show();
            ApplicationWindow.Owner = HiddenWindow;
            SyncHiddenWindowLocation();
        }
        /// 
        /// Handles the LocationChanged event of the ApplicationWindow control.
        /// 
        /// The source of the event.
        /// The  instance containing the event data.
        void ApplicationWindow_LocationChanged(object sender, EventArgs e)
        {
            SyncHiddenWindowLocation();
        }
        /// 
        /// Syncs the hidden window location.
        /// 
        public void SyncHiddenWindowLocation()
        {
            HiddenWindow.Width = ApplicationWindow.Width;
            HiddenWindow.Height = ApplicationWindow.Height;
            HiddenWindow.Top = ApplicationWindow.Top;
            HiddenWindow.Left = ApplicationWindow.Left;
            HiddenWindow.WindowState = ApplicationWindow.WindowState;
            ApplicationWindow.Activate();
        }
        /// 
        /// Loads the kernel.
        /// 
        protected override async void LoadKernel()
        {
            // Without this the app will shutdown after the splash screen closes
            ShutdownMode = ShutdownMode.OnExplicitShutdown;
            Kernel = InstantiateKernel();
            try
            {
                var now = DateTime.UtcNow;
                await Kernel.Init();
                Logger.Info("Kernel.Init completed in {0} seconds.", (DateTime.UtcNow - now).TotalSeconds);
                ShutdownMode = System.Windows.ShutdownMode.OnLastWindowClose;
                await OnKernelLoaded();
                InstantiateMainWindow().Show();
                ShowApplicationWindow();
                await ApplicationWindow.LoadInitialUI().ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                Logger.ErrorException("Error launching application", ex);
                MessageBox.Show("There was an error launching Media Browser: " + ex.Message);
                // Shutdown the app with an error code
                Shutdown(1);
            }
        }
        /// 
        /// Called when [kernel loaded].
        /// 
        /// Task.
        protected override async Task OnKernelLoaded()
        {
            await base.OnKernelLoaded().ConfigureAwait(false);
            PropertyChanged += AppPropertyChanged;
            // Update every 10 seconds
            ClockTimer = new Timer(ClockTimerCallback, null, 0, 10000);
            // Update every 30 minutes
            ServerConfigurationTimer = new Timer(ServerConfigurationTimerCallback, null, 0, 1800000);
            CurrentTheme = UIKernel.Instance.Themes.First();
            foreach (var resource in CurrentTheme.GetGlobalResources())
            {
                Resources.MergedDictionaries.Add(resource);
            }
        }
        /// 
        /// Raises the  event.
        /// 
        /// An  that contains the event data.
        protected override void OnExit(ExitEventArgs e)
        {
            var win = ApplicationWindow;
            if (win != null)
            {
                // Save window position
                var config = UIKernel.Instance.Configuration;
                config.WindowState = win.WindowState;
                config.WindowTop = win.Top;
                config.WindowLeft = win.Left;
                config.WindowWidth = win.Width;
                config.WindowHeight = win.Height;
                UIKernel.Instance.SaveConfiguration();
            }
            base.OnExit(e);
        }
        /// 
        /// Apps the property changed.
        /// 
        /// The sender.
        /// The  instance containing the event data.
        async void AppPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName.Equals("ServerConfiguration"))
            {
                if (string.IsNullOrEmpty(ServerConfiguration.WeatherLocation))
                {
                    CurrentWeather = null;
                }
                else
                {
                    try
                    {
                        CurrentWeather = await ApiClient.GetWeatherInfoAsync(ServerConfiguration.WeatherLocation);
                    }
                    catch (HttpException ex)
                    {
                        Logger.ErrorException("Error downloading weather information", ex);
                    }
                }
            }
        }
        /// 
        /// Clocks the timer callback.
        /// 
        /// The state info.
        private void ClockTimerCallback(object stateInfo)
        {
            CurrentTime = DateTime.Now;
        }
        /// 
        /// Servers the configuration timer callback.
        /// 
        /// The state info.
        private async void ServerConfigurationTimerCallback(object stateInfo)
        {
            try
            {
                ServerConfiguration = await ApiClient.GetServerConfigurationAsync();
            }
            catch (HttpException ex)
            {
                Logger.ErrorException("Error refreshing server configuration", ex);
            }
        }
        /// 
        /// Logouts the user.
        /// 
        /// Task.
        public async Task LogoutUser()
        {
            CurrentUser = null;
            await Dispatcher.InvokeAsync(() => Navigate(CurrentTheme.GetLoginPage()));
        }
        /// 
        /// Navigates the specified page.
        /// 
        /// The page.
        public void Navigate(Page page)
        {
            _remoteImageCache = new FileSystemRepository(UIKernel.Instance.ApplicationPaths.RemoteImageCachePath);
            ApplicationWindow.Navigate(page);
        }
        /// 
        /// Navigates to settings page.
        /// 
        public void NavigateToSettingsPage()
        {
            Navigate(new SettingsPage());
        }
        /// 
        /// Navigates to internal player page.
        /// 
        public void NavigateToInternalPlayerPage()
        {
            Navigate(CurrentTheme.GetInternalPlayerPage());
        }
        /// 
        /// Navigates to image viewer.
        /// 
        /// The image URL.
        /// The caption.
        public void OpenImageViewer(Uri imageUrl, string caption)
        {
            var tuple = new Tuple(imageUrl, caption);
            OpenImageViewer(new[] { tuple });
        }
        /// 
        /// Navigates to image viewer.
        /// 
        /// The images.
        public void OpenImageViewer(IEnumerable> images)
        {
            new ImageViewerWindow(images).ShowModal(ApplicationWindow);
        }
        /// 
        /// Navigates to item.
        /// 
        /// The item.
        public void NavigateToItem(BaseItemDto item)
        {
            if (item.IsRoot.HasValue && item.IsRoot.Value)
            {
                NavigateToHomePage();
            }
            else if (item.IsFolder)
            {
                Navigate(CurrentTheme.GetListPage(item));
            }
            else
            {
                Navigate(CurrentTheme.GetDetailPage(item));
            }
        }
        /// 
        /// Displays the weather.
        /// 
        public void DisplayWeather()
        {
            CurrentTheme.DisplayWeather();
        }
        /// 
        /// Navigates to home page.
        /// 
        public void NavigateToHomePage()
        {
            Navigate(CurrentTheme.GetHomePage());
        }
        /// 
        /// Shows a notification message that will disappear on it's own
        /// 
        /// The text.
        /// The caption.
        /// The icon.
        public void ShowNotificationMessage(string text, string caption = null, MessageBoxIcon icon = MessageBoxIcon.None)
        {
            ApplicationWindow.ShowModalMessage(text, caption: caption, icon: icon);
        }
        /// 
        /// Shows a notification message that will disappear on it's own
        /// 
        /// The text.
        /// The caption.
        /// The icon.
        public void ShowNotificationMessage(UIElement text, string caption = null, MessageBoxIcon icon = MessageBoxIcon.None)
        {
            ApplicationWindow.ShowModalMessage(text, caption: caption, icon: icon);
        }
        /// 
        /// Shows a modal message box and asynchronously returns a MessageBoxResult
        /// 
        /// The text.
        /// The caption.
        /// The button.
        /// The icon.
        /// MessageBoxResult.
        public MessageBoxResult ShowModalMessage(string text, string caption = null, MessageBoxButton button = MessageBoxButton.OK, MessageBoxIcon icon = MessageBoxIcon.None)
        {
            return ApplicationWindow.ShowModalMessage(text, caption: caption, button: button, icon: icon);
        }
        /// 
        /// Shows a modal message box and asynchronously returns a MessageBoxResult
        /// 
        /// The text.
        /// The caption.
        /// The button.
        /// The icon.
        /// MessageBoxResult.
        public MessageBoxResult ShowModalMessage(UIElement text, string caption = null, MessageBoxButton button = MessageBoxButton.OK, MessageBoxIcon icon = MessageBoxIcon.None)
        {
            return ApplicationWindow.ShowModalMessage(text, caption: caption, button: button, icon: icon);
        }
        /// 
        /// Shows the error message.
        /// 
        /// The message.
        /// The caption.
        public void ShowErrorMessage(string message, string caption = null)
        {
            caption = caption ?? "Error";
            ShowModalMessage(message, caption: caption, button: MessageBoxButton.OK, icon: MessageBoxIcon.Error);
        }
        /// 
        /// Shows the default error message.
        /// 
        public void ShowDefaultErrorMessage()
        {
            ShowErrorMessage("There was an error processing the request", "Error");
        }
        /// 
        /// The _remote image cache
        /// 
        private FileSystemRepository _remoteImageCache;
        /// 
        /// Gets the remote image async.
        /// 
        /// The URL.
        /// Task{Image}.
        public async Task GetRemoteImageAsync(string url)
        {
            var bitmap = await GetRemoteBitmapAsync(url);
            return new Image { Source = bitmap };
        }
        /// 
        /// Gets the remote image async.
        /// 
        /// The URL.
        /// Task{BitmapImage}.
        /// url
        public Task GetRemoteBitmapAsync(string url)
        {
            if (string.IsNullOrEmpty(url))
            {
                throw new ArgumentNullException("url");
            }
            Logger.Info("Image url: " + url);
            return Task.Run(async () =>
            {
                var cachePath = _remoteImageCache.GetResourcePath(url.GetMD5().ToString());
                await _remoteImageCache.WaitForLockAsync(cachePath).ConfigureAwait(false);
                var releaseLock = true;
                try
                {
                    using (var stream = new FileStream(cachePath, FileMode.Open, FileAccess.Read, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, true))
                    {
                        return await GetRemoteBitmapAsync(stream).ConfigureAwait(false);
                    }
                }
                catch (FileNotFoundException)
                {
                    // Doesn't exist. No biggie
                    releaseLock = false;
                }
                finally
                {
                    if (releaseLock)
                    {
                        _remoteImageCache.ReleaseLock(cachePath);
                    }
                }
                try
                {
                    using (var httpStream = await UIKernel.Instance.ApiClient.GetImageStreamAsync(url + "&x=1"))
                    {
                        return await GetRemoteBitmapAsync(httpStream, cachePath);
                    }
                }
                finally
                {
                    _remoteImageCache.ReleaseLock(cachePath);
                }
            });
        }
        /// 
        /// Gets the image async.
        /// 
        /// The source stream.
        /// The cache path.
        /// Task{BitmapImage}.
        private async Task GetRemoteBitmapAsync(Stream sourceStream, string cachePath = null)
        {
            byte[] bytes;
            using (var ms = new MemoryStream())
            {
                await sourceStream.CopyToAsync(ms).ConfigureAwait(false);
                bytes = ms.ToArray();
            }
            if (!string.IsNullOrEmpty(cachePath))
            {
                using (var fileStream = new FileStream(cachePath, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, true))
                {
                    await fileStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
                }
            }
            using (Stream stream = new MemoryStream(bytes))
            {
                var bitmapImage = new BitmapImage
                {
                    CreateOptions = BitmapCreateOptions.DelayCreation
                };
                bitmapImage.BeginInit();
                bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
                bitmapImage.StreamSource = stream;
                bitmapImage.EndInit();
                bitmapImage.Freeze();
                return bitmapImage;
            }
        }
    }
}