
/****************************************************************************/
/*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;
using System.Diagnostics;
using System.Collections.Generic;
using System.Xml;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.GamerServices;

using GE.TimeClock;
using GE.Tools;

namespace GE.Visualisation
{
    class Visu
    {
        #region Variables

        /// <summary>
        /// singleton variable
        /// </summary>
        static Visu m_instance = new Visu();

        /// <summary>
        /// Content object to load assets
        /// </summary>
        ContentManager m_content;

        /// <summary>
        /// Graphic device
        /// </summary>
        GraphicsDevice m_device;

        /// <summary>
        /// Sprite object
        /// </summary>
        SpriteBatch m_sprite;
        
        /// <summary>
        /// list of textures
        /// </summary>
        List<Texture> m_arrayTexture;

        /// <summary>
        /// list of animations
        /// </summary>
        List<Animation> m_arrayAnimation;

        /// <summary>
        /// A texture of one white pixel
        /// </summary>
        Texture2D m_singlePixel;

        /// <summary>
        /// Flag that indicates if Visu crashed
        /// </summary>
        bool m_errorFlag;

        #endregion

        #region Singleton, Constructor & Init

        /// <summary>
        /// Get the singleton
        /// </summary>
        public static Visu Instance
        {
            get
            {
                return m_instance;
            }
        }
 
        /// <summary>
        /// Constructor
        /// </summary>
        public Visu()
        {
            //create the list
            m_arrayTexture = new List<Texture>();
            m_arrayAnimation = new List<Animation>();

            m_errorFlag = false;
        }

        /// <summary>
        /// Initialise the Visualisation component
        /// </summary>
        /// <param name="game">The Game object</param>
        /// <returns>True if succeeded, false if failed</returns>
        public bool init(Game game)
        {
            //initialise references
            m_content = game.Content;
            m_device = game.GraphicsDevice;

            //create a sprite batch
            m_sprite = new SpriteBatch(m_device);

            //create a single pixel texture
            //m_singlePixel = new Texture2D(m_device, 1, 1, 1, TextureUsage.None, SurfaceFormat.Color);
            m_singlePixel = new Texture2D(m_device, 1, 1, false, SurfaceFormat.Color);
            Color[] pixels = new Color[1];
            pixels[0] = Color.Black;
            m_singlePixel.SetData<Color>(pixels);

            return true;
        }

        #endregion

        #region Load and Create

        /// <summary>
        /// Load a texture from contents
        /// </summary>
        /// <param name="assetName">The name of the asset</param>
        /// <returns>True if succeeded, false if failed</returns>
        public int loadTexture(string assetName)
        {
            //check if Visu crashed
            if (m_errorFlag)
                return -1;

            //check if the texture is already loaded
            int res = isAlreadyLoaded(assetName);
            if(res != -1)
                return res;

            //create texture
            Texture newTexture = new Texture();

            //load texture
            if (!newTexture.loadTexture(m_content, assetName))
            {
                //log the error
                string msgError = "Error in loadTexture. \nCannot load the textures : "+assetName;  
                Logger.Instance.error(this, msgError);

                m_errorFlag = true;
                return -1;
            }

            //return id
            m_arrayTexture.Add(newTexture);
            return m_arrayTexture.Count - 1;
        }

        /// <summary>
        /// Load a texture from content and describe its sprites
        /// </summary>
        /// <param name="assetName">Name of the asset</param>
        /// <param name="nbOfSprites">Number of sprites containes in the textures</param>
        /// <param name="nbOfSpritePerRow">Number of sprites in a single row in the textures</param>
        /// <param name="spriteWidth">Width of a sprite</param>
        /// <param name="spriteHeight">Height of a sprite</param>
        /// <param name="names">Array of string containing names of sprites. Use null if unused.</param>
        /// <returns>The id of the texture or -1 if failed</returns>
        public int loadTexture(string assetName, int nbOfSprites, int nbOfSpritePerRow, int spriteWidth,
            int spriteHeight, string[] names)
        {
            //check if Visu crashed
            if (m_errorFlag)
                return -1;

            //check if the texture is already loaded
            int res = isAlreadyLoaded(assetName);
            if (res != -1)
                return res;

            //create the texture
            TextureEx newTexture = new TextureEx();

            //load
            if (!newTexture.loadTexture(m_content, assetName, spriteWidth, spriteHeight, nbOfSpritePerRow, nbOfSprites, names))
            {
                //log the error
                string msgError = "Error in loadTexture. \nCannot load the textures : " + assetName;
                Logger.Instance.error(this, msgError);

                m_errorFlag = true;
                return -1;
            }

            //return id
            m_arrayTexture.Add(newTexture);
            return m_arrayTexture.Count - 1;
        }

        /// <summary>
        /// Load a texture with sprite of different size. Add sprites with addSprite.
        /// </summary>
        /// <param name="assetName">texture name</param>
        /// <returns>the texture id or -1 if failed</returns>
        public int loadTextureEx(string assetName)
        {
            //check if Visu crashed
            if (m_errorFlag)
                return -1;

            int res = isAlreadyLoaded(assetName);
            if (res != -1)
                return res;

            TextureEx newTexture = new TextureEx();
            if (!newTexture.loadTextureEx(m_content, assetName))
            {
                //log the error
                string msgError = "Error in loadTextureEx. \nCannot load the textures : " + assetName;
                Logger.Instance.error(this, msgError);

                m_errorFlag = true;
                return -1;
            }

            m_arrayTexture.Add(newTexture);
            return m_arrayTexture.Count - 1;
        }

        /// <summary>
        /// Load a tileset using a xml file
        /// </summary>
        /// <param name="xmlScriptFile">xml filename</param>
        /// <returns>The index of the tileset. -1 for errors.</returns>
        public int loadTilset(string xmlScriptFile)
        {
            //load the xml file
            XmlDocument doc = new XmlDocument();
            try { doc.Load(m_content.RootDirectory+"\\"+xmlScriptFile); }
            catch
            {
                Logger.Instance.error(this, "Cannot load : " + m_content.RootDirectory + "\\" + xmlScriptFile);
                return -1;
            }

            //check if the xml is an AI xml
            if (doc.DocumentElement.Name != "Tileset")
            {
                Logger.Instance.error(this, "Cannot load : " + xmlScriptFile);
                return -1;
            }

            bool regularSprite = XmlHelper.getBoolFromAttribute(doc.DocumentElement.Attributes[0]);

            
            if (regularSprite)
                return loadRegularTileset(doc.DocumentElement);
            else
                return loadNonRegularTileset(doc.DocumentElement);

        }

        /// <summary>
        /// Add a sprite to a texture loaded with loadTextureEx
        /// </summary>
        /// <param name="idTexture">The texture id</param>
        /// <param name="name">Sprite's name. Use null if not used</param>
        /// <param name="sprite"></param>
        public int addSprite(int idTexture, Rectangle sprite, string name)
        {
            //check if Visu crashed
            if (m_errorFlag)
                return -1;

            TextureEx texture = (TextureEx)m_arrayTexture[idTexture];
            return texture.addSprite(sprite, name);
        }

        /// <summary>
        /// Define and store an animation
        /// </summary>
        /// <param name="indexTexture">Index of the texture that contains sprites for the animation</param>
        /// <param name="indexFirstSprite">Sprite index of the animation's first sprite</param>
        /// <param name="indexLastSprite">Sprite index of the animation's last sprite</param>
        /// <param name="offsetTime">Time to display each animation sprite</param>
        /// <returns>The index of the animation, -1 if failed</returns>
        public int createAnimation(string name, int indexTexture, int indexFirstSprite, int indexLastSprite, int offsetTime)
        {
            //check if Visu crashed
            if (m_errorFlag)
                return -1;

	        //assert
	        Debug.Assert(indexTexture>=0);
            Debug.Assert(indexFirstSprite >= 0);
            Debug.Assert(indexLastSprite >= indexFirstSprite);
            Debug.Assert(offsetTime >= 0);

	        //creation of the animation
	        Animation newAnimation = new Animation(name, indexTexture, offsetTime, false);

	        //add the index of the sprites
	        for(int i=indexFirstSprite; i<=indexLastSprite; i++)
		        newAnimation.addSprite(i);

	        //add the animation to the vector
	        m_arrayAnimation.Add(newAnimation);

	        return m_arrayAnimation.Count - 1;
        }

        /// <summary>
        /// Define and store an animation using offset vectors
        /// </summary>
        /// <param name="indexTexture">Index of the texture that contains sprites for the animation</param>
        /// <param name="offsetTime">Time to display each animation sprite</param>
        /// <returns>The index of the animation, -1 if failed</returns>
        public int createAnimation(string name, int indexTexture, int offsetTime)
        {
            //check if Visu crashed
            if (m_errorFlag)
                return -1;

            //assert
            Debug.Assert(indexTexture >= 0);
            Debug.Assert(offsetTime >= 0);

            //creation of the animation
            Animation newAnimation = new Animation(name, indexTexture, offsetTime, true);

            //add the animation to the vector
            m_arrayAnimation.Add(newAnimation);

            //return the animation's index
            return m_arrayAnimation.Count - 1;
        }

        /// <summary>
        /// Load animations from a xml file
        /// </summary>
        /// <param name="xmlScriptFile">xml filename</param>
        /// <returns>1 if everything is ok. -1 for errors.</returns>
        public int loadAnimations(string xmlScriptFile)
        {
            //load the xml file
            XmlDocument doc = new XmlDocument();
            try { doc.Load(m_content.RootDirectory + "\\" + xmlScriptFile); }
            catch
            {
                Logger.Instance.error(this, "Cannot load : " + m_content.RootDirectory + "\\" + xmlScriptFile);
                return -1;
            }

            //check if the xml is an animation xml
            if (doc.DocumentElement.Name != "Animations")
            {
                Logger.Instance.error(this, "Cannot load : " + xmlScriptFile);
                return -1;
            }

            //go throught each animation
            foreach (XmlNode animationNode in doc.DocumentElement.ChildNodes)
            {
                //load the tileset
                int textureID = loadTilset(animationNode.Attributes["tileset"].InnerText);
                if (textureID == -1) return -1;

                //load the timePerFrame
                int timePerFrame = XmlHelper.getIntFromAttribute(animationNode.Attributes["timePerFrame"]);

                //load the animation's name
                string name = animationNode.Attributes["name"].InnerText;

                //create the animation
                int animationID = createAnimation(name, textureID, timePerFrame);
                Animation newAnimation = getAnimation(animationID);

                //load the frames
                foreach (XmlNode node in animationNode.ChildNodes)
                {
                    string spriteName = XmlHelper.getStringFromAttribute(node.Attributes["spriteName"]);
                    int spriteID = Visu.Instance.getSpriteId(textureID, spriteName);
                    //int spriteID = XmlHelper.getIntFromAttribute(node.Attributes["spriteID"]);
                    Vector2 position = XmlHelper.getVector2FromNode(node);
                    newAnimation.addSprite(spriteID, position);
                }
            }
            return 1;
        }

        #endregion

        #region Render

        /// <summary>
        /// Pre render function. To call before to draw anything
        /// </summary>
        public void preRender(bool useDepth)
        {
            //clear screen
            m_device.Clear(Color.LightBlue);

            //initialise sprite batch
            //if (!useDepth)
            //    m_sprite.Begin();
            //else
            //    m_sprite.Begin(SpriteSortMode.BackToFront | SpriteSortMode.Immediate, BlendState.AlphaBlend);

            m_sprite.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.PointClamp, null, null);
        }

        /// <summary>
        /// Post render function to call after drawing anything
        /// </summary>
        public void postRender()
        {
            //end sprite
            m_sprite.End();
        }

        /// <summary>
        /// Displays some text.
        /// </summary>
        /// <param name="text">text</param>
        /// <param name="position">position</param>
        /// <param name="color">color</param>
        public void displayText(SpriteFont font, string text, Vector2 position, Color color)
        {
            m_sprite.DrawString(font, text, position, color);
        }

        /// <summary>
        /// Display the entire texture
        /// </summary>
        /// <param name="idTexture">Id of the texture</param>
        /// <param name="position">Position to render the texture (relative to the top left corner)</param>
        public void displayTexture(int idTexture, Vector2 position)
        {
            Debug.Assert(idTexture >= 0);
            Debug.Assert(idTexture < m_arrayTexture.Count);

            m_arrayTexture[idTexture].display(m_sprite, position);
        }

        /// <summary>
        /// Display several times the entire texture.
        /// To be used from particle systems only.
        /// </summary>
        /// <param name="idTexture">Id of the texture</param>
        /// <param name="xy">coordinates array</param>
        public void displayTexture(int idTexture, float[] xy, int[] ttl)
        {
            Debug.Assert(idTexture >= 0);
            Debug.Assert(idTexture < m_arrayTexture.Count);

            m_arrayTexture[idTexture].display(m_sprite, xy, ttl);
        }

        /// <summary>
        /// Display the entire texture
        /// </summary>
        /// <param name="idTexture">Id of the texture</param>
        /// <param name="position">Position to render the texture (relative to rotation center)</param>
        /// <param name="rotationCenter">Define the center of the rotation</param>
        /// <param name="orientation">The rotation (in radians)</param>
        public void displayTexture(int idTexture, Vector2 position, Vector2 rotationCenter, float orientation)
        {
            Debug.Assert(idTexture >= 0);
            Debug.Assert(idTexture < m_arrayTexture.Count);

            m_arrayTexture[idTexture].display(m_sprite, position, rotationCenter, orientation, Vector2.One);
        }

        /// <summary>
        /// Display the entire texture
        /// </summary>
        /// <param name="idTexture">Id of the texture</param>
        /// <param name="position">Position to render the texture (relative to rotation center)</param>
        /// <param name="rotationCenter">Define the center of the rotation</param>
        /// <param name="orientation">The rotation (in radians)</param>
        /// <param name="alpha">the alpha value for alpha blending</param>
        public void displayTexture(int idTexture, Vector2 position, Vector2 rotationCenter, float orientation,
            int alpha)
        {
            Debug.Assert(idTexture >= 0);
            Debug.Assert(idTexture < m_arrayTexture.Count);

            m_arrayTexture[idTexture].display(m_sprite, position, rotationCenter, orientation, Vector2.One, alpha);
        }

        /// <summary>
        /// Display the entire texture
        /// </summary>
        /// <param name="idTexture">Id of the texture</param>
        /// <param name="position">Position to render the texture (relative to the rotation center)</param>
        /// <param name="rotationCenter">Define the center of rotation</param>
        /// <param name="orientation">The rotation (in radians)</param>
        /// <param name="scale">Scale vector</param>
        public void displayTexture(int idTexture, Vector2 position, Vector2 rotationCenter, float orientation,
            Vector2 scale)
        {
            Debug.Assert(idTexture >= 0);
            Debug.Assert(idTexture < m_arrayTexture.Count);

            m_arrayTexture[idTexture].display(m_sprite, position, rotationCenter, orientation, scale);
        }

        /// <summary>
        /// Display the entire texture
        /// </summary>
        /// <param name="idTexture">Id of the texture</param>
        /// <param name="position">Position to render the texture (relative to the rotation center)</param>
        /// <param name="rotationCenter">Define the center of rotation</param>
        /// <param name="orientation">The rotation (in radians)</param>
        /// <param name="scale">Scale vector</param>
        /// <param name="depth">dept. 0 for front, 1 for back.</param>
        public void displayTexture(int idTexture, Vector2 position, Vector2 rotationCenter, float orientation,
            Vector2 scale, float depth)
        {
            Debug.Assert(idTexture >= 0);
            Debug.Assert(idTexture < m_arrayTexture.Count);

            m_arrayTexture[idTexture].display(m_sprite, position, rotationCenter, orientation, scale, depth);
        }

        /// <summary>
        /// Display a sprite from a texture
        /// </summary>
        /// <param name="idTexture">Id of the texture</param>
        /// <param name="idSprite">Id of the sprite</param>
        /// <param name="position">Position of the sprite (relative to the top left corner of the sprite)</param>
        public void displaySprite(int idTexture, int idSprite, Vector2 position)
        {
            Debug.Assert(idTexture >= 0 && idTexture < m_arrayTexture.Count);
            Debug.Assert(idSprite >= 0);

            TextureEx t = (TextureEx)m_arrayTexture[idTexture];
            t.displaySprite(m_sprite, idSprite, position);
        }

        /// <summary>
        /// Display a sprite from a texture
        /// </summary>
        /// <param name="idTexture">Id of the texture</param>
        /// <param name="idSprite">Id of the sprite</param>
        /// <param name="position">Position of the sprite (relative to the top left corner of the sprite)</param>
        /// <param name="flip">set the flip to apply</param>
        public void displaySprite(int idTexture, int idSprite, Vector2 position, SpriteEffects flip)
        {
            Debug.Assert(idTexture >= 0 && idTexture < m_arrayTexture.Count);
            Debug.Assert(idSprite >= 0);

            TextureEx t = (TextureEx)m_arrayTexture[idTexture];
            t.displaySprite(m_sprite, idSprite, position, flip);
        }

        public void displaySprite(int idTexture, int idSprite, Vector2 position, SpriteEffects flip, Color tint)
        {
            Debug.Assert(idTexture >= 0 && idTexture < m_arrayTexture.Count);
            Debug.Assert(idSprite >= 0);

            TextureEx t = (TextureEx)m_arrayTexture[idTexture];
            t.displaySprite(m_sprite, idSprite, position, flip, tint);
        }

        /// <summary>
        /// Display a sprite from a texture
        /// </summary>
        /// <param name="idTexture">Id of the texture</param>
        /// <param name="idSprite">Id of the sprite</param>
        /// <param name="position">Position of the sprite (relative to the center of rotation)</param>
        /// <param name="rotationCenter">Define the center of rotation</param>
        /// <param name="orientation">Rotation angle (in radians)</param>
        public void displaySprite(int idTexture, int idSprite, Vector2 position, Vector2 rotationCenter,
            float orientation)
        {
            Debug.Assert(idTexture >= 0 && idTexture < m_arrayTexture.Count);
            Debug.Assert(idSprite >= 0);

            TextureEx t = (TextureEx)m_arrayTexture[idTexture];
            t.displaySprite(m_sprite, idSprite, position, rotationCenter, orientation, Vector2.One);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="idTexture">Id of the texture</param>
        /// <param name="idSprite">Id of the sprite</param>
        /// <param name="position">Position of the sprite (relative to the center of rotation)</param>
        /// <param name="rotationCenter">Define the center of rotation</param>
        /// <param name="orientation">Rotation angle (in radians)</param>
        /// <param name="scale">Scale vector</param>
        public void displaySprite(int idTexture, int idSprite, Vector2 position, Vector2 rotationCenter,
            float orientation, Vector2 scale)
        {
            Debug.Assert(idTexture >= 0 && idTexture < m_arrayTexture.Count);
            Debug.Assert(idSprite >= 0);

            TextureEx t = (TextureEx)m_arrayTexture[idTexture];
            t.displaySprite(m_sprite, idSprite, position, rotationCenter, orientation, scale);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="idTexture">Id of the texture</param>
        /// <param name="idSprite">Id of the sprite</param>
        /// <param name="position">Position of the sprite (relative to the center of rotation)</param>
        /// <param name="rotationCenter">Define the center of rotation</param>
        /// <param name="orientation">Rotation angle (in radians)</param>
        /// <param name="scale">Scale vector</param>
        /// <param name="depth">depth. 0 for front and 1 for back.</param>
        public void displaySprite(int idTexture, int idSprite, Vector2 position, Vector2 rotationCenter,
            float orientation, Vector2 scale, float depth)
        {
            Debug.Assert(idTexture >= 0 && idTexture < m_arrayTexture.Count);
            Debug.Assert(idSprite >= 0);

            TextureEx t = (TextureEx)m_arrayTexture[idTexture];
            t.displaySprite(m_sprite, idSprite, position, rotationCenter, orientation, scale, depth);
        }

        /// <summary>
        /// Draw a polygon of any size
        /// </summary>
        /// <param name="pointPosition">an array of vector.</param>
        public void displayPolygon(Vector2[] pointPosition)
        {
            int numberOfPoint = pointPosition.GetLength(0);

            if (numberOfPoint < 2)//can be change into an assert
                return;

            for (int i = 0; i < numberOfPoint; i++)
            {
                Vector2 vector1 = (Vector2)pointPosition[i];
                Vector2 vector2 = (Vector2)pointPosition[(i+1)%numberOfPoint];

                // calculate the distance between the two vectors
                float distance = Vector2.Distance(vector1, vector2);

                // calculate the angle between the two vectors
                float angle = (float)Math.Atan2((double)(vector2.Y - vector1.Y),
                    (double)(vector2.X - vector1.X));

                // stretch the pixel between the two vectors
                m_sprite.Draw(m_singlePixel,
                    vector1,
                    null,
                    Color.White,
                    angle,
                    Vector2.Zero,
                    new Vector2(distance, 1),
                    SpriteEffects.None,
                    0);
            }
        }

        /// <summary>
        /// Draw a circle
        /// </summary>
        /// <param name="center">the center </param>
        /// <param name="radius">the radius</param>
        public void displayCircle(Vector2 center, float radius, int precision)
        {

            float sides = precision;

            float max = MathHelper.TwoPi;
            float step = max / (float)sides;

            for (float theta = 0; theta < max; theta += step)
            {
                Vector2[] vectors = new Vector2[2];
                vectors[0] = new Vector2(radius * (float)Math.Cos(theta)+center.X,
                    radius * (float)Math.Sin(theta)+center.Y);

                float nextStep = (theta + step) % max;

                vectors[1] = new Vector2(radius * (float)Math.Cos(nextStep)+center.X,
                    radius * (float)Math.Sin(nextStep)+center.Y);

                displayPolygon(vectors);
                
            }
        }


        /// <summary>
        /// Display an animation
        /// </summary>
        /// <param name="indexAnimation">Animation's id</param>
        /// <param name="position">Position of the animation (relative to the top left corner of the sprites)</param>
        /// <param name="indexArrayCurrentSprite">The index of the current sprite</param>
        /// <param name="timeLastSprite">The time the current sprite started to be displayed</param>
        /// <returns>True if the animation is over or else false</returns>
        public bool displayAnimation(int idAnimation, Vector2 position, ref int idFrame, ref int timeLastSprite)
        {

            int idTexture = -1;
            int idSprite = -1;

            //compute the current sprite to display
            bool animationOver = computeCurrentFrameAnimation(idAnimation, ref idTexture, ref idSprite, ref position, 
                ref idFrame, ref timeLastSprite, SpriteEffects.None);

            //display the sprite
            displaySprite(idTexture, idSprite, position);

            return animationOver;
        }

        /// <summary>
        /// Display an animation
        /// </summary>
        /// <param name="indexAnimation">Animation's id</param>
        /// <param name="position">Position of the animation (relative to the top left corner of the sprites)</param>
        /// <param name="indexArrayCurrentSprite">The index of the current sprite</param>
        /// <param name="timeLastSprite">The time the current sprite started to be displayed</param>
        /// <param name="flip">Set the flip effect</param>
        /// <returns>True if the animation is over or else false</returns>
        public bool displayAnimation(int idAnimation, Vector2 position, ref int idFrame,
            ref int timeLastSprite, SpriteEffects flip)
        {
            int idTexture = -1;
            int idSprite = -1;

            //compute the current sprite to display
            bool animationOver = computeCurrentFrameAnimation(idAnimation, ref idTexture, ref idSprite, ref position, ref idFrame,
                ref timeLastSprite, flip);

            //display the sprite
            displaySprite(idTexture, idSprite, position, flip);

            return animationOver;
        }

        /// <summary>
        /// Display an animation
        /// </summary>
        /// <param name="idAnimation">animation id</param>
        /// <param name="position">Position in the screen</param>
        /// <param name="idFrame">Current frame id</param>
        /// <param name="timeLastFrame">Current time</param>
        /// <param name="flip">Sprite effect : flip horizontaly or vertically or none</param>
        /// <param name="tint">Color tint</param>
        /// <returns></returns>
        public bool displayAnimation(int idAnimation, Vector2 position, ref int idFrame, ref int timeLastFrame, SpriteEffects flip,
            Color tint)
        {
            int idTexture = -1;
            int idSprite = -1;

            //compute the current sprite to display
            bool animationOver = computeCurrentFrameAnimation(idAnimation, ref idTexture, ref idSprite, ref position, ref idFrame,
                ref timeLastFrame, flip);

            //display the sprite
            displaySprite(idTexture, idSprite, position, flip, tint);

            return animationOver;
        }

        /// <summary>
        /// Display an animation
        /// </summary>
        /// <param name="indexAnimation">Animation's id</param>
        /// <param name="position">Position of the animation (relative to the rotation center)</param>
        /// <param name="rotationCenter">The center for the rotation</param>
        /// <param name="orientation">Rotation angle (in radians)</param>
        /// <param name="indexArrayCurrentSprite">The index of the current sprite</param>
        /// <param name="timeLastSprite">The time the current sprite started to be displayed</param>
        /// <returns>True if the animation is over or else false</returns>
        public bool displayAnimation(int idAnimation, Vector2 position, Vector2 rotationCenter, float orientation,
            ref int idFrame, ref int timeLastSprite)
        {
            int idTexture = -1;
            int idSprite = -1;

            //compute the current sprite to display
            bool animationOver = computeCurrentFrameAnimation(idAnimation, ref idTexture, ref idSprite, ref position, ref idFrame,
                ref timeLastSprite, SpriteEffects.None);

            //display the sprite
            displaySprite(idTexture, idSprite, position, rotationCenter, orientation);      

            return animationOver;
        }

        /// <summary>
        /// Display an animation
        /// </summary>
        /// <param name="indexAnimation">Animation's id</param>
        /// <param name="position">Position of the animation (relative to the rotation center)</param>
        /// <param name="rotationCenter">The center for the rotation</param>
        /// <param name="orientation">Rotation angle (in radians)</param>
        /// <param name="scale">Scale vector</param>
        /// <param name="indexArrayCurrentSprite">The index of the current sprite</param>
        /// <param name="timeLastSprite">The time the current sprite started to be displayed</param>
        /// <returns>True if the animation is over or else false</returns>
        public bool displayAnimation(int idAnimation, Vector2 position, Vector2 rotationCenter, float orientation,
           Vector2 scale, ref int idFrame, ref int timeLastSprite)
        {
            int idTexture = -1;
            int idSprite = -1;

            //compute the current sprite to display
            bool animationOver = computeCurrentFrameAnimation(idAnimation, ref idTexture, ref idSprite, ref position, ref idFrame,
                ref timeLastSprite, SpriteEffects.None);

            //display the sprite
            displaySprite(idTexture, idSprite, position, rotationCenter, orientation, scale);
            

            return animationOver;
        }

        /// <summary>
        /// Display an animation upside down
        /// </summary>
        /// <param name="indexAnimation">Animation's id</param>
        /// <param name="position">Position of the animation (relative to the top left corner of the sprites)</param>
        /// <param name="indexArrayCurrentSprite">The index of the current sprite</param>
        /// <param name="timeLastSprite">The time the current sprite started to be displayed</param>
        /// <param name="flip">Set the flip effect</param>
        /// <returns>True if the animation is over or else false</returns>
        public bool displayReverseAnimation(int indexAnimation, Vector2 position, ref int indexFrame,
            ref int timeLastSprite, SpriteEffects flip)
        {
            int idTexture = -1;
            int idSprite = -1;

            //compute the current sprite to display
            bool animationOver = computeCurrentFrameReverseAnimation(indexAnimation, ref idTexture, ref idSprite, ref position, 
                ref indexFrame, ref timeLastSprite);

            //display the sprite
            displaySprite(idTexture, idSprite, position, flip);

            return animationOver;
        }
        #endregion 

        #region Tools

        /// <summary>
        /// Compute the current sprite to be displayed for an animation
        /// </summary>
        /// <param name="indexAnimation">Index of the animation to compute</param>
        /// <param name="idTexture">Texture's index of the animation</param>
        /// <param name="idSprite">Index of the current sprite to be displayed</param>
        /// <param name="position">Position of the current sprite</param>
        /// <param name="indexFrame">Index of the current frame to be displayed</param>
        /// <param name="timeLastSprite">Elapsed time since the sprite is displayed</param>
        /// <returns>True if the animation is over. False if the animation is not over.</returns>
        private bool computeCurrentFrameAnimation(int indexAnimation, ref int idTexture, ref int idSprite,
            ref Vector2 position, ref int indexFrame, ref int timeLastSprite, SpriteEffects flip)
        {
            //assert
            Debug.Assert(indexAnimation >= 0);

            idTexture = m_arrayAnimation[indexAnimation].indexTexture;
            int frameCount = m_arrayAnimation[indexAnimation].FrameCount;

            //get the id of the current sprite
            if (indexFrame != -1)
                idSprite = m_arrayAnimation[indexAnimation].getIndexSprite(indexFrame);
            else
                idSprite = -1;

            //if it's the beginning of the animation, we go back to the first frame
            if (idSprite < 0 || timeLastSprite == -1)
            {
                indexFrame = 0;
                idSprite = m_arrayAnimation[indexAnimation].idFirstSprite;
                timeLastSprite = Clock.instance.millisecs;
            }

            //if a certain amount of time has passed, we gotta change the frame
            if (Clock.instance.millisecs >= m_arrayAnimation[indexAnimation].offsetTime + timeLastSprite)
            {

                //if it's not the last frame
                if (indexFrame != frameCount-1)
                {
                    indexFrame++;
                    idSprite = m_arrayAnimation[indexAnimation].getIndexSprite(indexFrame);
                    timeLastSprite = Clock.instance.millisecs;
                }
                else //if it's the first frame
                {
                    timeLastSprite = -1;
                }

            }

            //calculate the position using offset if needed
            Vector2 offset = m_arrayAnimation[indexAnimation].getOffset(indexFrame);
            int idFirstSprite = getAnimation(indexAnimation).getIndexSprite(0);
            switch (flip)
            {
                case SpriteEffects.None:
                    position += offset;
                    break;

                case SpriteEffects.FlipHorizontally:
                    position.Y += offset.Y;
                    //position.X = position.X - getSpriteWidth(idTexture, idSprite) + getSpriteWidth(idTexture, idFirstSprite) - offset.X + m_arrayAnimation[indexAnimation].getOffset(0).X;

                    //Majic number 34 (length of the reference frame)
                    position.X = position.X - getSpriteWidth(idTexture, idSprite) + 34 - offset.X;// + m_arrayAnimation[indexAnimation].getOffset(0).X;
                    break;
                
                default:
                    throw new NotImplementedException();
                //case SpriteEffects.FlipVertically:
                //    position.X += offset.Y;
                //    position.Y = position.Y - getSpriteHeight(idTexture, idSprite) + getSpriteHeight(idTexture, idFirstSprite) - offset.Y + m_arrayAnimation[indexAnimation].getOffset(0).Y;
                //    break;
            }


            //if it's the last frame, animation is over	
            if (indexFrame == frameCount-1 && timeLastSprite == -1)
                return true;

            return false;
        }

        /// <summary>
        /// Compute the current sprite to be displayed for a reverse animation
        /// </summary>
        /// <param name="indexAnimation">Index of the animation to compute</param>
        /// <param name="idTexture">Texture's index of the animation</param>
        /// <param name="idSprite">Index of the current sprite to be displayed</param>
        /// <param name="position">Position of the current sprite</param>
        /// <param name="idFrame">Index of the current frame to be displayed</param>
        /// <param name="timeLastSprite">Elapsed time since the sprite is displayed</param>
        /// <returns>True if the animation is over. False if the animation is not over.</returns>
        private bool computeCurrentFrameReverseAnimation(int indexAnimation, ref int idTexture, ref int idSprite, 
            ref Vector2 position, ref int idFrame, ref int timeLastSprite)
        {
            //assert
            Debug.Assert(indexAnimation >= 0);

            idTexture = m_arrayAnimation[indexAnimation].indexTexture;
            int frameCount = m_arrayAnimation[indexAnimation].FrameCount;
            int frameCountMinusOne = frameCount - 1;

            //get the id of the current sprite
            if (idFrame != -1)
                idSprite = m_arrayAnimation[indexAnimation].getIndexSprite(idFrame);
                //idSprite = m_arrayAnimation[indexAnimation].getIndexSprite(frameCountMinusOne - idFrame);
            else
                idSprite = -1;

            //if it's the beginning of the animation, we go back to the last frame
            if (idSprite < 0 || timeLastSprite == -1)
            {
                idFrame = frameCountMinusOne;
                idSprite = m_arrayAnimation[indexAnimation].idLastSprite;
                timeLastSprite = Clock.instance.millisecs;
            }

            //if a certain amount of time has passed, we gotta change the frame
            if (Clock.instance.millisecs >= m_arrayAnimation[indexAnimation].offsetTime + timeLastSprite)
            {

                //if it's not the first frame
                if (idFrame != 0)
                {
                    idFrame--;
                    //idSprite = m_arrayAnimation[indexAnimation].getIndexSprite(frameCountMinusOne - idFrame);
                    idSprite = m_arrayAnimation[indexAnimation].getIndexSprite(idFrame);
                    timeLastSprite = Clock.instance.millisecs;
                }
                else //if it's the first frame
                {
                    timeLastSprite = -1;
                }

            }

            //calculate the position using offset if needed
            if (m_arrayAnimation[indexAnimation].useOffset)
                position += m_arrayAnimation[indexAnimation].getOffset(idFrame);
                //position += m_arrayAnimation[indexAnimation].getOffset(frameCountMinusOne - idFrame);

            //if it's the first frame, animation is over	
            if (idFrame == 0 && timeLastSprite == -1)
                return true;

            return false;
        }

        /// <summary>
        /// Check if an asset is already loaded
        /// </summary>
        /// <param name="assetName">The name of the asset</param>
        /// <returns>The id of the asset if it's loaded, -1 if it's not.</returns>
        private int isAlreadyLoaded(string assetName)
        {
            for (int i = 0; i < m_arrayTexture.Count; i++)
                if (m_arrayTexture[i].Name == assetName)
                    return i;

            return -1;
        }

        /// <summary>
        /// Load a tileset with same size sprites
        /// </summary>
        /// <param name="rootNode">the root node of the xml file containing the tileset description</param>
        /// <returns>the texture id</returns>
        private int loadRegularTileset(XmlNode rootNode)
        {
            //get the child nodes
            XmlNodeList nodeList = rootNode.ChildNodes;

            string texturePath = "";
            int nbOfSprites = 0;
            int nbOfSpritesPerRow = 0;
            int spriteWidth = 0;
            int spriteHeight = 0;
            string[] names = null;

            //get the infos
            foreach (XmlNode node in nodeList)
            {
                if (node.Name == "texturePath")
                    texturePath = XmlHelper.getStringFromNode(node);
                else if (node.Name == "textureInfo")
                {
                    nbOfSprites = XmlHelper.getIntFromAttribute(node.Attributes["nbOfSprites"]);
                    nbOfSpritesPerRow = XmlHelper.getIntFromAttribute(node.Attributes["nbOfSpritesPerRow"]);
                    spriteWidth = XmlHelper.getIntFromAttribute(node.Attributes["spriteWidth"]);
                    spriteHeight = XmlHelper.getIntFromAttribute(node.Attributes["spriteHeight"]);
                }
                else if (node.Name == "spriteNames")
                    names = XmlHelper.getWordsFromNode(node);
            }

            return loadTexture(m_content.RootDirectory + texturePath, nbOfSprites, nbOfSpritesPerRow,
                spriteWidth, spriteHeight, names);
        }

        /// <summary>
        /// Load a tilset with non regular sprite (different size)
        /// </summary>
        /// <param name="rootNode">the root node of the xml file describing the tileset</param>
        /// <returns>the texture id</returns>
        private int loadNonRegularTileset(XmlNode rootNode)
        {
            XmlNodeList nodeList = rootNode.ChildNodes;

            int textureId = 0;

            foreach (XmlNode node in nodeList)
            {
                if (node.Name == "texturePath")
                {
                    string path = XmlHelper.getStringFromNode(node);
                    //textureId = isAlreadyLoaded(m_content.RootDirectory + "\\" + path);
                    textureId = isAlreadyLoaded(path);
                    if (textureId != -1)
                        return textureId;

                    //textureId = loadTextureEx(m_content.RootDirectory + "\\" + path);
                    textureId = loadTextureEx(path);
                    if (textureId == -1)
                        return textureId;
                }
                else if (node.Name == "spriteInfo")
                {
                    string name = "";
                    Rectangle rect = new Rectangle();

                    name = XmlHelper.getStringFromAttribute(node.Attributes["name"]);
                    rect.X = XmlHelper.getIntFromAttribute(node.Attributes["x"]);
                    rect.Y = XmlHelper.getIntFromAttribute(node.Attributes["y"]);
                    rect.Width = XmlHelper.getIntFromAttribute(node.Attributes["width"]);
                    rect.Height = XmlHelper.getIntFromAttribute(node.Attributes["height"]);

                    addSprite(textureId, rect, name);
                }
            }

            return textureId;
        }

        #endregion

        #region Gettors

        /// <summary>
        /// Return the width of a texture
        /// </summary>
        /// <param name="idTexture">Texture id</param>
        /// <returns>texture width</returns>
        public int getTextureWidth(int idTexture)
        {
            //check if Visu crashed
            if (m_errorFlag)
                return 0;
            return m_arrayTexture[idTexture].Width; 
        }

        /// <summary>
        /// Return the height of a texture
        /// </summary>
        /// <param name="idTexture">Texture id</param>
        /// <returns>texture height</returns>
        public int getTextureHeight(int idTexture) 
        {
            //check if Visu crashed
            if (m_errorFlag)
                return 0;
            return m_arrayTexture[idTexture].Height;
        }

        /// <summary>
        /// Return the width of a sprite
        /// </summary>
        /// <param name="idTexture">Texture id</param>
        /// <param name="idSprite">Sprite id</param>
        /// <returns>Sprite width</returns>
        public int getSpriteWidth(int idTexture, int idSprite)
        {
            //check if Visu crashed
            if (m_errorFlag)
                return 0;
            return ((TextureEx)m_arrayTexture[idTexture]).SpriteWidth(idSprite); 
        }

        /// <summary>
        /// Return the height of a sprite
        /// </summary>
        /// <param name="idTexture">Texture id</param>
        /// <param name="idSprite">Sprite id</param>
        /// <returns>Sprite height</returns>
        public int getSpriteHeight(int idTexture, int idSprite)
        {
            //check if Visu crashed
            if (m_errorFlag)
                return 0;
            return ((TextureEx)m_arrayTexture[idTexture]).SpriteHeight(idSprite);
        }

        /// <summary>
        /// return the height of the sprite from an animation
        /// </summary>
        /// <param name="idAnim">animation id</param>
        /// <returns>height in pixel</returns>
        public int getSpriteHeightFromAnim(int idAnim)
        {
            //check if Visu crashed
            if (m_errorFlag)
                return 0;

            int idTexture = m_arrayAnimation[idAnim].indexTexture;
            return getSpriteHeight(idTexture, 0);
        }

        /// <summary>
        /// return the width of a sprite from an animation
        /// </summary>
        /// <param name="idAnim">animation id</param>
        /// <returns>width in pixel</returns>
        public int getSpriteWidthFromAnim(int idAnim)
        {
            //check if Visu crashed
            if (m_errorFlag)
                return 0;

            int idTexture = m_arrayAnimation[idAnim].indexTexture;
            return getSpriteWidth(idTexture, 0);
        }

        /// <summary>
        /// return the id of a sprite from its name
        /// </summary>
        /// <param name="idTexture">texture id</param>
        /// <param name="spriteName">sprite's name</param>
        /// <returns>the sprite's id or -1 of it the name doesn't exists.</returns>
        public int getSpriteId(int idTexture, string spriteName)
        {
            //check if Visu crashed
            if (m_errorFlag)
                return -1;
            int res = ((TextureEx)m_arrayTexture[idTexture]).getSpriteId(spriteName);
            if (res == -1)
                Logger.Instance.error(this, "The sprite : " + spriteName + " doesn't exists");

            return res;
        }

        /// <summary>
        /// Return the id of a sprite
        /// </summary>
        /// <param name="spriteName">Name of the sprite</param>
        /// <returns></returns>
        //public int getSpriteId(string spriteName)
        //{
        //    //check if Visu crashed
        //    if (m_errorFlag)
        //        return -1;

        //    foreach (TextureEx t in m_arrayTexture)
        //    {
        //        int res = t.getSpriteId(spriteName);
        //        if (res != -1) return res;
        //    }

        //    return -1;
        //}

        /// <summary>
        /// Return an animation object
        /// </summary>
        /// <param name="idAnimation">Animation's index</param>
        /// <returns>An animation object or null if an error occurs</returns>
        public Animation getAnimation(int idAnimation)
        {
            //check if Visu crashed
            if (m_errorFlag)
                return null;

            if (idAnimation < 0 || idAnimation >= m_arrayAnimation.Count) return null;
            return m_arrayAnimation[idAnimation];
        }

        /// <summary>
        /// Get the id of an animation.
        /// </summary>
        /// <param name="animationName">The name of the animation</param>
        /// <returns>A valid ID for an animation. -1 for an error.</returns>
        public int getAnimationID(string animationName)
        {
            //check if Visu crashed
            if (m_errorFlag)
                return -1;

            for (int i = 0; i < m_arrayAnimation.Count; i++)
            {
                if (m_arrayAnimation[i].Name == animationName) return i;
            }

            return -1;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Return a flag to true if Visu crashed or false if everything is fine.
        /// </summary>
        public bool Error { get { return m_errorFlag; } }

        /// <summary>
        /// Return the width of the screen
        /// </summary>
        public int ScreenWidth { get { return m_device.Viewport.Width; } }

        /// <summary>
        /// Return the height of the screen
        /// </summary>
        public int ScreenHeight { get { return m_device.Viewport.Height; } }

        #endregion
    }
}
