using System;
using ECOT.Engine;
using ECOT.Game.Tools;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using ECOT.Engine.Statistics;

namespace ECOT.Game.Buildings
{
    /// <summary>
    /// Catégories de bâtiments.
    /// </summary>
    public enum BuildingCategory
    {
        Water,
        Food,
        Energy,
        Ecology,
        Education,
    }

    /// <summary>
    /// Etats des bâtiments.
    /// </summary>
    public enum BuildingState
    {
        Construction,
        InService,
        OutOfService,
        Abandoned
    }

    /// <summary>
    /// Interface spécifique pour les bâtiment inclus dans des villes.
    /// </summary>
    public interface ICityBuiding
    {
        void AttachTo(City city, Vector2 slotPos);
        Vector2 SlotPosition
        { get; }
    }

    public delegate void BuildingEventHandler(Building building);

    /// <summary>
    /// La classe building représente un bâtiment, elle contient toutes ses statistiques.
    /// Cette classe abstraite est commune à tout type de structure dans le jeu.
    /// </summary>
    public abstract class Building : ISelectable, ICloneable
    {
        #region Membres

        /// <summary>
        /// Position du bâtiment dans les coordonnées de l'écran.
        /// </summary>
        protected Vector2 position;
        /// <summary>
        /// Informations du bâtiment.
        /// <see cref="BuildingInfo"/>
        /// </summary>
        protected BuildingInfo infos;
        /// <summary>
        /// Catégorie du bâtiment
        /// <see cref="BuildingCategory"/>
        /// </summary>
        protected BuildingCategory category;
        /// <summary>
        /// 
        /// </summary>
        protected BuildingState state = BuildingState.Construction;
        /// <summary>
        /// Poduction actuelle du bâtiment.
        /// </summary>
        protected Variable currentProduction = new Variable();

        /// <summary>
        /// Sprite avant, utilisé pour s'afficher en premier plan au cas où certaines entités du jeu voudraient s'afficher
        /// "dans" le bâtiment, par exemple les trains pour la gare.
        /// </summary>
        protected Sprite frontsprite = null;

        protected bool selected = false;
        /// <summary>
        /// Nombre de jours de mise en service.
        /// </summary>
        protected int days = 0;
        protected Tile tile = null; // tile sur lequel le bâtiment se trouve

        #endregion

        private int currentConstructionTime = 0;
        private float constructionProgress = 0.0f;

        #region Initialisation

        /// <summary>
        /// Ctor.
        /// </summary>
        /// <param name="infos">Informations du bâtiment</param>
        /// <param name="category">Catégorie du bâtiment</param>
        /// <param name="preview">Prévisualisation du bâtiment</param>
        public Building(BuildingInfo infos, BuildingCategory category)
        {
            this.infos = infos;
            this.category = category;
            this.CreateSprites();
        }

        /// <summary>
        /// Ctor privé utiliser pour le clonage.
        /// </summary>
        protected Building()
        {
        }

        private void CreateSprites()
        {
            string textureName = @"Textures\Buildings\Buildings";

            // Charge le sprite de premier plan s'il n'est pas animée
            if ((this.infos.IsAnimation == true))
            {
                frontsprite = new Sprite(textureName, 0, 0, 64, 128, 29, 0.1f);
                frontsprite.Play(PlayMode.Loop);                
            }
            else
            {
                Rectangle rect = this.infos.TextureRect;
                this.frontsprite = new Sprite(textureName, rect.X, rect.Y, (uint)rect.Width, (uint)rect.Height, HotSpotType.LeftCenter);
            }

            this.frontsprite.HotSpotType = HotSpotType.LeftCenter;
        }

        #endregion

        #region Propriétés

        public string Name
        {
            get { return this.infos.Name; }
        }

        public Sprite FrontSprite
        {
            get { return this.frontsprite; }
        }


        public BuildingCategory Category
        {
            get { return this.category; }
        }

        public string CategoryText
        {
            get { return this.category.ToString(); }
        }



        public BuildingInfo Infos
        {
            get { return this.infos; }
            set { this.infos = value; }
        }

        public BuildingState State
        {
            get { return this.state; }
            set
            {
                this.state = value;
            }
        }

        public Vector2 Position
        {
            get { return this.position; }
        }

        public Tile Tile
        {
            get { return this.tile; }
            set { this.tile = value; }
        }

        public double ConstructionCost
        {
            get
            {
                return this.infos.ConstructionCost;
            }
        }

        public double ConstructionTime
        {
            get
            {
                return this.infos.ConstructionTime;
            }
        }

        #endregion

        #region Méthodes

        #region Rendu

        /// <summary>
        /// Rendu bâtiment
        /// <remarks>Cette méthode héritera d'un objet plus générique pour le rendu.</remarks>
        /// </summary>
        public virtual void Render(GameTime gameTime)
        {
            if (this.state != BuildingState.Construction)
            {
                // Dessine le sprite de premier plan
                if (this.infos.IsAnimation)
                {
                    this.frontsprite.Playing = true;
                    this.frontsprite.Update(gameTime);
                }
                this.frontsprite.Draw(this.position, 0.0f, 0.0f, 1.0f, Color.White);
            }
        }

