using EonaCat.Editor.Language.Highlighting;
using EonaCat.Editor.Language.Highlighting.Eeh;
using Microsoft.Win32;
using System.Diagnostics;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Xml;

namespace EonaCat.JavaDecompile;

public partial class MainWindow : Window
{
    string? jarPath;
    bool maximized;
    List<TreeViewItem>? OriginalTree;

    public MainWindow()
    {
        InitializeComponent();
        LoadIcons();
        LoadCustomJavaTheme();
        Loaded += AnimateIntro;
    }

    void AnimateIntro(object s, RoutedEventArgs e)
    {
        var a = new DoubleAnimation(0, 1, new Duration(System.TimeSpan.FromSeconds(0.6)));
        BeginAnimation(OpacityProperty, a);
    }

    void LoadIcons()
    {
        string basePath = Path.Combine(AppContext.BaseDirectory, "assets", "external", "icons");
        void SetIcon(Image img, string file)
        {
            var p = Path.Combine(basePath, file);
            if (File.Exists(p))
            {
                var bmp = new BitmapImage();
                bmp.BeginInit();
                bmp.UriSource = new System.Uri(p, System.UriKind.Absolute);
                bmp.CacheOption = BitmapCacheOption.OnLoad;
                bmp.EndInit();
                img.Source = bmp;
            }
        }
        SetIcon(IconLogo, "icon.png");
        SetIcon(IconFile, "extension.png");
        //SetIcon(IconCode, "code_page.png");
        SetIcon(IconFilter, "filter.png");
        //SetIcon(IconFull, "full.png");
        //SetIcon(IconClose, "close.png");
    }

    void LoadCustomJavaTheme()
    {
        var theme = Path.Combine(AppContext.BaseDirectory, "assets", "external", "themes", "EonaCat.JavaDecompile.eeh");
        if (!File.Exists(theme))
        {
            return;
        }

        using var reader = XmlReader.Create(theme);
        var definition = HighlightsLoader.Load(reader, HighlightsManager.Instance);
        HighlightsManager.Instance.RegisterHighlights("EonaCat.JavaDecompile", new[] { ".java" }, definition);
        CodeViewer.SyntaxHighlights = definition;
    }

    void OpenJarClick(object s, RoutedEventArgs e)
    {
        var d = new OpenFileDialog { Filter = "JAR files (*.jar)|*.jar" };
        if (d.ShowDialog() == true)
        {
            jarPath = d.FileName;
            LoadJar(jarPath);
        }
    }

    void LoadJar(string path)
    {
        JarTree.Items.Clear();
        OriginalTree = JarProcessor.ExtractStructure(path);
        foreach (var item in OriginalTree)
        {
            JarTree.Items.Add(item);
        }
        EmptyState.Visibility = Visibility.Collapsed;
    }

    void FilterBox_TextChanged(object s, TextChangedEventArgs e)
    {
        if (OriginalTree == null)
        {
            return;
        }

        string query = FilterBox.Text.Trim().ToLower();
        JarTree.Items.Clear();

        if (string.IsNullOrEmpty(query))
        {
            foreach (var node in OriginalTree)
            {
                JarTree.Items.Add(node);
            }

            return;
        }

        var filtered = new List<TreeViewItem>();
        foreach (var node in OriginalTree)
        {
            var result = FilterTree(node, query);
            if (result != null)
            {
                filtered.Add(result);
            }
        }
        foreach (var n in filtered)
        {
            JarTree.Items.Add(n);
        }
    }

    TreeViewItem? FilterTree(TreeViewItem node, string query)
    {
        bool match = node.Header.ToString()!.ToLower().Contains(query);
        var matches = new List<TreeViewItem>();
        foreach (TreeViewItem child in node.Items)
        {
            var res = FilterTree(child, query);
            if (res != null)
            {
                matches.Add(res);
            }
        }
        if (match || matches.Count > 0)
        {
            var clone = new TreeViewItem { Header = node.Header, Tag = node.Tag };
            foreach (var m in matches)
            {
                clone.Items.Add(m);
            }

            return clone;
        }
        return null;
    }

