using System;
using System.Collections.Generic;
using System.Collections;
using System.Drawing;
using System.Windows.Forms;
using nft.ui.command;

namespace nft.framework.drawing
{
    /// <summary>
    /// IDrawableView is drawing delegate for DrawablePanel
	/// </summary>
    public interface IViewDrawer : IDisposable{
        void Attach(IView view);
        void Detach(IView view);
        void Draw(Graphics g, IView owner, Rectangle destArea, Region requestSrc);
        Size ContentSize { get; }
        Size ScrollUnit { get; }
    }

    /// <summary>
    /// the container of IViewDrawer should implements IView
    /// </summary>
    public interface IView {
        /// <summary>
        /// visible area of IViewDrawer content on this IView.
        /// </summary>
        Rectangle VisibleSourceRect { get; }
        /// <summary>
        /// get or set top-left positon of visible rectangle.
        /// </summary>
        Point ViewPosition { get; set; }
        /// <summary>
        /// Notify view content changed.
        /// Will raise callback to IViewDrawer.Draw()
        /// </summary>
        /// <param name="srcUpdated"></param>
        /// <param name="callbackImmediately"></param>
        void NotifyUpdate(Region srcUpdated, bool callbackImmediately);
        /// <summary>
        /// Notify content size changed.
        /// Will raise callback to IViewDrawer.ContentSize and IViewDrawer.ScrollUnit.
        /// </summary>
        /// <param name="newViewPosition"></param>
        void NotifyContentSizeChanged(Point newPos);
        /// <summary>
        /// Screen Surface to draw
        /// </summary>
        ISurfaceOld ScreenSurface { get; }
    }

    /// <summary>
    /// Abstract class of IViewDrawer
    /// </summary>
    public abstract class ViewDrawerBase : IViewDrawer{
        protected List<IView> views = new List<IView>(4);
        protected ISurfaceOld backSurface;
        protected bool doubleBuffered = true;
      
        #region IViewDrawer members
        public abstract Size ContentSize { get; }

        public abstract Size ScrollUnit { get; }

        public virtual void Attach(IView view) {
            if (!views.Contains(view)) {
                views.Add(view);
            }
        }

        public virtual void Detach(IView view) {
            if (views.Contains(view)) {
                views.Remove(view);
            }
        }

        public virtual void Draw(Graphics g, IView owner, Rectangle destArea, Region requestSrc) {
            if (DoubleBuffered) {
                if (OffscreenSurface == null) {
                    backSurface = PrepareOffscreenSurface();
                    if (backSurface == null) {
                        doubleBuffered = false;
                        Draw(g, owner, destArea, requestSrc);
                        return;
                    }
                }                
                DrawCore(OffscreenSurface, g, owner, destArea, requestSrc);
                DrawParams dp = new DrawParams(owner.ScreenSurface, destArea.Location);
                dp.SoruceClip = destArea;
                owner.ScreenSurface.BitBlt(OffscreenSurface, dp);
            } else {
                DrawCore(owner.ScreenSurface, g, owner, destArea, requestSrc);
            }
        }

        #endregion

        public int AttachedViewCount { get { return views.Count; } }

        protected virtual ISurfaceOld PrepareOffscreenSurface() {
            IGraphicManagerOld gm = GlobalModules.GraphicManagerOld;
            ISurfaceOld s = gm.CreateOffscreenSurface(Screen.PrimaryScreen.Bounds.Size);
            return s;
        }

        protected void NotifyAllUpdated(bool callbackImmediately) {
            //Region rgn = new Region(new Rectangle(new Point(), ContentSize));
            NotifyUpdated(null, callbackImmediately);
        }

        protected virtual void NotifyUpdated(Region updateRgn, bool callbackImmediately) {
            foreach (IView vw in views) {
                vw.NotifyUpdate(updateRgn, callbackImmediately);
            }
        }

        protected abstract void DrawCore(ISurfaceOld surfDest, Graphics g, IView owner, Rectangle rctDest, Region requestSrc);

        public bool DoubleBuffered {
            get { return doubleBuffered; }
            set {
                if (doubleBuffered != value) {
                    if (value) {
                        backSurface = PrepareOffscreenSurface();
                    } else if (backSurface != null) {
                        backSurface.Dispose();
                        backSurface = null;
                    }
                    doubleBuffered = value;
                }
            }
        }

        public ISurfaceOld OffscreenSurface {
            get { return backSurface; }
        }

        #region IDisposable ŽÀ‘•

        private bool disposed = false;

        public void Dispose() {
            GC.SuppressFinalize(this);
            this.Dispose(true);
        }

        ~ViewDrawerBase() {
            Dispose(false);
        }

        protected virtual void Dispose(bool disposing) {
            if (this.disposed) {
                return;
            }
            this.disposed = true;
            if (disposing) {
                views = null;
                backSurface.Dispose();
            }
        }
        #endregion
    }
}
