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 first map for Fredriksdal Assault, the valley.
    /// </summary>
    public class Valley : IMap
    {
        #region Instance variables

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

        // Stone texture
        Texture2D stoneTexture;

        #endregion

        #region Constructors

        /// <summary>
        /// Create a new valley map.
        /// </summary>
        /// <param name="content"></param>
        /// <param name="graphicsDevice"></param>
        public Valley(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 = 11;
            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/Valley/valleyHeightMap");
            this.sky = content.Load<Sky>("Maps/CommonResources/sky");
            this.water = new Water(content, this.graphicsDevice, new Vector3(0, -300, 0));
            this.barrelModel = content.Load<Model>("Models/Barrels/Barrel");
            this.treeModel = content.Load<Model>("Models/Trees/Oak_Bald_06");
            this.grassModel = content.Load<Model>("Models/Grass/grass_v4");

            // The terrain processor attached a HeightMapInfo to the terrain model's Tag. 
            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/Valley/valleyVegetationMap");

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

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

            // Load textures.
            this.roadTexture = content.Load<Texture2D>("Textures/road");
            this.sandTexture = content.Load<Texture2D>("Textures/sandRefraction");
            this.grassTexture = content.Load<Texture2D>("Textures/grass");
            this.rockTexture = content.Load<Texture2D>("Textures/rock");
            this.floorTexture = content.Load<Texture2D>("Models/House/cementFloor");
            this.wallTexture = content.Load<Texture2D>("Models/House/bricks");
            this.roofTexture = content.Load<Texture2D>("Models/House/roof");
            this.stoneTexture = content.Load<Texture2D>("Textures/stone-wall");

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

            // Add five houses.
            buildings.Add(new House(new Vector3(-500, -150, 800), new Vector3(0,0,0), 0.4f));
            buildings.Add(new House(new Vector3(-500, -215, -110), new Vector3(0, 0, 0), 0.4f));
            buildings.Add(new House(new Vector3(1100, -180, 500), new Vector3(0, 0, 0), 0.4f));
            buildings.Add(new House(new Vector3(1100, -170, -150), new Vector3(0, 0, 0), 0.4f));
            buildings.Add(new House(new Vector3(300, -140, -800), new Vector3(0, 0, 0), 0.4f));

            // Add some walls
            float height = 0;
            this.heightMapInfo.GetHeight(new Vector3(-3400, -180, -3400), out height);
            buildings.Add(new CrossWalls(new Vector3(-3400, height - 5, -3400), new Vector3(0, 0, 0), 1f));
            this.heightMapInfo.GetHeight(new Vector3(3700, 0, -3300), out height);
            buildings.Add(new CrossWalls(new Vector3(3700, height - 5, -3300), new Vector3(0, 0, 0), 1f));
            this.heightMapInfo.GetHeight(new Vector3(-3000, 0, 3300), out height);
            buildings.Add(new CrossWalls(new Vector3(-3000, height - 5, 3300), new Vector3(0, 0, 0), 1f));
            this.heightMapInfo.GetHeight(new Vector3(3500, 0, 3600), out height);
            buildings.Add(new CrossWalls(new Vector3(3500, height - 5, 3600), new Vector3(0, 0, 0), 1f));
            this.heightMapInfo.GetHeight(new Vector3(5200, 0, -200), out height);
            buildings.Add(new CrossWalls(new Vector3(5200, height - 5, -200), new Vector3(0, 0, 0), 1f));
            this.heightMapInfo.GetHeight(new Vector3(4500, 0, -1500), out height);
            buildings.Add(new CrossWalls(new Vector3(4500, height - 5, -1500), 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(-650, -150, 820), new Vector3(0, 0, 0), 35f);
                items[1] = new Barrel(new Vector3(-650, -215, -90), new Vector3(0, 0, 0), 35f);
                items[2] = new Barrel(new Vector3(950, -180, 520), new Vector3(0, 0, 0), 35f);
                items[3] = new Barrel(new Vector3(950, -170, -130), new Vector3(0, 0, 0), 35f);
                items[4] = new Barrel(new Vector3(150, -140, -780), new Vector3(0, 0, 0), 35f);
            }

            // Load the cubeHouseList with cubes for a house.
            this.cubeHouseList = House.CreateCubes(this.graphicsDevice);
            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 house.
        /// </summary>
        /// <param name="view"></param>
        /// <param name="projection"></param>
        private void DrawPrimitivesHouses(Matrix view, Matrix projection)
        {

            // Loop through each cube in the cubeHouseList.
            for (int i = 0; i < cubeHouseList.Count; i++)
            {

                if (i == 0)
                    cubeHouseList[i].DrawTextureVerts(buildings, "House", 5, view, projection, this.floorTexture);
                else if (i == 1)
                    cubeHouseList[i].DrawTextureVerts(buildings, "House", 5, view, projection, this.roofTexture);
                else
                    cubeHouseList[i].DrawTextureVerts(buildings, "House", 5, view, projection, this.wallTexture);

            }

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

        } // End of the DrawPrimitivesHouses method

        #endregion

    } // End of class

} // End of namespace