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;
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(),
    };
    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
        startdate.SelectedDate = DateTime.Now;
        enddate.SelectedDate = DateTime.Now;
        journallocation.Text = Config.Global.JournalLocation;
    }
    private void TreeView_CheckBox_Updated(object sender, RoutedEventArgs args) {
        GenerateLog();
    }
    private void HandleEntries(List entries) {
        try {
            TransactionParser parser = new TransactionParser();
            List transactions = parser.Parse(entries);
            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 Loadentries_EntriesLoaded(List lines) {
        HandleEntries(lines);
    }
    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 ParseJournal_Click(object sender, RoutedEventArgs e) {
        try {
            TransactionParser parser = new TransactionParser();
            DateTime start = startdate.SelectedDate ?? DateTime.Now;
            DateTime end = enddate.SelectedDate ?? 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);
            List entries = journal.Files
                .Where(f => f.NormalisedDateTime >= actualstart && f.NormalisedDateTime <= end)
                .SelectMany(e => e.Entries)
                .ToList()
                ;
            // Now further sort the list down to entries that are actually within the given datetime
            // Note that entry datetimes are not normalised, so we have to sort until end + 1 day
            DateTime actualend = end.AddDays(1);
            entries = entries
                .Where(e => e.Timestamp >= start && e.Timestamp < actualend)
                .ToList()
                ;
            HandleEntries(entries);
            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, 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);
    }
    /// 
    /// Gets the currently selected objective, even if a log entry in said objective
    /// is selected instead. If nothing is selected, returns null.
    /// 
    /// 
    private Objective GetSelectedObjective() {
        var obj = entries.SelectedItem;
        if (obj == null) {
            return null;
        }
        if (obj.GetType() == typeof(Objective)) {
            return obj as Objective;
        }
        // Some form of entry perhaps?
        if (obj.GetType().IsSubclassOf(typeof(Transaction))) {
            Transaction entry = obj as Transaction;
            Objective objective = entries.Items
                .OfType()
                .First(x => x.Transactions.Contains(entry))
                ;
            return objective;
        }
        return null;
    }
    private void AddCombatZone_Click(object sender, RoutedEventArgs e) {
        Objective objective = GetSelectedObjective();
        if (objective == null) {
            return;
        }
        CombatZoneDialog dialog = new CombatZoneDialog() { Owner = this };
        if (!(dialog.ShowDialog() ?? false)) {
            return;
        }
        CombatZone zone = new CombatZone {
            Faction = objective.Faction,
            System = objective.System,
            Grade = dialog.Grade,
            Type = dialog.Type,
            Amount = dialog.Amount
        };
        objective.Transactions.Add(zone);
        RefreshView();
    }
    private void RefreshView() {
        entries.Items.Refresh();
        GenerateLog();
    }
    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;
            RefreshView();
        }
    }
    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;
    }
}