using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.IO;
using nft.framework.plugin;

namespace nft.framework.drawing {
    /// <summary>
    /// Manage reference count of Image object and dispose on proper timing.
    /// </summary>
    public abstract class ImageRef {
        /// <summary>
        /// Used as TranspalentColor to indicate that this image has no color to be transpalent.
        /// </summary>
        public static Color Undefined = Color.FromArgb((~Color.Transparent.ToArgb())&0xffffff);
        public enum Lifetime : short { Immediate = 0, Short, Long, Permanent };
        private Lifetime life;
        private short refcount;
        private object imgref = null;
        private Color transpalent = Color.Magenta;

        protected ImageRef()
            : this(Lifetime.Long) {
        }

        protected ImageRef(Lifetime lifetime) {
            this.life = lifetime;
            this.refcount = 0;
        }

       /// <summary>
       /// Do not call while reference count is not zero.
       /// </summary>
        protected void ForceDispose(){
            if (refcount > 0)
                throw new InvalidOperationException("Reference count is not zero.");
            if (imgref != null) {
                if (imgref is WeakReference) {
                    WeakReference wr = (WeakReference)imgref;
                    if (wr.IsAlive) {
                        ((Image)wr.Target).Dispose();
                    }
                } else {
                    ((Image)imgref).Dispose();
                }
                imgref = null;
            }
        }

        protected Image PrepareImage() {
            Image ret;
            if (imgref != null) {
                if (imgref is WeakReference) {
                    WeakReference wr = (WeakReference)imgref;
                    if (wr.IsAlive) {
                        ret = (Image)wr.Target;
                    } else {
                        ret = CreateImage();
                    }
                } else {
                    ret = (Image)imgref;
                }
            } else {
                ret = CreateImage();
            }
            return ret;
        }

        public int RefCount { get { return refcount; } }

        public Color TranspalentColor {
            get { return transpalent; }
            set { transpalent = value; }
        }

        /// <summary>
        /// Returns image and increment reference count.
        /// NEVER Dispose the returned image. call ReleaseRef method instead.
        /// </summary>
        public Image AddRef() {
            Image ret = null;
            if (refcount == 0) {
                ret = PrepareImage();
                imgref = ret;
            } else {
                ret = (Image)imgref;
            }
            this.refcount++;
            return ret;
        }

        /// <summary>
        /// Do not access without calling AddRef() method.
        /// </summary>
        public Image Image {
            get {
                return imgref as Image;
            }
        }

        /// <summary>
        /// Decrease reference count.
        /// Do not Dispose the image which returned by AddRef method.
        /// </summary>
        public void ReleaseRef() {
            if (refcount > 0) {
                this.refcount--;
                if (refcount == 0) {
                    Isolated();
                }
            }
        }

        protected void Isolated() {
            switch (life) {
                case Lifetime.Long:
                    imgref = new WeakReference(imgref, true);
                    break;
                case Lifetime.Short:
                    imgref = new WeakReference(imgref, false);
                    break;
                case Lifetime.Permanent:
                    break;
                case Lifetime.Immediate:
                    DisposeImage((Image)imgref);
                    imgref = null;
                    break;
            }
        }

        public Lifetime ReferenceLife {
            get { return this.life; }
            set { this.life = value; }
        }

        protected virtual void DisposeImage(Image image) {
            image.Dispose();
        }

        protected abstract Image CreateImage();
    }

}
