using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using Microsoft.Win32;
using TweetDuck.Configuration;
using System.Linq;
using System.Management;
using System.Text.RegularExpressions;
using TweetDuck.Core.Handling;
using TweetDuck.Core.Notification;
using TweetDuck.Core.Utils;
using TweetDuck.Plugins;

namespace TweetDuck.Core.Other.Analytics{
    static class AnalyticsReportGenerator{
        public static AnalyticsReport Create(AnalyticsFile file, ExternalInfo info, PluginManager plugins){
            Dictionary<string, string> editLayoutDesign = EditLayoutDesignPluginData;

            return new AnalyticsReport{
                { "App Version"   , Program.VersionTag },
                { "App Type"      , Program.IsPortable ? "portable" : "installed" },
                { "App Dev Tools" , Bool(ContextMenuBase.HasDevTools) },
                0,
                { "System Name"        , SystemName },
                { "System Edition"     , SystemEdition },
                { "System Environment" , Environment.Is64BitOperatingSystem ? "64-bit" : "32-bit" },
                { "System Build"       , SystemBuild },
                { "System Locale"      , Program.Culture.Name.ToLower() },
                0,
                { "RAM" , Exact(RamSize) },
                { "GPU" , GpuVendor },
                0,
                { "Screen Count"      , Exact(Screen.AllScreens.Length) },
                { "Screen Resolution" , info.Resolution ?? "(unknown)" },
                { "Screen DPI"        , info.DPI != null ? Exact(info.DPI.Value) : "(unknown)" },
                0,
                { "Hardware Acceleration" , Bool(SysConfig.HardwareAcceleration) },
                0,
                { "Expand Links"             , Bool(UserConfig.ExpandLinksOnHover) },
                { "Switch Account Selectors" , Bool(UserConfig.SwitchAccountSelectors) },
                { "Search In First Column"   , Bool(UserConfig.OpenSearchInFirstColumn) },
                { "Best Image Quality"       , Bool(UserConfig.BestImageQuality) },
                { "Spell Check"              , Bool(UserConfig.EnableSpellCheck) },
                { "Zoom"                     , Exact(UserConfig.ZoomLevel) },
                0,
                { "Updates"          , Bool(UserConfig.EnableUpdateCheck) },
                { "Update Dismissed" , Bool(!string.IsNullOrEmpty(UserConfig.DismissedUpdate)) },
                0,
                { "Tray"           , TrayMode },
                { "Tray Highlight" , Bool(UserConfig.EnableTrayHighlight) },
                0,
                { "Notification Position"            , NotificationPosition },
                { "Notification Size"                , NotificationSize },
                { "Notification Timer"               , NotificationTimer },
                { "Notification Timer Speed"         , RoundUp(UserConfig.NotificationDurationValue, 5) },
                { "Notification Scroll Speed"        , Exact(UserConfig.NotificationScrollSpeed) },
                { "Notification Column Title"        , Bool(UserConfig.DisplayNotificationColumn) },
                { "Notification Media Previews"      , Bool(UserConfig.NotificationMediaPreviews) },
                { "Notification Link Skip"           , Bool(UserConfig.NotificationSkipOnLinkClick) },
                { "Notification Non-Intrusive"       , Bool(UserConfig.NotificationNonIntrusiveMode) },
                { "Notification Idle Pause"          , Exact(UserConfig.NotificationIdlePauseSeconds) },
                { "Custom Sound Notification"        , string.IsNullOrEmpty(UserConfig.NotificationSoundPath) ? "off" : Path.GetExtension(UserConfig.NotificationSoundPath) },
                { "Custom Sound Notification Volume" , RoundUp(UserConfig.NotificationSoundVolume, 5) },
                0,
                { "Program Arguments"       , List(ProgramArguments) },
                { "Custom CEF Arguments"    , RoundUp((UserConfig.CustomCefArgs ?? string.Empty).Length, 10) },
                { "Custom Browser CSS"      , RoundUp((UserConfig.CustomBrowserCSS ?? string.Empty).Length, 50) },
                { "Custom Notification CSS" , RoundUp((UserConfig.CustomNotificationCSS ?? string.Empty).Length, 50) },
                0,
                { "Plugins All"     , List(plugins.Plugins.Select(plugin => plugin.Identifier)) },
                { "Plugins Enabled" , List(plugins.Plugins.Where(plugin => plugins.Config.IsEnabled(plugin)).Select(plugin => plugin.Identifier)) },
                0,
                { "Theme"               , Dict(editLayoutDesign, "_theme",                  "light/def") },
                { "Column Width"        , Dict(editLayoutDesign, "columnWidth",             "310px/def") },
                { "Font Size"           , Dict(editLayoutDesign, "fontSize",                "12px/def") },
                { "Large Quote Font"    , Dict(editLayoutDesign, "increaseQuoteTextSize",   "false/def") },
                { "Small Compose Font"  , Dict(editLayoutDesign, "smallComposeTextSize",    "false/def") },
                { "Avatar Radius"       , Dict(editLayoutDesign, "avatarRadius",            "2/def") },
                { "Hide Tweet Actions"  , Dict(editLayoutDesign, "hideTweetActions",        "true/def") },
                { "Move Tweet Actions"  , Dict(editLayoutDesign, "moveTweetActionsToRight", "true/def") },
                { "Theme Color Tweaks"  , Dict(editLayoutDesign, "themeColorTweaks",        "true/def") },
                { "Revert Icons"        , Dict(editLayoutDesign, "revertIcons",             "true/def") },
                { "Optimize Animations" , Dict(editLayoutDesign, "optimizeAnimations",      "true/def") },
                { "Reply Account Mode"  , ReplyAccountConfigFromPlugin },
                { "Template Count"      , Exact(TemplateCountFromPlugin) },
                0,
                { "Opened Options"                   , LogRound(file.CountOpenOptions, 4) },
                { "Opened Plugins"                   , LogRound(file.CountOpenPlugins, 4) },
                { "Opened About"                     , LogRound(file.CountOpenAbout, 4) },
                { "Opened Guide"                     , LogRound(file.CountOpenGuide, 4) },
                { "Desktop Notifications"            , LogRound(file.CountDesktopNotifications, 5) },
                { "Sound Notifications"              , LogRound(file.CountSoundNotifications, 5) },
                { "Mute Notifications"               , LogRound(file.CountMuteNotifications, 2) },
                { "Browser Context Menus"            , LogRound(file.CountBrowserContextMenus, 2) },
                { "Browser Extra Mouse Buttons"      , LogRound(file.CountBrowserExtraMouseButtons, 2) },
                { "Notification Context Menus"       , LogRound(file.CountNotificationContextMenus, 2) },
                { "Notification Extra Mouse Buttons" , LogRound(file.CountNotificationExtraMouseButtons, 2) },
                { "Notification Keyboard Shortcuts"  , LogRound(file.CountNotificationKeyboardShortcuts, 2) },
                { "Tweet Screenshots"                , LogRound(file.CountTweetScreenshots, 2) },
                { "Tweet Details"                    , LogRound(file.CountTweetDetails, 2) },
                { "Video Plays"                      , LogRound(file.CountVideoPlays, 4) }
            }.FinalizeReport();
        }

