using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Ookii.Dialogs.Wpf;
using EDJournal;
using EliteBGS.BGS;
using EliteBGS.Util;
using EliteBGS.UI;
using EliteBGS.EDDB;

namespace EliteBGS {
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window {
        private PlayerJournal journal = null;
        private Report report = new Report();
        private Config config = new Config();
        private API api = null;

        private PopulatedSystems systems_db = null;
        private Stations stations_db = null;

        public Config Config => config;

        public Report Report => report;

        private LoadEntriesWindow loadentries = new LoadEntriesWindow();

        private static readonly List<DiscordLogGenerator> logtypes = new List<DiscordLogGenerator>() {
            new NonaDiscordLog(),
            new GenericDiscordLog(),
        };

        public MainWindow() {
            InitializeComponent();

            try {
                config.LoadGlobal();
            } catch (Exception) {
                /* ignored */
            }

            loadentries.EntriesLoaded += Loadentries_EntriesLoaded;

            report.OnLog += Report_OnLog;

            foreach (DiscordLogGenerator type in logtypes) {
                LogType.Items.Add(type);
            }

            string lastused = config.Global.LastUsedDiscordTemplate;
            int lastindex = logtypes.FindIndex(x => x.ToString() == lastused);
            if (lastindex > -1) {
                LogType.SelectedIndex = lastindex;
            } else {
                LogType.SelectedIndex = 0;
            }

            api = new API(config.ConfigPath);
            journal = new PlayerJournal(config.Global.JournalLocation);

            // Set both to now
            startdate.SelectedDate = DateTime.Now;
            enddate.SelectedDate = DateTime.Now;
            journallocation.Text = Config.Global.JournalLocation;
            useeddb.IsChecked = Config.Global.UseEDDB;

            try {
                config.LoadObjectives(Report);
                RefreshObjectives();
            } catch (Exception e) {
                Log(e.Message);
            }

            api.SystemsAvailable += Api_SystemsAvailable;
            api.StationsAvailable += Api_StationsAvailable;

            try {
                api.CheckDatabases();
            } catch (Exception e) {
                Log(e.Message);
            }
        }

        private void Loadentries_EntriesLoaded(List<Entry> lines) {
            try {
                report.Scan(lines);
                RefreshObjectives();
            } catch (Exception exception) {
                Log("Something went terribly wrong while parsing the E:D player journal.");
                Log("Please send this to CMDR Hekateh:");
                Log(exception.ToString());
            }
        }

        private void Api_StationsAvailable() {
            try {
                stations_db = api.MakeStations();
            } catch (Exception e) {
                Log(e.Message);
            }
        }

        private void Api_SystemsAvailable() {
            try {
                systems_db = api.MakePopulatedSystems();
                system.Provider = new SystemSuggestionProvider(systems_db);
            } catch (Exception e) {
                Log(e.Message);
            }
        }

        private void Report_OnLog(string message) {
            StringBuilder builder = new StringBuilder();

            builder.Append(DateTime.Now.ToString());
            builder.Append(": ");
            builder.Append(message);
            builder.Append("\n");

            log.AppendText(builder.ToString());
        }

        private void Log(string message) {
            Report_OnLog(message);
        }

        private void RefreshObjectives() {
            entries.Items.Clear();

            if (report.Objectives == null) {
                return;
            }

            foreach (Objective obj in report.Objectives) {
                entries.Items.Add(obj);
                obj.IsExpanded = obj.ManuallyAdded;
                obj.IsEnabled = obj.ManuallyAdded;
            }
        }

        private void ParseJournal_Click(object sender, RoutedEventArgs e) {
            try {
                journal.Open(); // Load all files
                var start = startdate.SelectedDate ?? DateTime.Now;
                var end = enddate.SelectedDate ?? DateTime.Now;
                report.Scan(journal, start, end);
                RefreshObjectives();
            } catch (Exception exception) {
                Log("Something went terribly wrong while parsing the E:D player journal.");
                Log("Please send this to CMDR Hekateh:");
                Log(exception.ToString());
            }
        }

        private void AddObjective() {
            Objective objective = new Objective {
                System = system.Text,
                Faction = faction.Text,
                Station = station.Text,
                ManuallyAdded = true,
            };

            if (!objective.IsValid) {
                return;
            }

            if (report.AddObjective(objective)) {
                RefreshObjectives();
                config.SaveObjectives(Report);
            }
        }

        private void AddFilter_Click(object sender, RoutedEventArgs e) {
            AddObjective();
        }

        private void GenerateDiscord_Click(object sender, RoutedEventArgs e) {
            try {
                DiscordLogGenerator discord = LogType.SelectedItem as DiscordLogGenerator;
                string report = discord.GenerateDiscordLog(Report);

                DiscordLog.Text = report;
            } catch (Exception exception) {
                Log("Something went terribly wrong while generating the Discord log.");
                Log("Please send this to CMDR Hekateh:");
                Log(exception.ToString());
            }
        }

        private void RemoveCurrentObjective() {
            if (entries.SelectedItem == null) {
                return;
            }

            object obj = entries.SelectedItem;
            bool removed = false;

            if (obj.GetType() == typeof(Objective)) {
                removed = report.Objectives.Remove(obj as Objective);
            } else if (obj.GetType() == typeof(LogEntry) ||
                obj.GetType().IsSubclassOf(typeof(LogEntry))) {
                foreach (Objective parent in report.Objectives) {
                    if (parent.LogEntries.Remove(obj as LogEntry)) {
                        removed = true;
                    }
                }
            }

            if (removed) {
                RefreshObjectives();
                config.SaveObjectives(Report);
            }
        }

        private void entries_KeyUp(object sender, KeyEventArgs e) {
            if (e.Key == Key.Delete) {
                RemoveCurrentObjective();
            }
        }

        private void browsejournallocation_Click(object sender, RoutedEventArgs e) {
            var dialog = new VistaFolderBrowserDialog();

            if ((bool)!dialog.ShowDialog()) {
                return;
            }

            Config.Global.JournalLocation = dialog.SelectedPath;
            journallocation.Text = Config.Global.JournalLocation;
            journal = new PlayerJournal(config.Global.JournalLocation);
        }

        private void useeddb_Click(object sender, RoutedEventArgs e) {
            Config.Global.UseEDDB = (bool)useeddb.IsChecked;
        }

        private void Filter_KeyDown(object sender, KeyEventArgs e) {
            if (e.Key == Key.Enter) {
                AddObjective();
            }
        }

        private void station_GotFocus(object sender, RoutedEventArgs e) {
            try {
                if (stations_db == null || systems_db == null) {
                    return;
                }

                var sys = system.Text;
                if (sys == null || sys.Length <= 0) {
                    return;
                }

                int system_id = systems_db.ToId(sys);
                station.Provider = new StationSuggestionProvider(stations_db, system_id);
            } catch (Exception exc) {
                Log(exc.ToString());
            }
        }

        private void DownloadData_Click(object sender, RoutedEventArgs e) {
            if (!Config.Global.UseEDDB) {
                return;
            }

            ProgressDialog dialog = new ProgressDialog(api);
            dialog.StartDownload();
            dialog.ShowDialog();
        }

        private void AddCombatZone_Click(object sender, RoutedEventArgs e) {
            if (entries.SelectedItem == null) {
                return;
            }

            var obj = entries.SelectedItem;

            if (obj.GetType() != typeof(Objective)) {
                return;
            }

            Objective objective = obj as Objective;

            CombatZoneDialog dialog = new CombatZoneDialog() { Owner = this };

            if (!(dialog.ShowDialog() ?? false)) {
                return;
            }

            CombatZone zone = new CombatZone {
                ManuallyAdded = true,
                Faction = objective.Faction,
                System = objective.System,
                Station = objective.Station,

                Grade = dialog.Grade,
                Type = dialog.Type,
                Amount = dialog.Amount
            };

            objective.LogEntries.Add(zone);
            RefreshObjectives();
        }

        private void AdjustProfit_Click(object sender, RoutedEventArgs e) {
            if (entries.SelectedItem == null || entries.SelectedItem.GetType() != typeof(SellCargo)) {
                return;
            }

            SellCargo sell = entries.SelectedItem as SellCargo;
            AdjustProfitWindow adjust = new AdjustProfitWindow() { Owner = this };

            adjust.Profit.Text = sell.Profit.ToString();

            if (!(adjust.ShowDialog() ?? false)) {
                return;
            }
            
            if (int.TryParse(adjust.Profit.Text, out int newprofit)) {
                sell.Profit = newprofit;
                RefreshObjectives();
            }
        }

        private void LogType_SelectionChanged(object sender, SelectionChangedEventArgs e) {
            if (LogType.SelectedItem == null) {
                return;
            }

            string template = LogType.SelectedItem.ToString();
            config.Global.LastUsedDiscordTemplate = template;
        }

        private void ManuallyParse_Click(object sender, RoutedEventArgs e) {
            loadentries.Show();
        }

        private void window_Closing(object sender, System.ComponentModel.CancelEventArgs e) {
            loadentries.Close();
        }
    }
}