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 EDPlayerJournal;
using EDPlayerJournal.BGS;
using EDPlayerJournal.Entries;
using EliteBGS.BGS;
using EliteBGS.Util;
using System.Globalization;
using System.Windows.Forms;
using System.Windows.Controls.Primitives;
namespace EliteBGS;
/// 
/// Interaction logic for MainWindow.xaml
/// 
public partial class MainWindow : Window {
    private PlayerJournal journal;
    private Report report;
    public Config Config { get; set; } = new Config();
    private LoadEntriesWindow loadentries = null;
    private static readonly List logtypes = new List() {
        new NonaDiscordLog(),
        new GenericDiscordLog(),
        new OneLineDiscordLog(),
    };
    public MainWindow() {
        InitializeComponent();
        try {
            Config.LoadGlobal();
        } catch (Exception) {
            /* ignored */
        }
        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;
        }
        journal = new PlayerJournal(Config.Global.JournalLocation);
        // Set both to now
        InitialiseTime();
        journallocation.Text = Config.Global.JournalLocation;
    }
    private void InitialiseTime() {
        DateTime today = DateTime.Today;
        DateTime tomorrow = today.AddDays(1);
        startdate.CultureInfo = enddate.CultureInfo = CultureInfo.InvariantCulture;
        startdate.Value = today;
        enddate.Value = tomorrow;
    }
    private void TreeView_CheckBox_Updated(object sender, RoutedEventArgs args) {
        GenerateLog();
    }
    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 HandleEntries(List entries, DateTime start, DateTime end) {
        try {
            TransactionParser parser = new TransactionParser();
            List transactions = parser.Parse(entries);
            // Filter the transactions down to the given time frame
            transactions = transactions
                .Where(t => t.CompletedAtDateTime >= start && t.CompletedAtDateTime <= end)
                .ToList()
                ;
            List incompletes = transactions.OfType().ToList();
            // Log incomplete and remove them from the results.
            foreach (var incomplete in incompletes) {
                Log(incomplete.Reason);
            }
            transactions.RemoveAll(x => incompletes.Contains(x));
            report = new Report(transactions);
            this.entries.ItemsSource = report.Objectives;
        } 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 HandleEntries(List entries) {
        HandleEntries(entries, startdate.Value ?? DateTime.Now, enddate.Value ?? DateTime.Now);
    }
    private void Loadentries_EntriesLoaded(List lines) {
        HandleEntries(lines);
    }
    private void ParseJournal_Click(object sender, RoutedEventArgs e) {
        try {
            TransactionParser parser = new TransactionParser();
            DateTime start = startdate.Value ?? DateTime.Now;
            DateTime end = enddate.Value ?? DateTime.Now;
            journal.Open(); // Load all files
            // Log files only get rotated if you restart the game client. This means that there might
            // be - say - entries from the 4th of May in the file with a timestamp of 3rd of May. This
            // happens if you happen to play a session late into the night.
            // At first I tried extracting the first and last line of a file to see the date range, but
            // if you have a lot of files this becomes quite slow, and quite the memory hog (as journal
            // files have to be read in their entirety to check this). So we assume that you can't play
            // three days straight, and keep the code fast.
            DateTime actualstart = start.AddDays(-3);
            DateTime actualend = end.AddDays(1);
            List entries = journal.Files
                .Where(f => f.NormalisedDateTime >= actualstart && f.NormalisedDateTime <= actualend)
                .SelectMany(e => e.Entries)
                .ToList()
                ;
            HandleEntries(entries, start, end);
            GenerateLog();
        } 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 GenerateLog() {
        try {
            DiscordLogGenerator discord = LogType.SelectedItem as DiscordLogGenerator;
            string report = discord.GenerateDiscordLog(this.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 GenerateDiscord_Click(object sender, RoutedEventArgs e) {
        GenerateLog();
    }
    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(UITransaction) ||
            obj.GetType().IsSubclassOf(typeof(UITransaction))) {
            foreach (Objective parent in report.Objectives) {
                if (parent.UITransactions.Remove(obj as UITransaction)) {
                    removed = true;
                }
            }
        }
        if (removed) {
            RefreshView();
        }
    }
    private void entries_KeyUp(object sender, System.Windows.Input.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 Objective GetObjectiveFromControl(object sender) {
        System.Windows.Controls.Control control = sender as System.Windows.Controls.Control;
        if (control == null || control.DataContext == null) {
            return null;
        }
        return control.DataContext as Objective;
    }
    private void AddCombatZone_Click(object sender, RoutedEventArgs e) {
        Objective objective = GetObjectiveFromControl(sender);
        if (objective == null) {
            return;
        }
        CombatZone zone = new CombatZone {
            Faction = objective.Faction,
            System = objective.System,
            Grade = "Low",
            Type = "Ship",
        };
        UITransaction uitransaction = new UITransaction(zone);
        objective.UITransactions.Add(uitransaction);
        RefreshView();
    }
    private void RefreshView() {
        entries.Items.Refresh();
        GenerateLog();
    }
    private void LogType_SelectionChanged(object sender, SelectionChangedEventArgs e) {
        if (LogType.SelectedItem == null) {
            return;
        }
        string template = LogType.SelectedItem.ToString();
        Config.Global.LastUsedDiscordTemplate = template;
        GenerateLog();
    }
    private void ManuallyParse_Click(object sender, RoutedEventArgs e) {
        if (loadentries != null) {
            loadentries.Show();
            return;
        }
        loadentries = new LoadEntriesWindow();
        loadentries.Closed += Loadentries_Closed;
        loadentries.EntriesLoaded += Loadentries_EntriesLoaded;
        loadentries.Show();
    }
    private void Loadentries_Closed(object sender, EventArgs e) {
        loadentries = null;
    }
    private void window_Closing(object sender, System.ComponentModel.CancelEventArgs e) {
        loadentries?.Close();
        loadentries = null;
    }
    private void Transaction_Initialized(object sender, EventArgs e) {
    }
    private TransactionType GetTransaction(object sender) where TransactionType : Transaction {
        System.Windows.Controls.Control button = sender as System.Windows.Controls.Control;
        if (button == null || button.DataContext == null) {
            return null;
        }
        UITransaction transaction = button.DataContext as UITransaction;
        if (transaction == null) {
            return null;
        }
        return transaction.Transaction as TransactionType;
    }
    private void Low_Click(object sender, RoutedEventArgs e) {
        CombatZone transaction = GetTransaction(sender);
        if (transaction == null) {
            return;
        }
        transaction.Grade = CombatZones.DifficultyLow;
        RefreshView();
    }
    private void Med_Click(object sender, RoutedEventArgs e) {
        CombatZone transaction = GetTransaction(sender);
        if (transaction == null) {
            return;
        }
        transaction.Grade = CombatZones.DifficultyMedium;
        RefreshView();
    }
    private void High_Click(object sender, RoutedEventArgs e) {
        CombatZone transaction = GetTransaction(sender);
        if (transaction == null) {
            return;
        }
        transaction.Grade = CombatZones.DifficultyHigh;
        RefreshView();
    }
    private void VeryHigh_Click(object sender, RoutedEventArgs e) {
        CombatZone transaction = GetTransaction(sender);
        if (transaction == null) {
            return;
        }
        transaction.Grade = CombatZones.DifficultyVeryHigh;
        RefreshView();
    }
    private void Ground_Click(object sender, RoutedEventArgs e) {
        CombatZone transaction = GetTransaction(sender);
        if (transaction == null) {
            return;
        }
        transaction.Type = CombatZones.GroundCombatZone;
        RefreshView();
    }
    private void Ship_Click(object sender, RoutedEventArgs e) {
        CombatZone transaction = GetTransaction(sender);
        if (transaction == null) {
            return;
        }
        transaction.Type = CombatZones.ShipCombatZone;
        RefreshView();
    }
    private void Thargoid_Click(object sender, RoutedEventArgs e) {
        CombatZone transaction = GetTransaction(sender);
        if (transaction == null) {
            return;
        }
        transaction.Type = CombatZones.AXCombatZone;
        RefreshView();
    }
    private void Profit_LostFocus(object sender, RoutedEventArgs e) {
        RefreshView();
    }
    private void Profit_KeyUp(object sender, System.Windows.Input.KeyEventArgs e) {
        if (e.Key == Key.Enter) {
            RefreshView();
        }
    }
    private DateTime ResetTimeToZero(DateTime d) {
        DateTime obj = d;
        obj = obj.AddHours(d.Hour * -1);
        obj = obj.AddMinutes(d.Minute * -1);
        obj = obj.AddSeconds(d.Second * -1);
        return obj;
    }
    private void ResetTime_Click(object sender, RoutedEventArgs e) {
        DateTime? d = startdate.Value;
        if (d != null) {
            startdate.Value = ResetTimeToZero(d.Value);
        }
        d = enddate.Value;
        if (d != null) {
            enddate.Value = ResetTimeToZero(d.Value);
        }
    }
    private void ToggleAll_Click(object sender, RoutedEventArgs e) {
        ToggleButton button = sender as ToggleButton;
        Objective objective = GetObjectiveFromControl(sender);
        if (objective == null) {
            return;
        }
        objective.UITransactions
            .ForEach(x => x.IsEnabled = (button.IsChecked ?? true))
            ;
    }
}