using System;
using System.IO;
using Gtk;
using UI = Gtk.Builder.ObjectAttribute;

public class MapWindow : IDisposable
{
    [UI] private DrawingArea MapArea       = null;
    [UI] private Label       MapTitleLabel = null;
    [UI] private Button      MapBackButton = null;

    [UI] private Label        MapCaseLabel      = null;
    [UI] private RadioButton  BodySkeletonRadio = null;
    [UI] private RadioButton  BodyJacketRadio   = null;
    [UI] private ComboBoxText JacketSizeCombo   = null;
    [UI] private RadioButton  ViewFrontRadio    = null;
    [UI] private RadioButton  ViewBackRadio     = null;
    [UI] private RadioButton  ViewRightRadio    = null;
    [UI] private RadioButton  ViewLeftRadio     = null;
    [UI] private CheckButton  HideCircleCheck   = null;
    [UI] private CheckButton  BlackAudioCheck   = null;
    [UI] private Button       SoundTestButton   = null;
    [UI] private TextView     MapLogView        = null;
    [UI] private Box          MapControlSlots   = null;

    private readonly Language _lang;
    private readonly SoundLooper _sound = new SoundLooper();

    private Gdk.Pixbuf _body;
    private Gdk.Pixbuf _map;

    private const double MapAlpha = 0.7;

    public event EventHandler BackRequested;

    public MapWindow(Builder builder, Language language)
    {
        builder.Autoconnect(this);
        _lang = language;

        MapArea.Drawn += OnMapDrawn;
        MapBackButton.Clicked += OnBackClicked;

        _lang.Register("MapBackButton", MapBackButton);
        _lang.RegisterDynamic("MapTitleLabel", MapTitleLabel);
        _sound.Log += msg => Logger.Write("sound", msg);
    }

    /// <summary>Called when a disease is picked. Loads images, starts the loop.</summary>
    public void ShowCase(string title, string bodyPath, string mapPath, string soundPath)
    {
        MapTitleLabel.Text = title ?? "";

        DisposePixbufs();
        _body = LoadPixbuf(bodyPath, false);
        _map  = LoadPixbuf(mapPath, true);   // true = key out black

        Logger.Write("case", "open: " + title + " | map=" + mapPath + " | sound=" + soundPath);

        MapArea.QueueDraw();
        _sound.Start(soundPath);
    }

    /// <summary>Kills audio without touching the images. Call from OnBackClicked.</summary>
    public void StopSession()
    {
        _sound.Stop();
         Logger.Write("case", "closed");
    }

    private void OnBackClicked(object sender, EventArgs e)
    {
        StopSession();
        var h = BackRequested;
        if (h != null) h(this, EventArgs.Empty);
    }

    private void OnMapDrawn(object o, DrawnArgs args)
    {
        var cr = args.Cr;
        int w = MapArea.AllocatedWidth;
        int h = MapArea.AllocatedHeight;

        DrawFitted(cr, _body, w, h, 1.0);
        DrawFitted(cr, _map,  w, h, MapAlpha);

        args.RetVal = true;
    }

    private static void DrawFitted(Cairo.Context cr, Gdk.Pixbuf pb, int w, int h, double alpha)
    {
        if (pb == null) return;

        double s = Math.Min((double)w / pb.Width, (double)h / pb.Height);
        cr.Save();
        cr.Translate((w - pb.Width * s) / 2, (h - pb.Height * s) / 2);
        cr.Scale(s, s);
        Gdk.CairoHelper.SetSourcePixbuf(cr, pb, 0, 0);
        cr.PaintWithAlpha(alpha);
        cr.Restore();
    }

    private static Gdk.Pixbuf LoadPixbuf(string path, bool keyOutBlack)
    {
        if (string.IsNullOrEmpty(path) || !File.Exists(path))
        {
            // Console.WriteLine("[MapWindow] image not found: " + path);
            Logger.Write("map", "image not found: " + path);

            return null;
        }

        try
        {
            var pb = new Gdk.Pixbuf(path);
            if (!keyOutBlack) return pb;

            var keyed = pb.AddAlpha(true, 0, 0, 0);   // == MakeTransparent(Color.Black)
            pb.Dispose();
            return keyed;
        }
        catch (Exception ex)
        {
            // Console.WriteLine("[MapWindow] load failed " + path + ": " + ex.Message);
            Logger.Write("map", "image not found: " + path);

            return null;
        }
    }

    private void DisposePixbufs()
    {
        if (_body != null) { _body.Dispose(); _body = null; }
        if (_map  != null) { _map.Dispose();  _map  = null; }
    }

    public void Dispose()
    {
        _sound.Dispose();
        DisposePixbufs();
    }
}