    void JarTree_SelectedItemChanged(object s, RoutedPropertyChangedEventArgs<object> e)
    {
        if (jarPath == null)
        {
            return;
        }

        if (e.NewValue is not TreeViewItem item)
        {
            return;
        }

        if (item.Tag is not string entryPath || string.IsNullOrWhiteSpace(entryPath))
        {
            return;
        }

        string ext = System.IO.Path.GetExtension(entryPath).ToLower();

        if (ext == ".class")
        {
            ShowCode(JarProcessor.DecompileClass(jarPath, entryPath));
            return;
        }

        if (JarProcessor.IsImage(entryPath))
        {
            var img = JarProcessor.ExtractImage(jarPath, entryPath);
            if (img != null)
            {
                ShowImage(img);
            }
            else
            {
                ShowCode("// Image could not be loaded");
            }

            return;
        }

        if (ext == ".jar")
        {
            if (item.Items.Count == 0)
            {
                var sub = JarProcessor.ExtractStructureFromInnerJar(jarPath, entryPath);
                if (sub != null)
                {
                    foreach (var node in sub)
                    {
                        item.Items.Add(node);
                    }

                    item.IsExpanded = true;
                }
                else
                {
                    ShowCode("// Could not open inner JAR");
                }
            }
            return;
        }

        ShowCode(JarProcessor.ReadTextFromJar(jarPath, entryPath));
    }

    void ShowCode(string text)
    {
        ImageViewer.Visibility = Visibility.Collapsed;
        CodeViewer.Visibility = Visibility.Visible;
        CodeViewer.ApplyTemplate();
        CodeViewer.Text = text;
        CodeViewer.ScrollToHome();

        // Run post-layout work at Loaded priority so the control is measured/arranged.
        CodeViewer.Dispatcher.BeginInvoke(new Action(() => {
            try {
                var ta = CodeViewer.TextArea;
                if (ta != null) {
                    var tv = ta.TextView;
                    if (tv != null) {
                        try {
                            tv.EnsureVisualLines();
                        } catch (Exception ex) { Debug.WriteLine("EnsureVisualLines scheduled failed: " + ex.Message); }
                    }
                }
            } catch (Exception ex) { Debug.WriteLine("ShowCode scheduled work failed: " + ex.Message); }
        }), System.Windows.Threading.DispatcherPriority.Loaded);
    }

    void ShowImage(BitmapImage? img)
    {
        if (img == null)
        {
            return;
        }

        CodeViewer.Visibility = Visibility.Collapsed;
        ImageViewer.Visibility = Visibility.Visible;
        ImageViewer.Source = img;
    }

    void DragWindow(object s, MouseButtonEventArgs e)
    {
        if (e.LeftButton == MouseButtonState.Pressed)
        {
            DragMove();
        }
    }

    void ToggleMaximize(object s, RoutedEventArgs e)
    {
        double targetScale = maximized ? 1.0 : 1.04;
        var scale = new System.Windows.Media.ScaleTransform(1, 1);
        Root.RenderTransformOrigin = new Point(0.5, 0.5);
        Root.RenderTransform = scale;

        var anim = new DoubleAnimation(targetScale, new Duration(System.TimeSpan.FromMilliseconds(300)))
        {
            EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseOut },
            AutoReverse = true
        };
        scale.BeginAnimation(System.Windows.Media.ScaleTransform.ScaleXProperty, anim);
        scale.BeginAnimation(System.Windows.Media.ScaleTransform.ScaleYProperty, anim);

        if (maximized)
        {
            WindowState = WindowState.Normal;
            Root.Margin = new Thickness(0);
            maximized = false;
        }
        else
        {
            WindowState = WindowState.Maximized;
            Root.Margin = new Thickness(6);
            maximized = true;
        }
    }

    void CloseApp(object s, RoutedEventArgs e) => Close();

    void OnDropJar(object s, DragEventArgs e)
    {
        if (!e.Data.GetDataPresent(DataFormats.FileDrop))
        {
            return;
        }

        var files = (string[])e.Data.GetData(DataFormats.FileDrop);
        var jar = files.FirstOrDefault(f => f.EndsWith(".jar"));
        if (jar == null)
        {
            return;
        }

        jarPath = jar;
        LoadJar(jarPath);
    }

    void OnPreviewDragOver(object s, DragEventArgs e)
    {
        e.Handled = true;
        e.Effects = DragDropEffects.Copy;
    }
}