using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using RecrownedAthenaeum.SpecialTypes;
using RecrownedAthenaeum.UI.SkinSystem.Definitions;
using System;
using System.Collections.Generic;
namespace RecrownedAthenaeum.UI.SkinSystem
{
    /// 
    /// A skin is used to group a theme which can then be applied to the UI via the use of modules.
    /// 
    public class Skin : IDisposable, ISkin
    {
        /// 
        /// Whether or not this skin is completed being built and thus ready to use.
        /// 
        public bool Laminated { get; private set; }
        private bool disposed;
        private TextureAtlas textureAtlas;
        Dictionary colors;
        Dictionary> definitions;
        /// 
        /// The texture for the cursor.
        /// 
        public virtual Texture2D CursorTexture { get; private set; }
        /// 
        /// Creates a basic unfilled skin.
        /// 
        /// The texture atlas to use for this skin.
        /// The texture the cursor will be.
        public Skin(TextureAtlas textureAtlas, Texture2D cursorTexture)
        {
            this.textureAtlas = textureAtlas;
            this.CursorTexture = cursorTexture;
            colors = new Dictionary();
            definitions = new Dictionary>();
        }
        /// 
        /// Returns a  with given name of region.
        /// 
        /// Name of region.
        /// The region corresponding to the name.
        public virtual TextureAtlas.Region GetTextureAtlasRegion(string name)
        {
            return textureAtlas[name];
        }
        /// 
        /// Returns a  with given name of defined color;
        /// 
        /// Name of defined color.
        /// The defined color based on the name given.
        public virtual Color GetColor(string name)
        {
            return colors[name];
        }
        /// 
        /// Draws a region from the texture atlas.
        /// 
        /// Region to draw.
        /// The color to tint the region.
        /// The batch to use.
        /// The destination to draw to.
        /// The rotation to use in radians.
        /// The origin for the rotation.
        public virtual void Draw(string regionName, string color, SpriteBatch batch, Rectangle destination, float rotation = 0, Vector2 origin = default(Vector2))
        {
            if (disposed) throw new ObjectDisposedException(GetType().Name);
            textureAtlas.Draw(regionName, batch, destination, colors[color], rotation, origin);
        }
        /// 
        /// Returns an  of the given name and type.
        /// 
        /// Name of definition of the 
        /// The UIModule the definition defines.
        /// The interface for the definition.
        public virtual ISkinDefinitionData ObtainDefinition(string definitionName, Type type)
        {
            if (disposed) throw new ObjectDisposedException(GetType().Name);
            if (!Laminated) throw new InvalidOperationException("Skin has yet to be laminated yet.");
            if (definitionName == null) definitionName = "default";
            if (!definitions.ContainsKey(type) || !definitions[type].ContainsKey(definitionName)) throw new KeyNotFoundException("Could not find skin of type " + type.Name + " with name " + definitionName);
            return definitions[type][definitionName];
        }
        /// 
        /// Returns the default  of the given parameters.
        /// 
        /// The type of definition the default should be coming from.
        /// The default definition for the given type.
        public virtual ISkinDefinitionData ObtainDefinition(Type type)
        {
            if (disposed) throw new ObjectDisposedException(GetType().Name);
            return ObtainDefinition(null, type);
        }
        /// 
        /// Returns the proper definition for the given parameters or throws exception in the case the requested definition does not exist.
        /// 
        /// Convenience to cast to the needed definition type.
        /// The name of the definition.
        /// UIModule type the definition defines.
        /// The definition cast to T.
        public virtual T ObtainDefinition(string definitionName, Type type) where T : ISkinDefinitionData
        {
            if (disposed) throw new ObjectDisposedException(GetType().Name);
            if (definitionName == null) definitionName = "default";
            return (T)ObtainDefinition(definitionName, type);
        }
        /// 
        /// Returns the default definition.
        /// 
        /// Convenience to cast to T.
        /// The type of the UIModule to retrieve the default from.
        /// The default definition for the given type.
        public virtual T ObtainDefinition(Type type) where T : ISkinDefinitionData
        {
            if (disposed) throw new ObjectDisposedException(GetType().Name);
            return ObtainDefinition(null, type);
        }
        /// 
        /// Adds the definition.
        /// 
        /// The name of the definition.
        /// The definition itself.
        public virtual void AddDefinition(string definitionName, ISkinDefinitionData skinDefinition)
        {
            if (disposed) throw new ObjectDisposedException(GetType().Name);
            if (Laminated) throw new InvalidOperationException("This object has been laminated and cannot be edited.");
            if (!definitions.ContainsKey(skinDefinition.UIModuleType))
            {
                definitions.Add(skinDefinition.UIModuleType, new Dictionary());
            } else if (definitions[skinDefinition.UIModuleType].ContainsKey(definitionName)) throw new ArgumentException("Type of definition with that name already exists!");
            definitions[skinDefinition.UIModuleType].Add(definitionName, skinDefinition);
        }
        /// 
        /// Adds color to skin.
        /// 
        /// 
        /// 
        public virtual void AddColor(string name, Color color)
        {
            if (Laminated) throw new InvalidOperationException("This object has been laminated and cannot be edited.");
            colors.Add(name, color);
        }
        /// 
        /// Laminates the skin. Making sure no more additions are done and sets the skin to be ready for use.
        /// Needs to be called before any use of skin. Building skin needs to be done before lamination.
        /// 
        public void Laminate()
        {
            Laminated = true;
        }
        /// 
        /// Disposes  and the  holding the cursor texture.
        /// 
        public void Dispose()
        {
            if (disposed) throw new ObjectDisposedException(GetType().Name);
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        /// 
        /// Overridable dispose function.
        /// 
        /// true when it's a user call to dispose.
        public virtual void Dispose(bool disposing)
        {
            disposed = true;
            if (disposing && !disposed)
            {
                textureAtlas.Dispose();
                CursorTexture.Dispose();
            }
        }
        /// 
        /// Destructor. Calls the dispose with false.
        /// 
        ~Skin()
        {
            Dispose(false);
        }
    }
}