
/****************************************************************************/
/*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.Windows.Forms;
using System.Xml;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

using GE.Visualisation;

namespace GFXEditor
{
    public partial class wndSpritesheet : Form
    {
        /// <summary>
        /// Represent the modifications available
        /// </summary>
        enum eMode
        {
            eModeNone,
            eModeAddSprite,
            eModeResizeSpriteTop,
            eModeResizeSpriteBottom,
            eModeResizeSpriteLeft,
            eModeResizeSpriteRight
        }

        /// <summary>
        /// Represent the different color the rules can have
        /// </summary>
        enum eRulesColor
        {
            eColorBlack,
            eColorWhite
        }

        #region "Variables"

        /// <summary>
        /// The rules' color
        /// </summary>
        eRulesColor _rulesColor;

        /// <summary>
        /// XNA Graphic device used to draw
        /// </summary>
        GraphicsDevice _device;

        /// <summary>
        /// Parameter used by the device
        /// </summary>
        PresentationParameters _param;

        /// <summary>
        /// Sprite batch used to draw
        /// </summary>
        SpriteBatch _spriteBatch;

        /// <summary>
        /// Visuaisation object
        /// </summary>
        Visu _visualisation;

        /// <summary>
        /// Id of the texture used as spritesheet
        /// </summary>
        int _idTexture;

        /// <summary>
        /// Texture Ex object of the current texture
        /// </summary>
        TextureEx _textureEx;

        /// <summary>
        /// Scrolling offset of the texture
        /// </summary>
        Vector2 _textureScrollOffset;

        /// <summary>
        /// Absolute position of the texture
        /// </summary>
        Vector2 _textureRealPosition;

        /// <summary>
        /// Flag set when the first point of a new sprite has been added.
        /// </summary>
        bool _bSpriteFirstPointAdded;

        /// <summary>
        /// Flag indicates if the rules have to be displayed
        /// </summary>
        bool _bDisplayRules;

        /// <summary>
        /// Zoom currently used
        /// </summary>
        int _zoom;

        /// <summary>
        /// upper left et bottom right points of a new sprites
        /// </summary>
        Vector2[] _newSprite;

        /// <summary>
        /// id of the current selected sprite
        /// </summary>
        int _idSpriteSelected;

        /// <summary>
        /// User mode defining what action the user can do
        /// </summary>
        eMode _userMode;

        /// <summary>
        /// xml filename
        /// </summary>
        string _xmlFilename;

        /// <summary>
        /// texture name
        /// </summary>
        string _textureName;

        #endregion

        #region "Properties"

        /// <summary>
        /// Return the xml filename
        /// </summary>
        public string XmlScriptName { get { return _xmlFilename; } set { _xmlFilename = value; Text = "Spritesheet " + value; } }

        #endregion

        #region "Constructors"

        /// <summary>
        /// Constructor for a new spritesheet
        /// </summary>
        public wndSpritesheet()
        {
            InitializeComponent();

            //variables initialisation
            _idTexture = -1;
            _textureScrollOffset = new Vector2(0, 0);
            _textureRealPosition = new Vector2(0, 0);
            _zoom = 1;
            _textureEx = null;
            _bDisplayRules = false;
            _newSprite = new Vector2[2];
            _bSpriteFirstPointAdded = false;
            _idSpriteSelected = -1;
            tstbSpriteName.Text = "No Sprite Selected";
            _userMode = eMode.eModeNone;
            XmlScriptName = "";
            _textureName = "";
            tscbRulesColor.SelectedIndex = 0;
            _rulesColor = eRulesColor.eColorBlack;

            //create the presentation parameters
            _param = new PresentationParameters();
            _param.DeviceWindowHandle = panel.Handle;
            _param.IsFullScreen = false;  

            //create a new device.
            _device = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, GraphicsProfile.HiDef, _param);
            _spriteBatch = new SpriteBatch(_device);

            //create the visualisation component
            _visualisation = new Visu();
            _visualisation.init(_device);
            
            //initialise the scroll bars
            vScrollBar.Minimum = 0;
            vScrollBar.Maximum = 0;
            hScrollBar.Minimum = 0;
            hScrollBar.Maximum = 0;

            //DoubleBuffered = true;
            this.SetStyle(System.Windows.Forms.ControlStyles.OptimizedDoubleBuffer, true);
        }

        /// <summary>
        /// Constructor for an existing spritesheet
        /// </summary>
        /// <param name="xmlScriptFilename">filename of the spritesheet</param>
        public wndSpritesheet(string xmlScriptFilename) : this()
        {
            //load the spritesheet
            _idTexture = _visualisation.loadTileset(xmlScriptFilename);
            if (_idTexture == -1) return;
        
            _textureEx = _visualisation.getTextureEx(_idTexture);
            XmlScriptName = xmlScriptFilename;
            _textureName = _textureEx.Name;
            int id = _textureName.LastIndexOf('\\');
            _textureName = _textureName.Substring(id + 1, _textureName.Length - id - 5);

        }

        #endregion

        #region "Event"

        /// <summary>
        /// Change the color of the rules
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tscbRulesColor_SelectedIndexChanged(object sender, System.EventArgs e)
        {
            switch (tscbRulesColor.SelectedIndex)
            {
                case 0:
                    _rulesColor = eRulesColor.eColorBlack;
                    break;

                case 1:
                    _rulesColor = eRulesColor.eColorWhite;
                    break;
            }
        }

        /// <summary>
        /// Resize the window
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void wndSpritesheet_Resize(object sender, System.EventArgs e)
        {
            if (panel.Height == 0 || panel.Width == 0) return;

            //resize the device
            _param.BackBufferHeight = panel.Height;
            _param.BackBufferWidth = panel.Width;
            _device.Reset(_param);

            //calculate the max value of the vertical scroll  bar
            computeVScrollMax();
            computeHScrollMax();

            render();
        }

        /// <summary>
        /// Click on the button "Open Texture"
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tsbOpenTexture_Click(object sender, System.EventArgs e)
        {
            openTexture();
        }

        /// <summary>
        /// Change in the vertical scrollbar
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void vScrollBar_ValueChanged(object sender, System.EventArgs e)
        {
            _textureScrollOffset.Y = vScrollBar.Value;
            _textureRealPosition = -_textureScrollOffset;
            render();

        }

        /// <summary>
        /// Zoom in
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tsbZoomIn_Click(object sender, System.EventArgs e)
        {
            zoomIn();
        }

        /// <summary>
        /// Zoom out
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tsbZoomOut_Click(object sender, System.EventArgs e)
        {
            zoomOut();
        }

        /// <summary>
        /// Repaint the panel
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void panel_Paint(object sender, PaintEventArgs e)
        {
            render();

        }

        /// <summary>
        /// Change in the horizontal scrollbar
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void hScrollBar_ValueChanged(object sender, System.EventArgs e)
        {
            _textureScrollOffset.X = hScrollBar.Value;
            _textureRealPosition = -_textureScrollOffset;
            render();
        }

        /// <summary>
        /// Add a sprite in the spritesheet
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tsbNewSprite_Click(object sender, System.EventArgs e)
        {
            //_bModeAddSprite = true;
            _userMode = eMode.eModeAddSprite;
            _newSprite[0].X = _newSprite[0].Y = _newSprite[1].X = _newSprite[1].Y = 0;
        }

        /// <summary>
        /// Handle the mouse in the panel
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void panel_MouseMove(object sender, MouseEventArgs e)
        {

            //reset the cursor to the default cursor
            Cursor = Cursors.Default;

            //get the position in texture coordinate
            System.Drawing.Point mousePosInPanel = panel.PointToClient(MousePosition);
            Vector2 vec2Mouse = getPanelPositionToTexturePosition(new Vector2(mousePosInPanel.X, mousePosInPanel.Y));

            Rectangle rec;
            switch (_userMode)
            {
                //add sprite mode : set the position of the lower right point
                case eMode.eModeAddSprite:
                    if (!_bSpriteFirstPointAdded) break;
                    _newSprite[1].X = (int)vec2Mouse.X;
                    _newSprite[1].Y = (int)vec2Mouse.Y;
                    break;

                //move the top bound of a sprite
                case eMode.eModeResizeSpriteTop:
                    //get the rectangle
                    rec = _textureEx.Sprite(_idSpriteSelected);

                    //calculate the new top and height
                    _textureEx.setSpriteTop(_idSpriteSelected, (int)vec2Mouse.Y);
                    _textureEx.setSpriteHeight(_idSpriteSelected, (int)(rec.Height - vec2Mouse.Y + rec.Y));
                    Cursor = Cursors.HSplit;
                    break;

                //move the bottom bound of a sprite
                case eMode.eModeResizeSpriteBottom:
                    //get the rectangle
                    rec = _textureEx.Sprite(_idSpriteSelected);

                    //calculate the new height
                    _textureEx.setSpriteHeight(_idSpriteSelected, (int)(vec2Mouse.Y - rec.Y));
                    Cursor = Cursors.HSplit;
                    break;

                //move the left bound of a sprite
                case eMode.eModeResizeSpriteLeft:
                    //get the rectangle
                    rec = _textureEx.Sprite(_idSpriteSelected);

                    //calculate the new left and width
                    _textureEx.setSpriteLeft(_idSpriteSelected, (int)vec2Mouse.X);
                    _textureEx.setSpriteWidth(_idSpriteSelected, (int)(rec.Width - vec2Mouse.X + rec.Left));
                    Cursor = Cursors.HSplit;
                    break;

                //move the right bound of a sprite
                case eMode.eModeResizeSpriteRight:
                    //get the rectangle
                    rec = _textureEx.Sprite(_idSpriteSelected);

                    //calculate the new height
                    _textureEx.setSpriteWidth(_idSpriteSelected, (int)(vec2Mouse.X - rec.X));
                    Cursor = Cursors.HSplit;
                    break;

                default:
                    //if a sprite is selected, change the cursor
                    if (_idSpriteSelected != -1)
                    {
                        //check if the mouse is on a line of the sprite
                        rec = _textureEx.Sprite(_idSpriteSelected);

                        //check top
                        if (vec2Mouse.Y == rec.Y && vec2Mouse.X > rec.X && vec2Mouse.X < rec.X + rec.Width)
                            Cursor = Cursors.HSplit;

                        //check left
                        if (vec2Mouse.X == rec.X && vec2Mouse.Y > rec.Y && vec2Mouse.Y < rec.Y + rec.Height)
                            Cursor = Cursors.VSplit;

                        //check right
                        if (vec2Mouse.X == rec.X + rec.Width && vec2Mouse.Y > rec.Y && vec2Mouse.Y < rec.Y + rec.Height)
                            Cursor = Cursors.VSplit;

                        //check bottom
                        if (vec2Mouse.Y == rec.Y + rec.Height && vec2Mouse.X > rec.X && vec2Mouse.X < rec.X + rec.Width)
                            Cursor = Cursors.HSplit;
                    }
                    break;

            }

            //refresh the panel
            render();
        }

        /// <summary>
        /// Display or hide the rules
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tsbRules_Click(object sender, System.EventArgs e)
        {
            _bDisplayRules = !_bDisplayRules;
        }

        /// <summary>
        /// Handle mouse down event in the panel
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void panel_MouseDown(object sender, MouseEventArgs e)
        {
            //adding a new sprite
            if (_userMode == eMode.eModeAddSprite)
            {

                System.Drawing.Point mousePosInPanel = panel.PointToClient(MousePosition);
                Vector2 mousePosVec2 = new Vector2(mousePosInPanel.X, mousePosInPanel.Y);
                mousePosVec2 += _textureScrollOffset;

                mousePosVec2 /= _zoom;

                _newSprite[0].X = _newSprite[1].X = (int)mousePosVec2.X;
                _newSprite[0].Y = _newSprite[1].Y = (int)mousePosVec2.Y;
                _bSpriteFirstPointAdded = true;
            }

            //if a sprite is selected
            if (_idSpriteSelected != -1)
            {
                //get the position in texture coordinate
                System.Drawing.Point mousePosInPanel = panel.PointToClient(MousePosition);
                Vector2 vec2Mouse = getPanelPositionToTexturePosition(new Vector2(mousePosInPanel.X, mousePosInPanel.Y));

                //check if the mouse is on a line of the sprite
                Rectangle rec = _textureEx.Sprite(_idSpriteSelected);

                //check top
                if (vec2Mouse.Y == rec.Y && vec2Mouse.X > rec.X && vec2Mouse.X < rec.X + rec.Width)
                    _userMode = eMode.eModeResizeSpriteTop;// _bModeChangingRectangleSpriteTop = true;

                //check left
                if (vec2Mouse.X == rec.X && vec2Mouse.Y > rec.Y && vec2Mouse.Y < rec.Y + rec.Height)
                    _userMode = eMode.eModeResizeSpriteLeft; //_bModeChangingRectangleSpriteLeft = true;

                //check right
                if (vec2Mouse.X == rec.X + rec.Width && vec2Mouse.Y > rec.Y && vec2Mouse.Y < rec.Y + rec.Height)
                    _userMode = eMode.eModeResizeSpriteRight; //_bModeChangingRectangleSpriteRight = true;

                //check bottom
                if (vec2Mouse.Y == rec.Y + rec.Height && vec2Mouse.X > rec.X && vec2Mouse.X < rec.X + rec.Width)
                    _userMode = eMode.eModeResizeSpriteBottom; //_bModeChangingRectangleSpriteBottom = true;
            }

        }

        /// <summary>
        /// Handle the mouse up event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void panel_MouseUp(object sender, MouseEventArgs e)
        {
            //if add sprite mode, turn it off
            if (_userMode == eMode.eModeAddSprite)
            {
                _userMode = eMode.eModeNone;//_bModeAddSprite = false;
                _bSpriteFirstPointAdded = false;
                tsbNewSprite.Checked = false;
                _idSpriteSelected = _textureEx.addSprite(new Rectangle((int)_newSprite[0].X, (int)_newSprite[0].Y, (int)(_newSprite[1].X - _newSprite[0].X), (int)(_newSprite[1].Y - _newSprite[0].Y)), _textureEx.SpriteCount.ToString());
                tstbSpriteName.Text = _textureEx.SpriteCount.ToString();
            }

            if (_userMode == eMode.eModeResizeSpriteBottom) _userMode = eMode.eModeNone;
            if (_userMode == eMode.eModeResizeSpriteTop) _userMode = eMode.eModeNone;
            if (_userMode == eMode.eModeResizeSpriteLeft) _userMode = eMode.eModeNone;
            if (_userMode == eMode.eModeResizeSpriteRight) _userMode = eMode.eModeNone;
        }

        /// <summary>
        /// Handle key down
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void wndSpritesheet_KeyDown(object sender, KeyEventArgs e)
        {
            //turn off the add sprite mode
            if (e.KeyData == Keys.Escape && _userMode == eMode.eModeAddSprite)
            {
                _userMode = eMode.eModeNone;
                _bSpriteFirstPointAdded = false;
                tsbNewSprite.Checked = false;
            }

            //delete the current sprite
            if (e.KeyData == Keys.Delete && _idSpriteSelected != -1)
            {
                _textureEx.deleteSprite(_idSpriteSelected);
                _idSpriteSelected = -1;
                tstbSpriteName.Text = "No Sprite Selected";
            }

            render();
        }

        /// <summary>
        /// Handle the mouse movement on the panel
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void panel_MouseClick(object sender, MouseEventArgs e)
        {
            if (_userMode == eMode.eModeAddSprite) return;
            if (_idTexture == -1) return;

            //calculate the point in the texture
            Vector2 texturePos = getPanelPositionToTexturePosition(new Vector2(e.X, e.Y));

            //go throught all the sprites and check if the point is inside a sprite
            for (int i = 0; i < _textureEx.SpriteCount; i++)
            {
                Rectangle rec = _textureEx.Sprite(i);
                if (rec.X <= texturePos.X && rec.X + rec.Width >= texturePos.X && rec.Y <= texturePos.Y && rec.Y + rec.Height >= texturePos.Y)
                {
                    _idSpriteSelected = i;
                    tstbSpriteName.Text = _textureEx.getSpriteName(_idSpriteSelected);
                    return;
                }
            }
            _idSpriteSelected = -1;
            tstbSpriteName.Text = "No Sprite Selected";
        }

        /// <summary>
        /// Set the name of a sprite when the text box value has changed
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tstbSpriteName_TextChanged(object sender, System.EventArgs e)
        {
            if (_idSpriteSelected == -1) return;
            _textureEx.setSpriteName(_idSpriteSelected, tstbSpriteName.Text);
        }

        /// <summary>
        /// Click on the save button
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tsbSave_Click(object sender, System.EventArgs e)
        {
            if (XmlScriptName == "")
                saveEx();
            else
                save();
        }

        /// <summary>
        /// Click on the save as button
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tsbSaveAs_Click(object sender, System.EventArgs e)
        {
            saveEx();
        }

        #endregion

        #region "Slots"

        /// <summary>
        /// open a file dialog and save the spritesheet
        /// </summary>
        private void saveEx()
        {
            //create a save file dialog
            SaveFileDialog sfd = new SaveFileDialog();
            sfd.Title = "Save spritesheet";
            sfd.Filter = "Xml files (*.xml)|*.xml";

            //open the save file dialog
            DialogResult res = sfd.ShowDialog();
            if (res != DialogResult.OK) return;

            //save the spritesheet
            XmlScriptName = sfd.FileName;
            save();
        }

        /// <summary>
        /// Sauvegarde le spritesheet dans un fichier xml ayant pour non _xmlFilename
        /// </summary>
        private void save()
        {
            //create the xml file
            XmlTextWriter textWriter = new XmlTextWriter(_xmlFilename, null);
            textWriter.WriteStartDocument();

            //write root
            textWriter.WriteStartElement("Tileset");
            textWriter.WriteStartAttribute("regularSprite");
            textWriter.WriteString("false");
            textWriter.WriteEndAttribute();

            //write the texture name node
            textWriter.WriteStartElement("texturePath");
            textWriter.WriteString(_textureName);
            textWriter.WriteEndElement();

            //go through all the sprites to save them
            for (int i = 0; i < _textureEx.SpriteCount; i++)
            {
                //write the node
                textWriter.WriteStartElement("spriteInfo");

                //write the name
                textWriter.WriteStartAttribute("name");
                textWriter.WriteString(_textureEx.getSpriteName(i));
                textWriter.WriteEndAttribute();

                //get the rectangle 
                Rectangle rec = _textureEx.Sprite(i);

                //write x
                textWriter.WriteStartAttribute("x");
                textWriter.WriteString(rec.X.ToString());
                textWriter.WriteEndAttribute();

                //write y
                textWriter.WriteStartAttribute("y");
                textWriter.WriteString(rec.Y.ToString());
                textWriter.WriteEndAttribute();

                //write width
                textWriter.WriteStartAttribute("width");
                textWriter.WriteString(rec.Width.ToString());
                textWriter.WriteEndAttribute();

                //write height
                textWriter.WriteStartAttribute("height");
                textWriter.WriteString(rec.Height.ToString());
                textWriter.WriteEndAttribute();

                textWriter.WriteEndElement();
            }

            //root end
            textWriter.WriteEndElement();

            //save the xml file
            textWriter.WriteEndDocument();
            textWriter.Close();
            MessageBox.Show("Spritesheet saved");
        }

        /// <summary>
        /// Open a openfiledialog and load a texture as a spritesheet
        /// </summary>
        /// <returns></returns>
        private bool openTexture()
        {
            //open the openfiledialog
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Title = "Select texture";
            ofd.Filter = "PNG Files (*.png)|*.png";
            DialogResult res = ofd.ShowDialog();

            //if no file selected
            if (res != System.Windows.Forms.DialogResult.OK)
                return false;

            //load the texture
            _idTexture = _visualisation.loadTextureEx(ofd.FileName);
            if (_idTexture == -1)
                return false;

            _textureEx = _visualisation.getTextureEx(_idTexture);
            _textureName = _textureEx.Name;
            int id = _textureName.LastIndexOf('\\');
            _textureName = _textureName.Substring(id + 1, _textureName.Length - id - 5);

            computeVScrollMax();
            computeHScrollMax();

            render();
            return true;
        }

        /// <summary>
        /// Increase the zoom (x2)
        /// </summary>
        private void zoomIn()
        {
            if (_zoom == 8) return;
            _zoom *= 2;
            computeVScrollMax();
            computeHScrollMax();
            render();
        }

        /// <summary>
        /// Decrease the zoom (/2)
        /// </summary>
        private void zoomOut()
        {
            if (_zoom == 1) return;
            _zoom = (int)(_zoom*0.5f);
            computeVScrollMax();
            computeHScrollMax();
            render();
        }

        /// <summary>
        /// Compute the new max value for the vertical scroll bar
        /// </summary>
        private void computeVScrollMax()
        {
            if (_idTexture == -1) return;
            int textureHeight = _visualisation.getTextureHeight(_idTexture) * _zoom;
            if (textureHeight > panel.Height)
                vScrollBar.Maximum = textureHeight - panel.Height;
            else
                vScrollBar.Maximum = 0;
        }

        /// <summary>
        /// Compute the new max value for the horizontal scroll bar
        /// </summary>
        private void computeHScrollMax()
        {
            if (_idTexture == -1) return;
            int textureWidth = _visualisation.getTextureWidth(_idTexture) * _zoom;
            if (textureWidth > panel.Width)
                hScrollBar.Maximum = textureWidth - panel.Width;
            else
                hScrollBar.Maximum = 0;
        }

        #endregion

        #region "Tools"

        /// <summary>
        /// render in the xna viewport
        /// </summary>
        private void render()
        {
            _visualisation.preRender( false);

            //render the texture
            if (_idTexture != -1)
            {
                _visualisation.displayTexture( _idTexture, _textureRealPosition, new Vector2(0, 0), 0, new Vector2(_zoom, _zoom));
                //render all the sprites
                for (int idSprite = 0; idSprite < _textureEx.SpriteCount; idSprite++)
                {
                    Rectangle rec = _textureEx.Sprite(idSprite);
                    rec.X *= _zoom;
                    rec.Y *= _zoom;
                    rec.X -= (int)_textureScrollOffset.X;
                    rec.Y -= (int)_textureScrollOffset.Y;
                    
                    rec.Width *= _zoom;
                    rec.Height *= _zoom;
                    if(_rulesColor == eRulesColor.eColorBlack)
                        _visualisation.displayBlackRectangle( rec);
                    else
                        _visualisation.displayWhiteRectangle( rec);
                }
            }

            //render the rules
            if (_bDisplayRules)
            {
                //calculate the screen position into the panel
                System.Drawing.Point mousePosInPanel = panel.PointToClient(MousePosition);

                //convert the position into a pixel position so the lines are always along pixels.
                Vector2 vec2Mouse = new Vector2(mousePosInPanel.X, mousePosInPanel.Y);
                vec2Mouse = getPanelPositionToPixelPosition(vec2Mouse);
                
                //render the horizontal line
                Vector2[] hLine = new Vector2[2];
                hLine[0].X = 0;
                hLine[1].X = panel.Width;
                hLine[0].Y = hLine[1].Y = vec2Mouse.Y;

                //render the vertical line
                Vector2[] vLine = new Vector2[2];
                vLine[0].Y = 0;
                vLine[1].Y = panel.Height;
                vLine[0].X = vLine[1].X = vec2Mouse.X;
                if (_rulesColor == eRulesColor.eColorBlack)
                {
                    _visualisation.displayBlackPolygon( hLine);
                    _visualisation.displayBlackPolygon( vLine);
                }
                else
                {
                    _visualisation.displayWhitePolygon( hLine);
                    _visualisation.displayWhitePolygon( vLine);
                }
            }

            //render a new sprite
            if (_bSpriteFirstPointAdded)
            {
                Vector2[] spriteRect = new Vector2[4];
                spriteRect[0] = _newSprite[0];
                spriteRect[1] = _newSprite[0];
                spriteRect[1].X = _newSprite[1].X;
                spriteRect[2] = _newSprite[1];
                spriteRect[3] = _newSprite[1];
                spriteRect[3].X = _newSprite[0].X;

                for (int i = 0; i < 4; i++)
                {
                    spriteRect[i] *= _zoom;
                    spriteRect[i] -= _textureScrollOffset;
                }
                if (_rulesColor == eRulesColor.eColorBlack)
                    _visualisation.displayBlackPolygon( spriteRect);
                else
                    _visualisation.displayWhitePolygon( spriteRect);
            }

            _visualisation.postRender();

            _device.Present();
        }

        /// <summary>
        /// Take a point in panel coordinate and return the position of the closest pixel in panel coordinate.
        /// </summary>
        /// <param name="v"></param>
        /// <returns></returns>
        private Vector2 getPanelPositionToPixelPosition(Vector2 v)
        {
            Vector2 res = v + _textureScrollOffset;
            res /= _zoom;

            res.X = (int)res.X;
            res.Y = (int)res.Y;

            res *= _zoom;
            res -= _textureScrollOffset;
            return res;
        }

        /// <summary>
        /// Take a point in the panel coordinate and return it into texture coordinate.
        /// </summary>
        /// <param name="v"></param>
        /// <returns></returns>
        private Vector2 getPanelPositionToTexturePosition(Vector2 v)
        {
            Vector2 res = v;
            res += _textureScrollOffset;
            res /= _zoom;
            res.X = (int)res.X;
            res.Y = (int)res.Y;
            return res;
        }

        #endregion

    }
}
