//------------------------------------------------------------------------------
        // Function: WriteTileSets
        // Author: nholmes
        // Summary: writes all the tile sets to the xml document
        //------------------------------------------------------------------------------
        public bool WriteTileSets(TileSet[] tileSets)
        {
            XmlNode listNode;
            XmlNode childNode;
            XmlAttribute newAttribute;

            // if we haven't got a valid root node, return failure
            if (writeRoot == null) return false;

            // create a node to hold the list of tile sets
            listNode = writeLevelDOM.CreateElement("TileSets");

            // add an attribute to the list node to store the number of tile sets we are going to store
            newAttribute = writeLevelDOM.CreateAttribute("Count");
            newAttribute.Value = tileSets.Length.ToString();
            listNode.Attributes.Append(newAttribute);

            // create a node for every tile set that contains it's data
            for (int tileSet = 0; tileSet < tileSets.Length; tileSet++)
            {
                // create a node for this tile set
                childNode = writeLevelDOM.CreateElement("TileSet");

                // write the name of the tile map as an attribute of the node
                newAttribute = writeLevelDOM.CreateAttribute("Name");
                newAttribute.Value = tileSets[tileSet].Name;
                childNode.Attributes.Append(newAttribute);

                // write the index of this tile set in an attribute of the node
                newAttribute = writeLevelDOM.CreateAttribute("Number");
                newAttribute.Value = tileSet.ToString();
                childNode.Attributes.Append(newAttribute);

                // write the size of this tile set in an attribute of the node
                newAttribute = writeLevelDOM.CreateAttribute("TileSize");
                newAttribute.Value = tileSets[tileSet].TileSize.ToString();
                childNode.Attributes.Append(newAttribute);

                // append the tile set node to the list of tile sets
                listNode.AppendChild(childNode);
            }

            // append the list of tile sets to the document
            writeRoot.AppendChild(listNode);

            // report success!
            return true;
        }
        //------------------------------------------------------------------------------
        // Function: ReadTileSets
        // Author: nholmes
        // Summary: finds all the tile sets in the loaded level and re-creates them
        //------------------------------------------------------------------------------
        public bool ReadTileSets(ContentManager contentManager, string filePath, out TileSet[] tileSets)
        {
            // initialy set tileSets to null, in case there are any errors and we need to bail out early!
            tileSets = null;

            // if we haven't got a valid root node, return failure
            if (readRoot == null) return false;

            // retrieve number of TileSets
            int numTileSets = readRoot.SelectNodes("//TileSets/TileSet").Count;

            // create the tile set array
            tileSets = new TileSet[numTileSets];

            // loop through all of the tile sets and re-create them
            for (int tileSet = 0; tileSet < numTileSets; tileSet++)
            {
                // get the tile set that corresponds to the current index
                XmlNode tileSetData = readRoot.SelectSingleNode("//TileSets/TileSet[@Number=" + tileSet + "]");

                // check that the tile set was found and return failure if not
                if (tileSetData == null) return false;

                // read the tile set name
                XmlNode tileSetNameNode = tileSetData.Attributes.GetNamedItem("Name");

                // was the name data found ok? return failure if not!
                if (tileSetNameNode == null) return false;

                // read the tile set size
                XmlNode tileSetSizeNode = tileSetData.Attributes.GetNamedItem("TileSize");

                // was the size data found ok? return failure if not!
                if (tileSetSizeNode == null) return false;

                // create the tile set
                tileSets[tileSet] = new TileSet(contentManager, filePath, tileSetNameNode.InnerText, Convert.ToInt32(tileSetSizeNode.InnerText));
            }

            // return success!
            return true;
        }
        //------------------------------------------------------------------------------
        // Function: TileLayer
        // Author: nholmes
        // Summary: constructor to use when data is available - sets the map data to use,
        //          graphics to use, the size of the tiles and how the layer moves in
        //          relation to it's parent position
        //------------------------------------------------------------------------------
        public TileLayer(string layerName, TileSet tileSet, TileMap tileMap, TileLayerMode layerMode, TileLayer targetLayer, float displayScale, Vector2 positionScale, Vector2 positionOffset, Vector2 displaySize, Color tintColor)
        {
            // store the layer's name
            this.name = layerName;

            // store the name of the tile set and a reference to them
            tileSetName = tileSet.Name;
            this.tileSet = tileSet;

            // store the name of the tile map and a reference to them
            tileMapName = tileMap.Name;
            this.tileMap = tileMap;

            // store the layer mode
            this.mode = layerMode;

            // store the target layer
            target = targetLayer;

            // clear the updated status
            updated = false;

            // store the scale
            this.displayScale = displayScale;

            // store the position scale (this will be ignored if position mode is anything other than 'Scaled'
            this.positionScale = positionScale;

            // store the position offset (used by all targeted layer modes)
            this.positionOffset = positionOffset;

            // precalculate values used for displaying the tile layer
            SetDisplaySize(displaySize);

            // set the tint color
            this.tintColor = tintColor;
        }
        //------------------------------------------------------------------------------
        // Function: ReadTileLayers
        // Author: nholmes
        // Summary: finds all the tile layers in the loaded level and re-creates them
        //------------------------------------------------------------------------------
        public bool ReadTileLayers(Game game, out TileLayer[] tileLayers, out int masterTileLayer, TileSet[] tileSets, TileMap[] tileMaps)
        {
            // initialy set tileLayers to null, in case there are any errors and we need to bail out early!
            tileLayers = null;

            // set master tile layer to -1, in case there are any errors and we need to bail out early!
            masterTileLayer = -1;

            // if we haven't got a valid root node, return failure
            if (readRoot == null) return false;

            // find the tile layer node
            XmlNode tileLayerNode = readRoot.SelectSingleNode("//TileLayers");

            // find the master tile layer attribute
            XmlNode masterTileLayerNode = tileLayerNode.Attributes.GetNamedItem("MasterTileLayer");

            // was the master tile layer attribute found ok? return failure if not!
            if (masterTileLayerNode == null) return false;

            // store the master tile layer index
            masterTileLayer = Convert.ToInt32(masterTileLayerNode.InnerText);

            // find the number of tile layers attribute
            XmlNode numberOfTileLayersNode = tileLayerNode.Attributes.GetNamedItem("Count");

            // was the number of tile layers attribute found ok? return failure if not!
            if (numberOfTileLayersNode == null) return false;

            // store the number of tile layers
            int numTileLayers = Convert.ToInt32(numberOfTileLayersNode.InnerText);

            // create the tile layer array
            tileLayers = new TileLayer[numTileLayers];

            // create an array to hold the tile layer target names so we can process them once all layers are loaded
            string[] layerTargetNames = new string[numTileLayers];

            // loop through all of the tile maps and re-create them
            for (int tileLayer = 0; tileLayer < numTileLayers; tileLayer++)
            {
                // get the tile layer that corresponds to the current index
                XmlNode tileLayerData = readRoot.SelectSingleNode("//TileLayers/TileLayer[@Number=" + tileLayer + "]");

                // check that the tile layer was found and return failure if not
                if (tileLayerData == null) return false;

                // read the tile layer name
                XmlNode tileLayerNameNode = tileLayerData.Attributes.GetNamedItem("Name");

                // was the name data found ok? return failure if not!
                if (tileLayerNameNode == null) return false;

                // create the tile layer
                tileLayers[tileLayer] = new TileLayer(game, tileLayerNameNode.InnerText);

                // null the tile layer's tile set and tile map pointers in case there is an error and we have to bail out
                tileLayers[tileLayer].TileSet = null;
                tileLayers[tileLayer].TileMap = null;

                // find the tile set data
                XmlNode tileSetData = tileLayerData.Attributes.GetNamedItem("TileSet");

                // was the tile set data found ok? return failure if not!
                if (tileSetData == null) return false;

                // Find the correct tile set in the supplied list of tile sets
                for(int tileSet = 0; tileSet < tileSets.Length; tileSet++)
                {
                    if(tileSets[tileSet].Name == tileSetData.InnerText)
                    {
                        // found the correct tile set - store a reference to the tile set in the layer
                        tileLayers[tileLayer].TileSet = tileSets[tileSet];
                        break;
                    }
                }

                // check that we found a valid tile set - return failure if we didn't!
                if (tileLayers[tileLayer].TileSet == null) return false;

                // find the tile map data
                XmlNode tileMapData = tileLayerData.Attributes.GetNamedItem("TileMap");

                // was the tile map data found ok? return failure if not!
                if (tileMapData == null) return false;

                // Find the correct tile map in the supplied list of tile maps
                for (int tileMap = 0; tileMap < tileMaps.Length; tileMap++)
                {
                    if (tileMaps[tileMap].Name == tileMapData.InnerText)
                    {
                        // found the correct tile map - store a reference to the tile map in the layer
                        tileLayers[tileLayer].TileMap = tileMaps[tileMap];
                        break;
                    }
                }

                // check that we found a valid tile map - return failure if we didn't!
                if (tileLayers[tileLayer].TileMap == null) return false;

                // find the layer mode data
                XmlNode layerModeData = tileLayerData.Attributes.GetNamedItem("Mode");

                // was the layer mode data found ok? return failure if not!
                if (layerModeData == null) return false;

                // set the layer mode according to the data we extracted
                switch (layerModeData.InnerText)
                {
                    case "Static":
                        tileLayers[tileLayer].LayerMode = TileLayerMode.Static;
                        break;

                    case "Forced":
                        tileLayers[tileLayer].LayerMode = TileLayerMode.Forced;
                        break;

                    case "Follow":
                        tileLayers[tileLayer].LayerMode = TileLayerMode.Follow;
                        break;

                    case "Relative":
                        tileLayers[tileLayer].LayerMode = TileLayerMode.Relative;
                        break;

                    case "Scaled":
                        tileLayers[tileLayer].LayerMode = TileLayerMode.Scaled;
                        break;

                    default:

                        // something has gone wrong - return failure!
                        return false;
                }

                // find the layer target data
                XmlNode layerTargetData = tileLayerData.Attributes.GetNamedItem("Target");

                // was the layer target data found ok? return failure if not!
                if (layerTargetData == null) return false;

                // read and store the layer target name so we can fix these up once all layers are loaded
                layerTargetNames[tileLayer] = layerTargetData.InnerText;

                // find the tint color data
                XmlNode tintColorData = tileLayerData.Attributes.GetNamedItem("TintColor");

                // was the tint color data found ok? return failure if not!
                if (tintColorData == null) return false;

                // read and store the tint color
                string dataRef = tintColorData.InnerText;
                Color tintColor = new Color();

                // get the alpha value
                int nextComma = dataRef.IndexOf(',');
                tintColor.A = (byte)Convert.ToInt32(dataRef.Remove(nextComma));
                dataRef = dataRef.Remove(0, nextComma + 1);

                // get the red value
                nextComma = dataRef.IndexOf(',');
                tintColor.R = (byte)Convert.ToInt32(dataRef.Remove(nextComma));
                dataRef = dataRef.Remove(0, nextComma + 1);

                // get the green value
                nextComma = dataRef.IndexOf(',');
                tintColor.G = (byte)Convert.ToInt32(dataRef.Remove(nextComma));
                dataRef = dataRef.Remove(0, nextComma + 1);

                // get the blue value
                tintColor.B = (byte)Convert.ToInt32(dataRef);

                // store the tint color
                tileLayers[tileLayer].TintColor = tintColor;

                // find the position scale data
                XmlNode positionScaleData = tileLayerData.Attributes.GetNamedItem("PositionScale");

                // was the position scale data found ok? return failure if not!
                if (positionScaleData == null) return false;

                // read and store the position scale
                dataRef = positionScaleData.InnerText;

                // get and store the X value
                nextComma = dataRef.IndexOf(',');
                tileLayers[tileLayer].PositionScaleX = Convert.ToSingle(dataRef.Remove(nextComma));
                dataRef = dataRef.Remove(0, nextComma + 1);

                // get and store the Y value
                tileLayers[tileLayer].PositionScaleY = Convert.ToSingle(dataRef);

                // find the position scale data
                XmlNode positionOffsetData = tileLayerData.Attributes.GetNamedItem("PositionOffset");

                // was the position offset data found ok? return failure if not!
                if (positionOffsetData == null) return false;

                // read and store the position offset
                dataRef = positionOffsetData.InnerText;

                // get and store the X value
                nextComma = dataRef.IndexOf(',');
                tileLayers[tileLayer].PositionOffsetX = Convert.ToSingle(dataRef.Remove(nextComma));
                dataRef = dataRef.Remove(0, nextComma + 1);

                // get and store the Y value
                tileLayers[tileLayer].PositionOffsetY = Convert.ToSingle(dataRef);
            }

            // now all the layers are loaded and created we need to fix up the layer targets
            for (int tileLayer = 0; tileLayer < numTileLayers; tileLayer++)
            {
                if (layerTargetNames[tileLayer] == "None")
                    tileLayers[tileLayer].Target = null;
                else
                {
                    int searchLayer;

                    // search all of the tile layers for the layer this layer wishes to target
                    for (searchLayer = 0; searchLayer < numTileLayers; searchLayer++)
                    {
                        // check if this layer matches the name we are looking for
                        if (tileLayers[searchLayer].Name == layerTargetNames[tileLayer])
                        {
                            // found the match - store it and bail out
                            tileLayers[tileLayer].Target = tileLayers[searchLayer];
                            break;
                        }
                    }

                    // check to see if we failed to find a batch and bail out with failure if so
                    if (searchLayer == numTileLayers) return false;
                }
            }

            // return success
            return true;
        }
        //------------------------------------------------------------------------------
        // Function: TileLayer
        // Author: nholmes
        // Summary: constructor to use to create a blank tile layer
        //------------------------------------------------------------------------------
        public TileLayer(Game game, string layerName)
        {
            // get a handle to the display manager service
            displayManager = (DisplayManager)game.Services.GetService(typeof(DisplayManager));

            // store the layer's name
            name = layerName;

            // store the name of the tile set and a reference to them
            tileSetName = "";
            tileSet = null;

            // store the name of the tile map and a reference to them
            tileMapName = "";
            tileMap = null;

            // store the layer mode
            mode = TileLayerMode.Follow;

            // set the target layer to be undefined
            target = null;

            // default tint color is white
            tintColor = new Color(255, 255, 255, 255);

            // clear the updated status
            updated = false;

            // store the scale
            displayScale = 1.0f;

            // store the position scale (this will be ignored if position mode is anything other than 'Scaled'
            positionScale = new Vector2(1.0f, 1.0f);

            // store the position offset (used by all targeted layer modes)
            positionOffset = new Vector2(0.0f, 0.0f);
        }