// The CMLeonOS Project (https://github.com/Leonmmcoset/CMLeonOS)
// Copyright (C) 2025-present LeonOS 2 Developer Team
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

using Cosmos.System.Graphics;
using CMLeonOS;
using CMLeonOS.Gui.UILib;
using CMLeonOS.Logger;
using CMLeonOS.Utils;
using System;
using System.Drawing;
using System.IO;

namespace CMLeonOS.Gui.Apps
{
    internal class ImageViewer : Process
    {
        internal ImageViewer() : base("Image Viewer", ProcessType.Application) { }

        internal ImageViewer(string path) : base("Image Viewer", ProcessType.Application)
        {
            initialPath = path;
        }

        private AppWindow window;
        private Window canvas;
        private Button openButton;
        private Button clearButton;

        private WindowManager wm = ProcessManager.GetProcess<WindowManager>();
        private FileBrowser fileBrowser;

        private Bitmap currentBitmap;
        private string currentPath = string.Empty;
        private string initialPath = string.Empty;

        private const int toolbarHeight = 30;
        private const int padding = 6;
        private const int buttonWidth = 64;

        private void ShowError(string text)
        {
            MessageBox messageBox = new MessageBox(this, "Image Viewer", text);
            messageBox.Show();
        }

        private void Relayout()
        {
            int y = padding;
            int buttonY = y;

            openButton.MoveAndResize(window.Width - (buttonWidth * 2 + padding * 2), buttonY, buttonWidth, toolbarHeight);
            clearButton.MoveAndResize(window.Width - (buttonWidth + padding), buttonY, buttonWidth, toolbarHeight);
            openButton.Render();
            clearButton.Render();

            int canvasY = toolbarHeight + (padding * 2);
            int canvasHeight = window.Height - canvasY - padding;
            canvas.MoveAndResize(padding, canvasY, window.Width - (padding * 2), canvasHeight);
            RenderCanvas();
        }

        private void DrawBitmapFit(Bitmap bitmap, Window target)
        {
            int srcWidth = (int)bitmap.Width;
            int srcHeight = (int)bitmap.Height;
            if (srcWidth <= 0 || srcHeight <= 0)
            {
                return;
            }

            int dstWidth = target.Width - 2;
            int dstHeight = target.Height - 2;
            if (dstWidth <= 0 || dstHeight <= 0)
            {
                return;
            }

            int scaledWidth = dstWidth;
            int scaledHeight = (srcHeight * dstWidth) / srcWidth;
            if (scaledHeight > dstHeight)
            {
                scaledHeight = dstHeight;
                scaledWidth = (srcWidth * dstHeight) / srcHeight;
            }

            scaledWidth = Math.Max(1, scaledWidth);
            scaledHeight = Math.Max(1, scaledHeight);

            int startX = (target.Width - scaledWidth) / 2;
            int startY = (target.Height - scaledHeight) / 2;

            for (int y = 0; y < scaledHeight; y++)
            {
                int srcY = (y * srcHeight) / scaledHeight;
                int sourceRowOffset = srcY * srcWidth;
                int targetY = startY + y;
                for (int x = 0; x < scaledWidth; x++)
                {
                    int srcX = (x * srcWidth) / scaledWidth;
                    int sourceIndex = sourceRowOffset + srcX;
                    Color color = Color.FromArgb(bitmap.RawData[sourceIndex]);
                    target.DrawPoint(startX + x, targetY, color);
                }
            }
        }

        private void RenderCanvas()
        {
            canvas.Clear(Color.FromArgb(20, 20, 20));
            canvas.DrawRectangle(0, 0, canvas.Width, canvas.Height, Color.FromArgb(70, 70, 70));

            if (currentBitmap == null)
            {
                const string hint = "Open a BMP file to preview.";
                canvas.DrawString(hint, Color.LightGray, Math.Max(2, (canvas.Width / 2) - ((hint.Length * 8) / 2)), canvas.Height / 2 - 8);
                wm.Update(canvas);
                return;
            }

            DrawBitmapFit(currentBitmap, canvas);

            string fileName = Path.GetFileName(currentPath);
            int srcWidth = (int)currentBitmap.Width;
            int srcHeight = (int)currentBitmap.Height;
            string info = $"{fileName}  {srcWidth}x{srcHeight}";
            canvas.DrawFilledRectangle(6, canvas.Height - 22, Math.Min(canvas.Width - 12, (info.Length * 8) + 8), 16, Color.FromArgb(0, 0, 0));
            canvas.DrawString(info, Color.White, 10, canvas.Height - 20);

            wm.Update(canvas);
        }

        private bool LoadImage(string path, bool showPopup = true)
        {
            if (string.IsNullOrWhiteSpace(path))
            {
                if (showPopup)
                {
                    ShowError("Path is empty.");
                }
                return false;
            }

            string sanitizedPath = PathUtil.Sanitize(path.Trim());
            if (!File.Exists(sanitizedPath))
            {
                if (showPopup)
                {
                    ShowError("File not found.");
                }
                return false;
            }

            if (!Path.GetExtension(sanitizedPath).Equals(".bmp", StringComparison.OrdinalIgnoreCase))
            {
                if (showPopup)
                {
                    ShowError("Only BMP images are supported.");
                }
                return false;
            }

            try
            {
                byte[] bmpBytes = File.ReadAllBytes(sanitizedPath);
                currentBitmap = new Bitmap(bmpBytes);
                currentPath = sanitizedPath;
                window.Title = $"Image Viewer - {Path.GetFileName(sanitizedPath)}";
                RenderCanvas();
                return true;
            }
            catch (Exception ex)
            {
                Logger.Logger.Instance.Error("ImageViewer", $"Failed to load BMP: {ex.Message}");
                if (showPopup)
                {
                    ShowError("Cannot load this BMP file.");
                }
                return false;
            }
        }

        private void OpenDialog()
        {
            fileBrowser = new FileBrowser(this, wm, (string selectedPath) =>
            {
                if (!string.IsNullOrWhiteSpace(selectedPath))
                {
                    LoadImage(selectedPath);
                }
            }, selectDirectoryOnly: false);
            fileBrowser.Show();
        }

        private void ClearImage()
        {
            currentBitmap = null;
            currentPath = string.Empty;
            window.Title = "Image Viewer";
            RenderCanvas();
        }

        public override void Start()
        {
            base.Start();

            window = new AppWindow(this, 170, 90, 760, 520);
            window.Title = "Image Viewer";
            window.Icon = AppManager.DefaultAppIcon;
            window.CanResize = true;
            window.UserResized = Relayout;
            window.Closing = TryStop;
            wm.AddWindow(window);

            openButton = new Button(window, 0, 0, 1, 1);
            openButton.Text = "Open";
            openButton.OnClick = (_, _) => OpenDialog();
            wm.AddWindow(openButton);

            clearButton = new Button(window, 0, 0, 1, 1);
            clearButton.Text = "Clear";
            clearButton.OnClick = (_, _) => ClearImage();
            wm.AddWindow(clearButton);

            canvas = new Window(this, window, 0, 0, 1, 1);
            wm.AddWindow(canvas);

            Relayout();

            if (!string.IsNullOrWhiteSpace(initialPath))
            {
                LoadImage(initialPath, showPopup: false);
            }

            wm.Update(window);
        }

        public override void Run()
        {
        }
    }
}