        private static UserConfig UserConfig => Program.UserConfig;
        private static SystemConfig SysConfig => Program.SystemConfig;

        private static string Bool(bool value) => value ? "on" : "off";
        private static string Exact(int value) => value.ToString();
        private static string RoundUp(int value, int multiple) => (multiple*(int)Math.Ceiling((double)value/multiple)).ToString();
        private static string LogRound(int value, int logBase) => (value <= 0 ? 0 : (int)Math.Pow(logBase, Math.Floor(Math.Log(value, logBase)))).ToString();
        private static string Dict(Dictionary<string, string> dict, string key, string def = "(unknown)") => dict.TryGetValue(key, out string value) ? value : def;
        private static string List(IEnumerable<string> list) => string.Join("|", list.DefaultIfEmpty("(none)"));

        private static string SystemName { get; }
        private static string SystemEdition { get; }
        private static string SystemBuild { get; }
        private static int RamSize { get; }
        private static string GpuVendor { get; }
        private static string[] ProgramArguments { get; }

        static AnalyticsReportGenerator(){
            string osName, osEdition, osBuild;

            try{
                using(RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", false)){
                    // ReSharper disable once PossibleNullReferenceException
                    osName = key.GetValue("ProductName") as string;
                    osBuild = key.GetValue("CurrentBuild") as string;
                    osEdition = null;
                    
                    if (osName != null){
                        Match match = Regex.Match(osName, @"^(.*?\d+(?:\.\d+)?) (.*)$");

                        if (match.Success){
                            osName = match.Groups[1].Value;
                            osEdition = match.Groups[2].Value;
                        }
                    }
                }
            }catch{
                osName = osEdition = osBuild = null;
            }

            SystemName = osName ?? "Windows (unknown)";
            SystemEdition = osEdition ?? "(unknown)";
            SystemBuild = osBuild ?? "(unknown)";

            try{
                using(ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT Capacity FROM Win32_PhysicalMemory")){
                    foreach(ManagementBaseObject obj in searcher.Get()){
                        RamSize += (int)((ulong)obj["Capacity"]/(1024L*1024L));
                    }
                }
            }catch{
                RamSize = 0;
            }

            string gpu = null;

            try{
                using(ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT Caption FROM Win32_VideoController")){
                    foreach(ManagementBaseObject obj in searcher.Get()){
                        string vendor = obj["Caption"] as string;

                        if (!string.IsNullOrEmpty(vendor)){
                            gpu = vendor;
                        }
                    }
                }
            }catch{
                // rip
            }

            GpuVendor = gpu ?? "(unknown)";

            Dictionary<string, string> args = new Dictionary<string, string>();
            Arguments.GetCurrentClean().ToDictionary(args);
            ProgramArguments = args.Keys.Select(key => key.TrimStart('-')).ToArray();
        }

