using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using VisimBox.Resources;
using System.Xml.Serialization;

namespace VisimBox.Renderer
{
    /// <summary>
    /// Types of supported texture formats
    /// </summary>
    public enum TextureFormat
    {
        /// <summary>
        /// RGB texture with 8 bits per channel, 8 bits for alpha, but unused
        /// </summary>
        RGB,
        /// <summary>
        /// RGBA texture, 8 bits per color channel, 8 bits for alpha value
        /// </summary>
        RGBA,
        /// <summary>
        /// 8 bit Grayscale texture
        /// </summary>
        GRAY,
        /// <summary>
        /// Luminance with alpha - 4 bits for luminance, 4 bits for alpha
        /// </summary>
        LA4,
        /// <summary>
        /// Float texture - for zbuffer textures
        /// </summary>
        FLOAT
    }
    
    
    /// <summary>
    /// Basic texture class
    /// </summary>
    public abstract class Texture : IResource, IXmlSerializable
    {
        #region IResource fields
        protected string _name;
        protected string _filename;
        protected int bytesSize;
        protected ResourceUsage usage;

        #endregion

        #region Fields

        protected int _width, _height;
        protected int _originalwidth, _originalheight;
        protected TextureFormat _format;

        #endregion


        /// <summary>
        /// Internal name of the texture
        /// </summary>
        public string Name
        {
            get
            {
                return _name;
            }
        }

        /// <summary>
        /// File name of the texture
        /// </summary>
        public string FileName
        {
            get
            {
                return _filename;
            }
            set
            {
                _filename = value;
            }
        }

        /// <summary>
        /// Actual width of the texture (textures with incorrect sizes will be resized to width/height of power of two)
        /// </summary>
        public int Width
        {
            get
            {
                return _width;
            }
        }

        /// <summary>
        /// Actual height of the texture (textures with incorrect sizes will be resized to width/height of power of two)
        /// </summary>
        public int Height
        {
            get
            {
                return _height;
            }
        }


        /// <summary>
        /// Original width of the texture (as was in the file / source data)
        /// </summary>
        public int OriginalWidth
        {
            get
            {
                return _originalwidth;
            }
        }

        /// <summary>
        /// Original height of the texture (as was in the file / source data)
        /// </summary>
        public int OriginalHeight
        {
            get
            {
                return _originalheight;
            }
        }

        /// <summary>
        /// Center of the texture in X
        /// </summary>
        public int CenterX {get; set;}
        /// <summary>
        /// Center of the texture in Y
        /// </summary>
        public int CenterY { get; set; }

        /// <summary>
        /// Is texture wrappable?
        /// </summary>
        public virtual bool Wrappable { get; set; }

        /// <summary>
        /// Size of the texture in bytes
        /// </summary>
        public virtual int SizeInBytes
        {
            get
            {
                return bytesSize;
            }
        }

        /// <summary>
        /// Texture format (RGB/RGBA)
        /// </summary>
        public TextureFormat Format
        {
            get
            {
                return _format;
            }
        }

        /// <summary>
        /// True if texture contains alpha value
        /// </summary>
        public virtual bool HasAlpha
        {
            get
            {
                switch (_format)
                {
                    case TextureFormat.RGBA:
                        return true;
                    case TextureFormat.LA4:
                        return true;
                    default:
                        return false;
                }
            }
        }

        public virtual void Dispose()
        {
            DisposeConsumers();
        }

        /// <summary>
        /// If true, resource will stay in memory even if it's not referenced
        /// </summary>
        public virtual bool AlwaysStay { get; set; }

        public ResourceType ResourceType
        {
            get { return ResourceType.Texture; }
        }

        public ResourceUsage Usage
        {
            get { return usage; }
        }

        protected List<IResourceConsumer> Consumers = new List<IResourceConsumer>();

        /// <summary>
        /// Number of current references
        /// </summary>
        public int References { get { return Consumers.Count; } }

        /// <summary>
        /// Returns number of null generic references
        /// </summary>
        public int NullReferences
        {
            get
            {
                int rval = 0;
                for (int i = 0; i < Consumers.Count; i++)
                {
                    if (Consumers[i] == null)
                        rval++;
                }
                return rval;
            }
        }

        /// <summary>
        /// Adds reference to this resource
        /// </summary>
        /// <param name="consumer">Consumer of resource</param>
        public virtual void AddReference(IResourceConsumer consumer)
        {
            Consumers.Add(consumer);
        }

        /// <summary>
        /// Removes reference to this resource
        /// </summary>
        /// <param name="consumer">Consumer of resource</param>
        public virtual void RemoveReference(IResourceConsumer consumer)
        {
            Consumers.Remove(consumer);
        }

        /// <summary>
        /// Manager wants to replace this resource by another - resource should inform all of it's consumers that it's being replaced 
        /// and update references based on their response
        /// It should also check if new resource is of compatible type
        /// </summary>
        /// <param name="newresource">New resource</param>
        /// <returns>True if resource is compatible type and all consumers accepted resource changing, false otherwise</returns>
        public virtual bool ReplaceReferences(IResource newresource)
        {
            if ((newresource as Texture) == null)
                return false;
            bool rval = true;

            for (int i = 0; i < Consumers.Count; i++)
            {
                IResourceConsumer consumer = Consumers[i];
                if (consumer != null)
                {
                    bool consresponse = consumer.ResourceReplaced(this, newresource);

                    if (consresponse)
                    {
                        RemoveReference(consumer);

                        if (newresource != null)
                        {
                            newresource.AddReference(consumer);
                        }
                    }
                    else rval = false;
                }
            }

            return rval;
        }

        protected virtual void DisposeConsumers()
        {
            for (int i = 0; i < Consumers.Count; i++)
            {
                if (Consumers[i] != null)
                {
                    Consumers[i].ResourceDisposing(this);
                }
            }
            Consumers.Clear();
        }




        /// <summary>
        /// Returns internal ID for direct (and faster) access to renderer. Not all renderers may support this.
        /// If InternalID = -1, texture doesn't have internal id or is not valid
        /// </summary>
        public virtual int InternalID
        {
            get { return -1; }
        }

        /// <summary>
        /// Activates texture in specified render context
        /// </summary>
        /// <param name="rc"></param>
        public abstract void Activate(IRenderContext rc);

        /// <summary>
        /// Creates System.Drawin.Bitmap from actual version of the texture and returns it
        /// User must dispose it correctly by himself.
        /// </summary>
        /// <returns>Bitmap containg image in the current texture</returns>
        public abstract System.Drawing.Bitmap GetBitmap();

        #region IXmlSerializable Members

        /// <summary>
        /// Reqired by IXMLserializable
        /// </summary>
        /// <returns>Null</returns>
        public System.Xml.Schema.XmlSchema GetSchema()
        {
            return null;
        }

        /// <summary>
        /// Deserializes object from XML
        /// </summary>
        /// <param name="reader">XML reader</param>
        public virtual void ReadXml(System.Xml.XmlReader reader)
        {
            //this should never happen!
            throw new ArgumentException();//XML import
        }

        /// <summary>
        /// Serializes object to XML
        /// </summary>
        /// <param name="writer">XML writer to save values</param>
        public virtual void WriteXml(System.Xml.XmlWriter writer)
        {
            writer.WriteAttributeString("name", this.Name);
            if (this.FileName != null)
            {
                writer.WriteAttributeString("file", this.FileName);
            }
            //todo: other parameters? warning for textures without data?
        }


        #endregion
    }//Texture

}