        #endregion


        #region ISelectable

        public virtual void Focus()
        {
            this.selected = true;
        }

        public virtual void Blur()
        {
            this.selected = false;
        }

        public abstract void Hover();

        public abstract void Out();

        public abstract bool IsSelected(Vector2 position);

        #endregion

        #region ICloneable

        public abstract object Clone();

        #endregion

        public virtual void DayChanged()
        {
            this.days++;

            // En construction
            if (this.state == BuildingState.Construction)
            {
                // Si le temps de construction arrive à échéance, le bâtiment passe en service
                if(this.State != BuildingState.InService)
                    this.Online(true, true);
            }
        }

        #endregion

        public virtual bool CanBuildOn(Tile tile)
        {
            return ((tile.Family & TileFamily.Constructible) != 0);
        }

        public virtual void Online(bool firstOnline, bool launchSound)
        {
            this.days = 0;
            this.State = BuildingState.InService;
        }
    }

    /// <summary>
    /// Classe représentant un bâtiment d'un ville. Elle permet donc de le lier à la ville et à ses statistiques.
    /// </summary>
    public abstract class CityBuilding : Building, ICityBuiding
    {
        #region Membres

        /// <summary>
        /// Ville mère du bâtiment.
        /// </summary>
        protected City city = null;

        protected Vector2 slotPosition = Vector2.Zero;

        protected bool hover = false;

        protected string prefixName;

        protected Rectangle streetElemOffsetBack;
        protected Rectangle streetElemOffsetFront;

        public static Texture2D ElementsBack = null;
        public static Texture2D ElementsFront = null;

        #endregion

        #region Initialisation

        /// <summary>
        /// Ctor.
        /// </summary>
        /// <param name="infos">Information du bâtiment</param>
        /// <param name="category">Catégorie du bâtiment</param>
        /// <param name="preview">Prévisualisation du bâtiment</param>
        public CityBuilding(BuildingInfo infos, BuildingCategory category)
            : base(infos, category)
        {
        }

        protected object Initialize(CityBuilding clone)
        {
            clone.city = null;
            clone.position = this.position;
            clone.infos = this.infos;
            clone.category = this.category;
            clone.frontsprite = (Sprite)this.frontsprite.Clone();

            return clone;
        }

        #endregion

        #region Propriétés

        public City City
        {
            get { return city; }
            set { city = value; }
        }

        #endregion

        #region Méthodes

        #region ICityBuiding

        /// <summary>
        /// Attatche le bâtiment à une ville et enregistre ses statistiques.
        /// </summary>
        /// <param name="city">Ville mère du bâtiment</param>
        /// <param name="slotPos">Position de l'emplacement du bâtiment dans la ville</param>
        public void AttachTo(City city, Vector2 slotPos)
        {
            this.City = city;
            this.position = new Vector2(
                (this.City.Position.X + slotPos.X - Rules.Cities.MaxDrawArea) * 64,
                (this.City.Position.Y + slotPos.Y - Rules.Cities.MaxDrawArea - Rules.Cities.Size.Height / 2) * 64);
            this.slotPosition = slotPos;
        }

        public Vector2 SlotPosition
        {
            get { return this.slotPosition; }
        }

        #endregion

        #region ISelectable

        public override void Focus()
        {
            base.Focus();
        }

        public override void Hover()
        {
            this.hover = true;
        }

        public override void Out()
        {
            this.hover = false;
        }

        public override bool IsSelected(Vector2 position)
        {
            return (position == new Vector2((int)(this.position.X / 64), (int)(this.position.Y / 64)));
        }

        #endregion

        public override void Render(GameTime gameTime)
        {
#if XBOX
            if (this.hover && GameController.Instance.CurrentTool == ToolType.Selection && GameRenderer.Instance.ShowCursor)
            {
                Kernel.Instance.SpriteBatch.Draw(
                    Kernel.Instance.Medias.Get<Texture2D>(@"Textures\Common\MapCursor"),
                    new Rectangle((int)this.position.X - 15, (int)this.position.Y - 15, 64 + 30, 64 + 30),
                    null,
                    new Color(30, 144, 255));
            }
#endif
                Kernel.Instance.SpriteBatch.Draw(
                    ECOT.Game.City.StreetsTexture,
                    this.position,
                    new Rectangle(256, 256, 64, 64),
                    Color.White);

                Kernel.Instance.SpriteBatch.Draw(
                    CityBuilding.ElementsBack, this.position, this.streetElemOffsetBack, Color.White);
            

            base.Render(gameTime);

                Kernel.Instance.SpriteBatch.Draw(
                    CityBuilding.ElementsFront, this.position, this.streetElemOffsetFront, Color.White);

        }
         #endregion
    }
}
