
/****************************************************************************/
/*Copyright (c) 2011, Florent DEVILLE.                                      */
/*All rights reserved.                                                      */
/*                                                                          */
/*Redistribution and use in source and binary forms, with or without        */
/*modification, are permitted provided that the following conditions        */
/*are met:                                                                  */
/*                                                                          */
/* - Redistributions of source code must retain the above copyright         */
/*notice, this list of conditions and the following disclaimer.             */
/* - Redistributions in binary form must reproduce the above                */
/*copyright notice, this list of conditions and the following               */
/*disclaimer in the documentation and/or other materials provided           */
/*with the distribution.                                                    */
/* - The names of its contributors cannot be used to endorse or promote     */
/*products derived from this software without specific prior written        */
/*permission.                                                               */
/* - The source code cannot be used for commercial purposes without         */
/*its contributors' permission.                                             */
/*                                                                          */
/*THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS       */
/*"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT         */
/*LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS         */
/*FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE            */
/*COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,       */
/*INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,      */
/*BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;          */
/*LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER          */
/*CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT        */
/*LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN         */
/*ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE           */
/*POSSIBILITY OF SUCH DAMAGE.                                               */
/****************************************************************************/

using System.Diagnostics;
using System.Collections.Generic;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace GE.Visualisation
{
    class TextureEx : Texture
    {
        /// <summary>
        /// Flag : true if the sprites got the same size or else false.
        /// </summary>
        bool m_simpleSprite;

        /// <summary>
        /// Array to store the position and size.
        /// </summary>
        List<Rectangle> m_arraySprite;

        /// <summary>
        /// Array storing the name of the sprites
        /// </summary>
        List<string> m_arrayName;

        /// <summary>
        /// Represents the width of the sprites. Used if m_simpleSprite is true.
        /// </summary>
        int m_spriteWidth;

        /// <summary>
        /// Represents the height of the sprites. Used if m_simpleSprite is true.
        /// </summary>
        int m_spriteHeight;

        /// <summary>
        /// Represent the number of sprites per row in the texture. Used if m_simpleSprite is true.
        /// </summary>
        int m_nbOfSpritePerRow;

        /// <summary>
        /// Represent the total number of sprites in the texture. Used if m_simpleSprite is true.
        /// </summary>
        int m_totalNbOfSprite;

        /// <summary>
        /// Return the number of sprites in the texture
        /// </summary>
        public int SpriteCount { get { return m_arraySprite.Count; } }

        public Rectangle Sprite(int idSprite) { return m_arraySprite[idSprite];} 

        /// <summary>
        /// Constructor
        /// </summary>
        public TextureEx()
        {
            m_arraySprite = new List<Rectangle>();
            m_arrayName = new List<string>();
        }
     
        /// <summary>
        /// load a texture with sprites (they got the same size)
        /// </summary>
        /// <param name="content">The content manager to load the texture</param>
        /// <param name="assetName">The name of the texture</param>
        /// <param name="spriteWidth">Width of the sprites</param>
        /// <param name="spriteHeight">Height of the sprites</param>
        /// <param name="nbOfSpritePerRow">Number of sprites per row in the texture</param>
        /// <param name="totalNbOfSprite">Total number of sprites in the texture</param>
        /// <param name="arrayOfName">Name of the sprites. Use null if you don't want to use names.</param>
        /// <returns>True if succeeded or else false</returns>
        public bool loadTexture(ContentManager content, string assetName, int spriteWidth, int spriteHeight,
            int nbOfSpritePerRow, int totalNbOfSprite, string[] arrayOfName)
        {
            //load the texture
            m_simpleSprite = true;
            if(!base.loadTexture(content, assetName))
                return false;

            //save the rectangle
            m_spriteWidth = spriteWidth;
            m_spriteHeight = spriteHeight;
            m_nbOfSpritePerRow = nbOfSpritePerRow;
            m_totalNbOfSprite = totalNbOfSprite;

            if (arrayOfName == null)
                return true;

            foreach (string s in arrayOfName)
                m_arrayName.Add(s);

            return true;
        }

        /// <summary>
        /// load a texture with sprites (they got the same size)
        /// </summary>
        /// <param name="content">The content manager to load the texture</param>
        /// <param name="assetName">The name of the texture</param>
        /// <param name="spriteWidth">Width of the sprites</param>
        /// <param name="spriteHeight">Height of the sprites</param>
        /// <param name="nbOfSpritePerRow">Number of sprites per row in the texture</param>
        /// <param name="totalNbOfSprite">Total number of sprites in the texture</param>
        /// <param name="arrayOfName">Name of the sprites. Use null if you don't want to use names.</param>
        /// <returns>True if succeeded or else false</returns>
        public bool loadTexture(GraphicsDevice device, string assetName, int spriteWidth, int spriteHeight,
            int nbOfSpritePerRow, int totalNbOfSprite, string[] arrayOfName)
        {
            //load the texture
            m_simpleSprite = true;
            if (!base.loadTexture(device, assetName))
                return false;

            //save the rectangle
            m_spriteWidth = spriteWidth;
            m_spriteHeight = spriteHeight;
            m_nbOfSpritePerRow = nbOfSpritePerRow;
            m_totalNbOfSprite = totalNbOfSprite;

            if (arrayOfName == null)
                return true;

            foreach (string s in arrayOfName)
                m_arrayName.Add(s);

            return true;
        }

        /// <summary>
        /// Load a texture with sprites of different sizes. Use addSprite to add sprites in it.
        /// </summary>
        /// <param name="content">Content manager</param>
        /// <param name="assetName">The texture name</param>
        /// <returns>True if succeeded or else false.</returns>
        public bool loadTextureEx(ContentManager content, string assetName)
        {
            //load the texture
            m_simpleSprite = false;
            if (!base.loadTexture(content, assetName))
                return false;

            return true;
        }

        /// <summary>
        /// Load a texture with sprites of different sizes. Use addSprite to add sprites in it.
        /// </summary>
        /// <param name="content">Content manager</param>
        /// <param name="assetName">The texture name</param>
        /// <returns>True if succeeded or else false.</returns>
        public bool loadTextureEx(GraphicsDevice device, string assetName)
        {
            //load the texture
            m_simpleSprite = false;
            if (!base.loadTexture(device, assetName))
                return false;

            m_name = assetName;
            m_filename = assetName.Substring(assetName.LastIndexOf('\\') + 1, assetName.Length - assetName.LastIndexOf('\\') - 5);
            return true;
        }

        //add a sprite with a specific size
        /// <summary>
        /// Add a sprite with a specific size. The associated texture must have been loaded with loadTextureEx.
        /// </summary>
        /// <param name="sprite">A rectangle describing the sprite</param>
        /// <param name="name">sprite's name. Use null if not used.</param>
        /// <returns>The sprite id or -1 if failed</returns>
        public int addSprite(Rectangle sprite, string name)
        {
            if (m_simpleSprite)
                return -1;
            //int id = m_arrayName.FindIndex(new System.Predicate<string>());
            m_arraySprite.Add(sprite);

            if(name!= null)
                m_arrayName.Add(name);
            return m_arraySprite.Count - 1;
        }

        /// <summary>
        /// add a name to sprite. To use only with regulat sprite.
        /// </summary>
        /// <param name="spriteName">the name of the sprite</param>
        public void addSpriteName(string spriteName)
        {
            m_arrayName.Add(spriteName);
        }

        /// <summary>
        /// Display a sprite.
        /// </summary>
        /// <param name="sprite">SpriteBatch object</param>
        /// <param name="idSprite">index of the sprite</param>
        /// <param name="position">position to display</param>
        public void displaySprite(SpriteBatch sprite, int idSprite, Vector2 position)
        {
            Debug.Assert(idSprite >= 0);

            Rectangle src;
            Rectangle dest;

            //if the sprites got the same size
            if (m_simpleSprite)
            {
                Debug.Assert(idSprite < m_totalNbOfSprite);

                //destination and source rectangles
                src = new Rectangle((idSprite % m_nbOfSpritePerRow) * m_spriteWidth,
                    (idSprite / m_nbOfSpritePerRow) * m_spriteHeight, m_spriteWidth, m_spriteHeight);
                dest = new Rectangle((int)position.X, (int)position.Y, m_spriteWidth, m_spriteHeight);
            }
            else 
            {
                Debug.Assert(idSprite < m_arraySprite.Count);

                src = m_arraySprite[idSprite];
                dest = new Rectangle((int)position.X, (int)position.Y, src.Width, src.Height);
            }

            //draw
            sprite.Draw(m_texture, dest, src, Color.White, 0, Vector2.Zero, SpriteEffects.None, m_defaultDepth);
        }

        /// <summary>
        /// Display a sprite.
        /// </summary>
        /// <param name="sprite">SpriteBatch object</param>
        /// <param name="idSprite">index of the sprite</param>
        /// <param name="position">position to display</param>
        /// <param name="flip">set the flip to apply</param>
        public void displaySprite(SpriteBatch sprite, int idSprite, Vector2 position, SpriteEffects flip)
        {
            Debug.Assert(idSprite >= 0);

            Rectangle src;
            Rectangle dest;

            //if the sprites got the same size
            if (m_simpleSprite)
            {
                Debug.Assert(idSprite < m_totalNbOfSprite);

                //destination and source rectangles
                src = new Rectangle((idSprite % m_nbOfSpritePerRow) * m_spriteWidth,
                    (idSprite / m_nbOfSpritePerRow) * m_spriteHeight, m_spriteWidth, m_spriteHeight);
                dest = new Rectangle((int)position.X, (int)position.Y, m_spriteWidth, m_spriteHeight);
            }
            else
            {
                Debug.Assert(idSprite < m_arraySprite.Count);

                src = m_arraySprite[idSprite];
                dest = new Rectangle((int)position.X, (int)position.Y, src.Width, src.Height);
            }

            //draw
            sprite.Draw(m_texture, dest, src, Color.White, 0, Vector2.Zero, flip, m_defaultDepth);
        }

        public void displaySprite(SpriteBatch sprite, int idSprite, Vector2 position, Vector2 scale, SpriteEffects flip)
        {
            Debug.Assert(idSprite >= 0);

            Rectangle src;
            Rectangle dest;

            //if the sprites got the same size
            if (m_simpleSprite)
            {
                Debug.Assert(idSprite < m_totalNbOfSprite);

                //destination and source rectangles
                src = new Rectangle((idSprite % m_nbOfSpritePerRow) * m_spriteWidth,
                    (idSprite / m_nbOfSpritePerRow) * m_spriteHeight, m_spriteWidth, m_spriteHeight);
                dest = new Rectangle((int)position.X, (int)position.Y, m_spriteWidth, m_spriteHeight);
            }
            else
            {
                Debug.Assert(idSprite < m_arraySprite.Count);

                src = m_arraySprite[idSprite];
                dest = new Rectangle((int)position.X, (int)position.Y, src.Width, src.Height);
            }

            //draw
            //sprite.Draw(m_texture, dest, src, Color.White, 0, Vector2.Zero, flip, m_defaultDepth);
            sprite.Draw(m_texture, position, src, Color.White, 0, Vector2.Zero, scale, flip, m_defaultDepth);
        }

        /// <summary>
        /// Display a sprite with more options
        /// </summary>
        /// <param name="sprite">SpriteBatch object</param>
        /// <param name="idSprite">Sprite index</param>
        /// <param name="position">position to display</param>
        /// <param name="rotationCenter">rotation center of the sprite</param>
        /// <param name="orientation">orientation of the sprite (in radians)</param>
        /// <param name="scale">scale factor</param>
        public void displaySprite(SpriteBatch sprite, int idSprite, Vector2 position, Vector2 rotationCenter,
            float orientation, Vector2 scale)
        {
            Debug.Assert(idSprite >= 0);

            Rectangle src;
            //Rectangle dest;

            //if the sprites got the same size
            if (m_simpleSprite)
            {
                Debug.Assert(idSprite < m_totalNbOfSprite);

                //destination and source rectangles
                src = new Rectangle((idSprite % m_nbOfSpritePerRow) * m_spriteWidth,
                    (idSprite / m_nbOfSpritePerRow) * m_spriteHeight, m_spriteWidth, m_spriteHeight);
                //dest = new Rectangle((int)position.X, (int)position.Y, m_spriteWidth, m_spriteHeight);
            }
            else
            {
                Debug.Assert(idSprite < m_arraySprite.Count);

                src = m_arraySprite[idSprite];
                //dest = new Rectangle((int)position.X, (int)position.Y, src.Width, src.Height);
            }

            //draw with rotation
            sprite.Draw(m_texture, position, src, Color.White, orientation, rotationCenter, scale,
                SpriteEffects.None, m_defaultDepth);
        }

        /// <summary>
        /// Display a sprite with more options
        /// </summary>
        /// <param name="sprite">SpriteBatch object</param>
        /// <param name="idSprite">Sprite index</param>
        /// <param name="position">position to display</param>
        /// <param name="rotationCenter">rotation center of the sprite</param>
        /// <param name="orientation">orientation of the sprite (in radians)</param>
        /// <param name="scale">scale factor</param>
        /// <param name="depth">the depth of the sprite. 0 for front and 1 for back</param>
        public void displaySprite(SpriteBatch sprite, int idSprite, Vector2 position, Vector2 rotationCenter,
            float orientation, Vector2 scale, float depth)
        {
            Debug.Assert(idSprite >= 0);

            Rectangle src;
            //Rectangle dest;

            //if the sprites got the same size
            if (m_simpleSprite)
            {
                Debug.Assert(idSprite < m_totalNbOfSprite);

                //destination and source rectangles
                src = new Rectangle((idSprite % m_nbOfSpritePerRow) * m_spriteWidth,
                    (idSprite / m_nbOfSpritePerRow) * m_spriteHeight, m_spriteWidth, m_spriteHeight);
                //dest = new Rectangle((int)position.X, (int)position.Y, m_spriteWidth, m_spriteHeight);
            }
            else
            {
                Debug.Assert(idSprite < m_arraySprite.Count);

                src = m_arraySprite[idSprite];
                //dest = new Rectangle((int)position.X, (int)position.Y, src.Width, src.Height);
            }

            //draw with rotation
            sprite.Draw(m_texture, position, src, Color.White, orientation, rotationCenter, scale,
                SpriteEffects.None, depth);
        }

        /// <summary>
        /// Return the width of a specific sprite
        /// </summary>
        /// <param name="id">id of the sprite</param>
        /// <returns>The width</returns>
        public int SpriteWidth(int id)
        {
            if (m_simpleSprite)
                return m_spriteWidth;
            else
                return m_arraySprite[id].Width;
        }

        /// <summary>
        /// Return the height of a specific sprite
        /// </summary>
        /// <param name="id">id of the sprite</param>
        /// <returns>The height</returns>
        public int SpriteHeight(int id)
        {
            if (m_simpleSprite)
                return m_spriteHeight;
            else
                return m_arraySprite[id].Height;
        }

        /// <summary>
        /// get the id of a sprite from its name
        /// </summary>
        /// <param name="spriteName">sprite's name</param>
        /// <returns>the id if the sprite or -1 if it doesn't exists.</returns>
        public int getSpriteId(string spriteName)
        {
            for (int i = 0; i < m_arrayName.Count; i++)
            {
                if (m_arrayName[i] == spriteName)
                    return i;
            }

            return -1;
        }

        /// <summary>
        /// Return the name of a sprite
        /// </summary>
        /// <param name="idSprite"></param>
        /// <returns></returns>
        public string getSpriteName(int idSprite)
        {
            if (idSprite < 0 || idSprite >= m_arraySprite.Count) return "";
            return m_arrayName[idSprite];
        }

        public void setSpriteLeft(int idSprite, int left)
        {
            if (idSprite < 0 || idSprite >= m_arraySprite.Count) return;
            Rectangle r = m_arraySprite[idSprite];
            r.X = left;
            m_arraySprite[idSprite] = r;
        }

        public void setSpriteTop(int idSprite, int top)
        {
            if (idSprite < 0 || idSprite >= m_arraySprite.Count) return;
            Rectangle r = m_arraySprite[idSprite];
            r.Y = top;
            m_arraySprite[idSprite] = r;
        }

        public void setSpriteWidth(int idSprite, int width)
        {
            if (idSprite < 0 || idSprite >= m_arraySprite.Count) return;
            Rectangle r = m_arraySprite[idSprite];
            r.Width = width;
            m_arraySprite[idSprite] = r;
        }

        public void setSpriteHeight(int idSprite, int height)
        {
            if (idSprite < 0 || idSprite >= m_arraySprite.Count) return;
            Rectangle r = m_arraySprite[idSprite];
            r.Height = height;
            m_arraySprite[idSprite] = r;
        }

        public void setSpriteName(int idSprite, string name)
        {
            if (idSprite < 0 || idSprite >= m_arraySprite.Count) return;
            m_arrayName[idSprite] = name;
        }

        public void deleteSprite(int idSprite)
        {
            if (idSprite < 0 || idSprite >= m_arraySprite.Count) return;
            m_arrayName.RemoveAt(idSprite);
            m_arraySprite.RemoveAt(idSprite);
        }
    }
}
