using System;
using System.Drawing;
using System.IO;
using TweetDuck.Core.Controls;
using TweetDuck.Core.Notification;
using TweetDuck.Core.Other;
using TweetDuck.Core.Utils;
using TweetDuck.Data;
using TweetDuck.Data.Serialization;

namespace TweetDuck.Configuration{
    sealed class UserConfig{
        private static readonly FileSerializer<UserConfig> Serializer = new FileSerializer<UserConfig>();

        static UserConfig(){
            Serializer.RegisterTypeConverter(typeof(WindowState), WindowState.Converter);

            Serializer.RegisterTypeConverter(typeof(Point), new SingleTypeConverter<Point>{
                ConvertToString = value => $"{value.X} {value.Y}",
                ConvertToObject = value => {
                    int[] elements = StringUtils.ParseInts(value, ' ');
                    return new Point(elements[0], elements[1]);
                }
            });
            
            Serializer.RegisterTypeConverter(typeof(Size), new SingleTypeConverter<Size>{
                ConvertToString = value => $"{value.Width} {value.Height}",
                ConvertToObject = value => {
                    int[] elements = StringUtils.ParseInts(value, ' ');
                    return new Size(elements[0], elements[1]);
                }
            });
        }
        
        // CONFIGURATION DATA

        public bool FirstRun                       { get; set; } = true;
        public bool AllowDataCollection            { get; set; } = false;
        public bool ShowDataCollectionNotification { get; set; } = true;

        public WindowState BrowserWindow { get; set; } = new WindowState();
        public WindowState PluginsWindow { get; set; } = new WindowState();

        public bool ExpandLinksOnHover      { get; set; } = true;
        public bool SwitchAccountSelectors  { get; set; } = true;
        public bool OpenSearchInFirstColumn { get; set; } = true;
        public bool BestImageQuality        { get; set; } = true;
        public bool EnableAnimatedImages    { get; set; } = true;
        public bool EnableSpellCheck        { get; set; } = false;
        public int VideoPlayerVolume        { get; set; } = 50;
        private int _zoomLevel                            = 100;
        private bool _muteNotifications;
        
        private TrayIcon.Behavior _trayBehavior       = TrayIcon.Behavior.Disabled;
        public bool EnableTrayHighlight { get; set; } = true;

        public bool EnableUpdateCheck { get; set; } = true;
        public string DismissedUpdate { get; set; } = null;

        public bool DisplayNotificationColumn    { get; set; } = false;
        public bool NotificationMediaPreviews    { get; set; } = true;
        public bool NotificationSkipOnLinkClick  { get; set; } = false;
        public bool NotificationNonIntrusiveMode { get; set; } = true;
        public int NotificationIdlePauseSeconds  { get; set; } = 0;

        public bool DisplayNotificationTimer     { get; set; } = true;
        public bool NotificationTimerCountDown   { get; set; } = false;
        public int NotificationDurationValue     { get; set; } = 25;

        public TweetNotification.Position NotificationPosition { get; set; } = TweetNotification.Position.TopRight;
        public Point CustomNotificationPosition                { get; set; } = ControlExtensions.InvisibleLocation;
        public int NotificationDisplay                         { get; set; } = 0;
        public int NotificationEdgeDistance                    { get; set; } = 8;

        public TweetNotification.Size NotificationSize { get; set; } = TweetNotification.Size.Auto;
        public Size CustomNotificationSize             { get; set; } = Size.Empty;
        public int NotificationScrollSpeed             { get; set; } = 100;

        public int NotificationSoundVolume { get; set; } = 100;
        private string _notificationSoundPath;

        public string CustomCefArgs         { get; set; } = null;
        public string CustomBrowserCSS      { get; set; } = null;
        public string CustomNotificationCSS { get; set; } = null;
        
        // SPECIAL PROPERTIES

        public bool IsCustomNotificationPositionSet => CustomNotificationPosition != ControlExtensions.InvisibleLocation;
        public bool IsCustomNotificationSizeSet => CustomNotificationSize != Size.Empty;

        public TwitterUtils.ImageQuality TwitterImageQuality => BestImageQuality ? TwitterUtils.ImageQuality.Orig : TwitterUtils.ImageQuality.Default;

        public string NotificationSoundPath{
            get => string.IsNullOrEmpty(_notificationSoundPath) ? string.Empty : _notificationSoundPath;
            set => _notificationSoundPath = value;
        }

        public bool MuteNotifications{
            get => _muteNotifications;

            set{
                if (_muteNotifications != value){
                    _muteNotifications = value;
                    MuteToggled?.Invoke(this, EventArgs.Empty);
                }
            }
        }

        public int ZoomLevel{
            get => _zoomLevel;

            set{
                if (_zoomLevel != value){
                    _zoomLevel = value;
                    ZoomLevelChanged?.Invoke(this, EventArgs.Empty);
                }
            }
        }
        
        public TrayIcon.Behavior TrayBehavior{
            get => _trayBehavior;

            set{
                if (_trayBehavior != value){
                    _trayBehavior = value;
                    TrayBehaviorChanged?.Invoke(this, EventArgs.Empty);
                }
            }
        }

        // EVENTS
        
        public event EventHandler MuteToggled;
        public event EventHandler ZoomLevelChanged;
        public event EventHandler TrayBehaviorChanged;

        // END OF CONFIG
        
        private readonly string file;

        private UserConfig(string file){
            this.file = file;
        }

        public void Save(){
            try{
                if (File.Exists(file)){
                    string backupFile = GetBackupFile(file);
                    File.Delete(backupFile);
                    File.Move(file, backupFile);
                }

                Serializer.Write(file, this);
            }catch(Exception e){
                Program.Reporter.HandleException("Configuration Error", "Could not save the configuration file.", true, e);
            }
        }

        public void Reload(){
            try{
                LoadInternal(false);
            }catch(FileNotFoundException){
                try{
                    Serializer.Write(file, new UserConfig(file));
                    LoadInternal(false);
                }catch(Exception e){
                    Program.Reporter.HandleException("Configuration Error", "Could not regenerate configuration file.", true, e);
                }
            }catch(Exception e){
                Program.Reporter.HandleException("Configuration Error", "Could not reload configuration file.", true, e);
            }
        }

        private void LoadInternal(bool backup){
            Serializer.Read(backup ? GetBackupFile(file) : file, this);

            if (NotificationScrollSpeed == 10){ // incorrect initial value
                NotificationScrollSpeed = 100;
                Save();
            }
        }
        
        public static UserConfig Load(string file){
            Exception firstException = null;

            for(int attempt = 0; attempt < 2; attempt++){
                try{
                    UserConfig config = new UserConfig(file);
                    config.LoadInternal(attempt > 0);
                    return config;
                }catch(FileNotFoundException){
                }catch(DirectoryNotFoundException){
                    break;
                }catch(Exception e){
                    if (attempt == 0){
                        firstException = e;
                        Program.Reporter.Log(e.ToString());
                    }
                    else if (firstException is FormatException){
                        Program.Reporter.HandleException("Configuration Error", "The configuration file is outdated or corrupted. If you continue, your program options will be reset.", true, e);
                        return new UserConfig(file);
                    }
                    else if (firstException != null){
                        Program.Reporter.HandleException("Configuration Error", "Could not open the backup configuration file. If you continue, your program options will be reset.", true, e);
                        return new UserConfig(file);
                    }
                }
            }

            if (firstException != null){
                Program.Reporter.HandleException("Configuration Error", "Could not open the configuration file.", true, firstException);
            }

            return new UserConfig(file);
        }

        public static string GetBackupFile(string file){
            return file+".bak";
        }
    }
}