//
//  The Pimp      The Pathetic Image Manipulation Program
//  Copyright (c) 2004-2008 Sam Hocevar <sam@zoy.org>
//                All Rights Reserved
//
//  $Id$
//
//  This library is free software. It comes without any warranty, to
//  the extent permitted by applicable law. You can redistribute it
//  and/or modify it under the terms of the Do What The Fuck You Want
//  To Public License, Version 2, as published by Sam Hocevar. See
//  http://sam.zoy.org/wtfpl/COPYING for more details.
//

using System;
using Gtk;
using Pipi;

namespace ThePimp
{
    public class PictureArea : Gtk.DrawingArea
    {
        private Pipi.Picture _p;
        private Adjustment _hadj = null, _vadj = null;
        private bool _drag = false;
        private double _xdrag, _ydrag;

        void HAdjust(object sender, EventArgs args)
        {
            QueueDraw();
        }

        void VAdjust(object sender, EventArgs args)
        {
            QueueDraw();
        }

        protected override bool OnExposeEvent(Gdk.EventExpose e)
        {
            bool ret = base.OnExposeEvent(e);

            if(_hadj == null || _vadj == null)
                return ret;

            using (Gdk.GC gc = new Gdk.GC(GdkWindow))
            {
                int w = e.Area.Width;
                int h = e.Area.Height;
                int x = (int)_hadj.Value + e.Area.X;
                int y = (int)_vadj.Value + e.Area.Y;
                if(x + w > _p.Width)
                    w = _p.Width - x < 0 ? 0 : _p.Width - x;
                if(y + h > _p.Height)
                    h = _p.Height - y < 0 ? 0 : _p.Height - y;

                byte[] a = _p.GetPixels(w, h, x, y);
                GdkWindow.DrawRgb32Image(gc, e.Area.X, e.Area.Y, w, h,
                                         0 /* no dithering */, a, w * 4);
            }

            return ret;
        }

        protected override void OnRealized()
        {
            base.OnRealized();
        }

        protected override bool OnButtonPressEvent(Gdk.EventButton e)
        {
            if(e.Button == 2)
            {
                GdkWindow.Cursor = new Gdk.Cursor(Gdk.CursorType.Hand1);
                _drag = true;
                _xdrag = e.X;
                _ydrag = e.Y;
            }

            return base.OnButtonPressEvent(e);
        }

        protected override bool OnButtonReleaseEvent(Gdk.EventButton e)
        {
            if(e.Button == 2)
            {
                GdkWindow.Cursor = null;
                _drag = false;
            }

            return base.OnButtonReleaseEvent(e);
        }

        protected override bool OnMotionNotifyEvent(Gdk.EventMotion e)
        {
            if(_drag)
            {
                if(_hadj != null && e.X != _xdrag)
                {
                    _hadj.Value += _xdrag - e.X;
                    _xdrag = e.X;
                    if(_hadj.Value + Allocation.Width > _p.Width)
                        _hadj.Value = _p.Width - Allocation.Width;
                    _hadj.ChangeValue();
                }
                if(_vadj != null && e.Y != _ydrag)
                {
                    _vadj.Value += _ydrag - e.Y;
                    _ydrag = e.Y;
                    if(_vadj.Value + Allocation.Height > _p.Height)
                        _vadj.Value = _p.Height - Allocation.Height;
                    _vadj.ChangeValue();
                }
            }

            return base.OnMotionNotifyEvent(e);
        }

        protected override void OnSetScrollAdjustments(Adjustment hadj,
                                                       Adjustment vadj)
        {
            _hadj = hadj;
            _vadj = vadj;
            if(hadj != null)
                hadj.ValueChanged += HAdjust;
            if(vadj != null)
                vadj.ValueChanged += VAdjust;
        }

        protected override void OnSizeAllocated(Gdk.Rectangle alloc)
        {
            base.OnSizeAllocated(alloc);

            if(_hadj != null)
            {
                _hadj.SetBounds(0, _p.Width, 1, alloc.Width, alloc.Width);
                if(_hadj.Value + alloc.Width > _p.Width)
                {
                    _hadj.Value = _p.Width - alloc.Width;
                    _hadj.ChangeValue();
                }
            }

            if(_vadj != null)
            {
                _vadj.SetBounds(0, _p.Height, 1, alloc.Height, alloc.Height);
                if(_vadj.Value + alloc.Height > _p.Height)
                {
                    _vadj.Value = _p.Height - alloc.Height;
                    _vadj.ChangeValue();
                }
            }
        }

        protected override void OnDestroyed()
        {
            if(_hadj != null)
            {
                _hadj.ValueChanged -= HAdjust;
                _hadj = null;
            }

            if(_vadj != null)
            {
                _vadj.ValueChanged -= VAdjust;
                _vadj = null;
            }
        }

        public PictureArea(Picture p)
        {
            _p = p;

            AddEvents((int)Gdk.EventMask.ButtonPressMask);
            AddEvents((int)Gdk.EventMask.ButtonReleaseMask);
            AddEvents((int)Gdk.EventMask.PointerMotionMask);
        }
    }

    public partial class PictureView : Gtk.ScrolledWindow
    {
        public readonly Picture Picture;

        public PictureView(Picture p)
        {
            Picture = p;
            Add(new PictureArea(p));
            ShowAll();
        }
    }
}