        private static string TrayMode{
            get{
                switch(UserConfig.TrayBehavior){
                    case TrayIcon.Behavior.DisplayOnly: return "icon";
                    case TrayIcon.Behavior.MinimizeToTray: return "minimize";
                    case TrayIcon.Behavior.CloseToTray: return "close";
                    case TrayIcon.Behavior.Combined: return "combined";
                    default: return "off";
                }
            }
        }

        private static string NotificationPosition{
            get{
                switch(UserConfig.NotificationPosition){
                    case TweetNotification.Position.TopLeft: return "top left";
                    case TweetNotification.Position.TopRight: return "top right";
                    case TweetNotification.Position.BottomLeft: return "bottom left";
                    case TweetNotification.Position.BottomRight: return "bottom right";
                    default: return "custom";
                }
            }
        }

        private static string NotificationSize{
            get{
                switch(UserConfig.NotificationSize){
                    case TweetNotification.Size.Auto: return "auto";
                    default: return RoundUp(UserConfig.CustomNotificationSize.Width, 20)+"x"+RoundUp(UserConfig.CustomNotificationSize.Height, 20);
                }
            }
        }

        private static string NotificationTimer{
            get{
                if (!UserConfig.DisplayNotificationTimer){
                    return "off";
                }
                else{
                    return UserConfig.NotificationTimerCountDown ? "count down" : "count up";
                }
            }
        }

        private static Dictionary<string, string> EditLayoutDesignPluginData{
            get{
                Dictionary<string, string> dict = new Dictionary<string, string>();
                
                try{
                    string data = File.ReadAllText(Path.Combine(Program.PluginDataPath, "official", "edit-design", "config.json"));

                    foreach(Match match in Regex.Matches(data, "\"(\\w+?)\":(.*?)[,}]")){
                        dict[match.Groups[1].Value] = match.Groups[2].Value.Trim('"');
                    }
                }catch{
                    // rip
                }

                return dict;
            }
        }

        private static int TemplateCountFromPlugin{
            get{
                try{
                    string data = File.ReadAllText(Path.Combine(Program.PluginDataPath, "official", "templates", "config.json"));
                    return Math.Min(StringUtils.CountOccurrences(data, "{\"name\":"), StringUtils.CountOccurrences(data, ",\"contents\":"));
                }catch{
                    return 0;
                }
            }
        }

        private static string ReplyAccountConfigFromPlugin{
            get{
                try{
                    string data = File.ReadAllText(Path.Combine(Program.PluginDataPath, "official", "reply-account", "configuration.js")).Replace(" ", "");

                    Match matchType = Regex.Match(data, "defaultAccount:\"([#@])(.*?)\"(?:,|$)");
                    Match matchAdvanced = Regex.Match(data, "useAdvancedSelector:(.*?)(?:,|$)", RegexOptions.Multiline);

                    if (!matchType.Success){
                        return data.Contains("defaultAccount:\"\"") ? "(legacy)" : "(unknown)";
                    }

                    string accType = matchType.Groups[1].Value == "#" ? matchType.Groups[2].Value : "account";
                    return matchAdvanced.Success && !matchAdvanced.Value.Contains("false") ? "advanced/"+accType : accType;
                }catch{
                    return "(unknown)";
                }
            }
        }

        public class ExternalInfo{
            public static ExternalInfo From(Form form){
                if (form == null){
                    return new ExternalInfo();
                }
                else{
                    Screen screen = Screen.FromControl(form);
                    int dpi;

                    using(Graphics graphics = form.CreateGraphics()){
                        dpi = (int)graphics.DpiY;
                    }

                    return new ExternalInfo{
                        Resolution = screen.Bounds.Width+"x"+screen.Bounds.Height,
                        DPI = dpi
                    };
                }
            }

            public string Resolution { get; private set; }
            public int? DPI { get; private set; }

            private ExternalInfo(){}
        }
    }
}