using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Primitives3D;

namespace FredriksdalAssault2
{
    /// <summary>
    /// This is the third map for Fredriksdal Assault, the fortrest.
    /// </summary>
    public class Fortrest : IMap
    {
        #region Instance variables

        // Lists of resources
        List<CubePrimitive> cubeFortrestList;
        List<CubePrimitive> cubeCrossWallList;

        #endregion

        #region Constructors

        /// <summary>
        /// Create a new valley map.
        /// </summary>
        /// <param name="content"></param>
        /// <param name="graphicsDevice"></param>
        public Fortrest(ContentManager content, GraphicsDevice graphicsDevice)
            : base (content, graphicsDevice, Vector3.Zero)
        {

            // Set the number of barrels
            this.numberOfBarrels = 5;

            // Create new lists (arrays)
            this.buildings = new List<Building>(100);
            this.numberOfBuildings = 3;
            this.items = new Item[numberOfBarrels];
            this.grass = new List<Vegetation>(400);

            // Load content
            LoadContent();
        }

        /// <summary>
        /// A helper method to load the graphical content.
        /// </summary>
        private void LoadContent()
        {

            // Load models.
            this.mapModel = content.Load<Model>("Maps/Fortrest/fortrestHeightMap");
            this.sky = content.Load<Sky>("Maps/CommonResources/sky");
            this.water = new Water(content, this.graphicsDevice, new Vector3(0, -560, 0));
            this.barrelModel = content.Load<Model>("Models/Barrels/Barrel");
            this.treeModel = content.Load<Model>("Models/Trees/Bleech_Bald_01");
            this.grassModel = content.Load<Model>("Models/Grass/grass_v3");

            // The terrain processor attached a HeightMapInfo to the terrain model's
            // Tag. We'll save that to a member variable now, and use it to
            // calculate the terrain's heights later.
            this.heightMapInfo = mapModel.Tag as HeightMapInfo;

            if (this.heightMapInfo == null)
            {
                string message = "The terrain model did not have a HeightMapInfo " +
                    "object attached. Are you sure you are using the " +
                    "TerrainProcessor?";
                throw new InvalidOperationException(message);
            }

            // Create the vegetation map.
            this.vegetationMap = content.Load<Texture2D>("Maps/Fortrest/fortrestVegetationMap");

            // Load the height data array with data.
            this.LoadVegetationHeights();

            // Load HLSL effect files.
            this.graphicEffect = content.Load<Effect>("Effects/TerrainEffect");

            // Load textures.
            this.sandTexture = content.Load<Texture2D>("Textures/yellow-sand");
            this.grassTexture = content.Load<Texture2D>("Textures/dry-grass");
            this.roadTexture = content.Load<Texture2D>("Textures/dirtRoad");
            this.rockTexture = content.Load<Texture2D>("Textures/white-rock");
            this.floorTexture = content.Load<Texture2D>("Models/House/cementFloor");
            this.wallTexture = content.Load<Texture2D>("Textures/stone-wall");
            this.roofTexture = content.Load<Texture2D>("Models/House/wall");

            // Load the minimap
            this.minimap = content.Load<Texture2D>("Maps/Fortrest/fortrestMinimap");

            // Add a fortrest building.
            buildings.Add(new FortrestBuilding(new Vector3(-400, -310, 3800), new Vector3(0,0,0), 1f));

            // Add cross walls
            float height = 0;
            this.heightMapInfo.GetHeight(new Vector3(-3400, 0, -800), out height);
            buildings.Add(new CrossWalls(new Vector3(-3400, height - 5, -800), new Vector3(0, 0, 0), 1f));
            this.heightMapInfo.GetHeight(new Vector3(3100, 0, -500), out height);
            buildings.Add(new CrossWalls(new Vector3(3100, height - 5, -500), new Vector3(0, 0, 0), 1f));

            // Add vegetation
            this.AddVegetation();

            // Add five barrels if the game mode is Assault.
            if (Program.gameMode == 0)
            {
                items[0] = new Barrel(new Vector3(-250, -260, 3750), new Vector3(0, 0, 0), 35f);
                items[1] = new Barrel(new Vector3(-150, -260, 3750), new Vector3(0, 0, 0), 35f);
                items[2] = new Barrel(new Vector3(-250, -260, 3850), new Vector3(0, 0, 0), 35f);
                items[3] = new Barrel(new Vector3(-150, -260, 3850), new Vector3(0, 0, 0), 35f);
                items[4] = new Barrel(new Vector3(-400, -60, 3800), new Vector3(0, 0, 0), 35f);
            }

            // Load the cubeFortrestList with cubes for a fortrest.
            this.cubeFortrestList = FortrestBuilding.CreateCubes(this.graphicsDevice);

            // Load cross walls
            this.cubeCrossWallList = CrossWalls.CreateCubes(this.graphicsDevice);

        } // End of the LoadContent method.

        #endregion

        #region Methods

        /// <summary>
        /// Update the valley map.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            // Update the water.
            this.water.Update(gameTime);

        } // End of the update method.

        /// <summary>
        /// Draw the map.
        /// </summary>
        /// <param name="gameTime"></param>
        /// <param name="view"></param>
        /// <param name="projection"></param>
        /// <param name="cameraPosition"></param>
        public override void Draw(GameTime gameTime, Matrix view, Matrix projection, Matrix viewProj, Vector3 cameraPosition)
        {

            // Draw the terrain first, then the sky. This is faster than
            // drawing the sky first, because the depth buffer can skip
            // bothering to draw sky pixels that are covered up by the
            // terrain. This trick works because the code used to draw
            // the sky forces all the sky vertices to be as far away as
            // possible, and turns depth testing on but depth writes off.
            this.DrawTerrain(view, projection);

            // Draw buildings.
            this.DrawPrimitivesHouses(view, projection);

            // Draw water (Must draw this last)
            this.water.Draw(gameTime, cameraPosition, viewProj);

            // Draw the sky last
            this.sky.Draw(view, projection);

            // Draw barrels if the game mode is Assault.
            if (Program.gameMode == 0)
                this.DrawItems(view, projection);

        } // End of the Draw method

        /// <summary>
        /// Helper to draw primitive boxes for a building.
        /// </summary>
        /// <param name="view"></param>
        /// <param name="projection"></param>
        private void DrawPrimitivesHouses(Matrix view, Matrix projection)
        {

            // Loop through each cube in the cubeFortrestList.
            for (int i = 0; i < cubeFortrestList.Count; i++)
            {
                if (i < 9)
                    cubeFortrestList[i].DrawTextureVerts(buildings, "FortrestBuilding", 1, view, projection, this.floorTexture);
                else if (i >= 9 && i < 11)
                    cubeFortrestList[i].DrawTextureVerts(buildings, "FortrestBuilding", 1, view, projection, this.roofTexture);
                else
                    cubeFortrestList[i].DrawTextureVerts(buildings, "FortrestBuilding", 1, view, projection, this.wallTexture);
            }

            // Draw cross walls
            for (int i = 0; i < cubeCrossWallList.Count; i++)
            {
                cubeCrossWallList[i].DrawTextureVerts(buildings, "CrossWalls", 3, view, projection, this.wallTexture);
            }

        } // End of the DrawPrimitivesHouses method

        #endregion

    } // End of class

} // End of namespace