using System;
using System.IO;
using System.Drawing;
using System.Collections;
using System.Xml.Serialization;

using Icarus.Graphics.Animation;
using Icarus.Logic.GameEngine;
using Icarus.Logic.Physics;

namespace Icarus.Logic 
{
    /// <summary>
    /// Contains the necessary items used to describe a Game stage.
    /// Holds the information about the current list of WorldObjects,
    /// the view background, the view terrain collision mask, and
    /// other necessary information used to play the game level.
    /// </summary>
    public class Stage 
    {
    
        #region Instance Variables
        /// <summary>
        /// This defines the scale from which the Physics Engine converts
        /// its calculations into GameWorld.
        /// </summary>
        private float fPhysicsScale;
        /// <summary>
        /// Game settings for the level.
        /// </summary>
        private GameSettings settings = null;
        /// <summary>
        /// List of all items in the level.
        /// </summary>
        private WorldObject[] items = null;
        /// <summary>
        /// List of all particles in the level.
        /// </summary>
        private WorldObject[] particles = null;
        /// <summary>
        /// List of all actors in the level.
        /// </summary>
        private WorldObject[] actors = null;
        /// <summary>
        /// List of all terrain objects.
        /// </summary>
        private WorldObject[] terrain = null;
        /// <summary>
        /// This is the guy controlled by the user.
        /// </summary>
        private int playerActorIndex = 0;
		/// <summary>
		/// The list of backgrounds.
		/// </summary>
		private ArrayList mBackgrounds = new ArrayList();
        /// <summary>
        /// The collision boxes that cause collisions with this level
        /// </summary>
		private CollisionBox[] collisionBoxes = null;

		/// <summary>
		/// The zoom for the stage. See Field description for more info.
		/// </summary>
		private float mZoomFactor = 1;

		/// <summary>
		/// The center of the view for the stage.
		/// </summary>
		private Icarus.Logic.Physics.Point mViewCenter = new Icarus.Logic.Physics.Point(0, 0, 0);

		private int mWidth;
		private int mHeight;

        /// <summary>
        /// The name of this stage
        /// </summary>
        private string strStageName;
        
        #endregion
    
        #region Initialization
        /* -------------- */
        /* Initialization */
        /* -------------- */

        /// <summary>
        /// Makes a new level.
        /// </summary>
        public Stage() 
        {
            fPhysicsScale = 1;
            settings = GameSettings.Instance;
            items = new WorldObject[settings.sv_maxItems];
            particles = new WorldObject[settings.sv_maxParticles];
            actors = new WorldObject[settings.sv_maxActors];
            terrain = new WorldObject[settings.sv_maxTerrainObjects];
            collisionBoxes = new CollisionBox[settings.sv_maxCollisionBoxes];

            strStageName = "NoName";
        }
        #endregion

        #region Acessing/Modifying
        
        /// <summary>
        /// This is VERY important.
        /// This defines the scale from which the Physics Engine converts
        /// its calculations into GameWorld.
        /// The result is then truncated into an unsigned integer
        ///  and then used to place the object in the world
        /// </summary>
        public float physicsScale 
        {
            get { return this.fPhysicsScale; }
            set { this.fPhysicsScale = value;}
        }
    
        /// <summary>
        /// Gives usable access to the list of items.
        /// </summary>
        public WorldObject[] itemList 
        {
            //Placing reference to merc in items list so he is draw again(drawn over other actors)
            get
            { 
                this.items[0] = this.actors[playerActorIndex];
                return this.items;
            }
            set { this.items = value; }
        }

        /// <summary>
        /// Gives usable access to the list of particles.
        /// </summary>
        public WorldObject[] particleList 
        {
            get { return this.particles; }
            set { this.particles = value; }
        }

        /// <summary>
        /// Gives usable access to the list of actors.
        /// </summary>
        public WorldObject[] actorList 
        {
            get { return this.actors; }
            set { this.actors = value; }
        }

        /// <summary>
        /// Gives usable access to the list of terrain.
        /// </summary>
        public WorldObject[] terrainList 
        {
            get { return this.terrain; }
            set { this.terrain = value; }
        }

        /// <summary>
        /// The collision boxes that cause collisions with this level
        /// </summary>
        public CollisionBox[] CollisionBoxes
        {
            get { return this.collisionBoxes; }
            set { this.collisionBoxes = value; }
        }

		/// <summary>
		/// Gets or sets the list of backgrounds. This is purely for XML serialization purposes.
		/// </summary>
		public LevelBackground[] Backgrounds
		{
			get { return (LevelBackground[]) mBackgrounds.ToArray(typeof(LevelBackground)); }
			set 
			{
				mBackgrounds.Clear();
				mBackgrounds.AddRange(value);
				mBackgrounds.Sort();
			}
		}

		/// <summary>
		/// Returns the number of backgrounds we have.
		/// </summary>
		/// <returns></returns>
		public int GetBackgroundCount() { return mBackgrounds.Count; }


        /// <summary>
        /// Adds a background to the background arraylist
        /// </summary>
        /// <param name="lb">LevelBackground to be added to stage</param>
        public void AddBackground(LevelBackground lb)
        {
            mBackgrounds.Add(lb);
        }

        /// <summary>
        /// Removed a background from the stage
        /// </summary>
        /// <param name="lb">The LevelBackground to be removed</param>
        public void removeBackground(LevelBackground lb)
        {
            mBackgrounds.Remove(lb);
        }

		/// <summary>
		/// Returns the index-th background.
		/// </summary>
		/// <param name="index"></param>
		/// <returns></returns>
		public LevelBackground GetBackgroundAt(int index) { return (LevelBackground) mBackgrounds[index]; }

        /// <summary>
        /// The world object that is controlled by the player.
        /// </summary>
        public WorldObject PlayerActor 
        {
            get { return this.actorList[this.PlayerActorIndex]; }
        }

        /// <summary>
        /// The index into the array of actors of the one that is controlled by the player.
        /// </summary>
        public int PlayerActorIndex
        {
            get { return this.playerActorIndex; }
            set { this.playerActorIndex = value; }
		}

		/// <summary>
		/// Gets or sets the zoom level for the display. The zoom level of 1 means the images appear precisely their size, the zoom level of 0.5 means everything is half size, etc. It might not be a good idea to set this to zero, as stuff would kinda disappear then.
		/// </summary>
		public float ZoomFactor
		{
			get { return mZoomFactor; }
			set { mZoomFactor = value; }
		}

		/// <summary>
		/// Gets or sets the view center. That is basically the world coordinates of the point that will be in the center of the screen.
		/// </summary>
		public Icarus.Logic.Physics.Point ViewCenter
		{
			get { return mViewCenter; }
			set { mViewCenter = value; }
		}

		/// <summary>
		/// Gets or sets stage width.
		/// </summary>
		public int Width
		{
			get { return mWidth; }
			set { mWidth = value; }
		}

		/// <summary>
		/// Gets or sets stage height.
		/// </summary>
		public int Height
		{
			get { return mHeight; }
			set { mHeight = value; }
		}

		/// <summary>
		/// Gets the center of the level.
		/// </summary>
		[XmlIgnore]
		public Icarus.Logic.Physics.Point LevelCenter
		{
			get { return new Icarus.Logic.Physics.Point(this.Width / 2.0f, this.Height / 2.0f, 0); }
		}

        /// <summary>
        /// gets or sets this stages name
        /// </summary>
        public string StageName
        {
            get { return this.strStageName; }
            set { this.strStageName = value; }
        }

        #endregion

        #region Old Crap 
#if TMP_TESTING
        //TODO: remove
        public void tmp()
        {

//            this.collisionBoxes[0] = new CollisionBoxWall();
//            this.collisionBoxes[1] = new CollisionBoxWall();
//            this.collisionBoxes[2] = new CollisionBoxFloor();
//            this.collisionBoxes[3] = new CollisionBox();
//            this.collisionBoxes[4] = new CollisionBoxFloor();
//            this.collisionBoxes[5] = new CollisionBox();
//            
//            Physics.PhysicsRectangle rec1 = new PhysicsRectangle(0,0, 50, 600);//left
//            Physics.PhysicsRectangle rec2 = new PhysicsRectangle(750, 0, 50, 600);//right
//            Physics.PhysicsRectangle rec3 = new PhysicsRectangle(50, 550, 700, 50);//floor
//            Physics.PhysicsRectangle rec4 = new PhysicsRectangle(350, 150, 400, 400);//triangle
//            Physics.PhysicsRectangle rec5 = new PhysicsRectangle(150, 325, 150, 25);//Platform
//            Physics.PhysicsRectangle rec6 = new PhysicsRectangle(550, 350, 200, 100);//triangle
//
//
//            
//            TriangleBR tri1 = new TriangleBR();
//            tri1.P1.x = 350f;
//            tri1.P1.y = 550f;
//            tri1.P2.x = 550f;
//            tri1.P2.y = 550f;
//            tri1.P3.x = 550f;
//            tri1.P3.y = 350f;
//            tri1.calculateTriangleBounds();
//            tri1.Slope = tri1.calculateSlope();
//
//            TriangleBL tri2 = new TriangleBL();
//            tri2.P1.x = 550f;
//            tri2.P1.y = 350f;
//            tri2.P2.x = 550f;
//            tri2.P2.y = 450f;
//            tri2.P3.x = 750f;
//            tri2.P3.y = 450f;
//            tri2.calculateTriangleBounds();
//            tri2.Slope = tri2.calculateSlope();
//
//
//            this.collisionBoxes[0].BoundingBox  = rec1;
//            this.collisionBoxes[1].BoundingBox  = rec2;
//            this.collisionBoxes[2].BoundingBox  = rec3;
//            this.collisionBoxes[3].BoundingBox  = rec4;
//            this.collisionBoxes[3].TriArray[0] = tri1;
//            this.collisionBoxes[4].BoundingBox = rec5;
//            this.collisionBoxes[5].BoundingBox = rec6;
//            this.collisionBoxes[5].TriArray[0] = tri2;
//
//            Logic.Physics.PhysicsMerc merc = new Icarus.Logic.Physics.PhysicsMerc();
//            Logic.Physics.Point point = new Icarus.Logic.Physics.Point();
//            point.x = 150;
//            point.y = 400;
//            merc.AnimationId = "merc\\stand_left\\frameset\\stand_left.png";
//            merc.Location = point;
//           
//            actors[0] = merc;

#endif

#if OLD_COLLISION_LEVEL_STUFF
            Logic.Physics.PhysicsParticle particle = new Icarus.Logic.Physics.PhysicsParticle();
            Logic.Physics.Point point2 = new Icarus.Logic.Physics.Point();
            point2.x = 700;
            point2.y = 100;
            particle.Velocity.i = -75;
            particle.AnimationId = "/enemy1/enemy1.png";
            particle.Location = point2;
            particle.BounceCoeffecient = .5f;

            particles[0] = particle;

            Logic.Physics.PhysicsTarget target = new Icarus.Logic.Physics.PhysicsTarget();
            Logic.Physics.Point point3 = new Icarus.Logic.Physics.Point();
            point3.x = 650;
            point3.y = 100;
            target.AnimationId = "/edo/stand_left/frameset/stand_left.png";
            target.Location = point3;

            actors[1] = target;

            Logic.Physics.PhysicsFootball ball = new Icarus.Logic.Physics.PhysicsFootball();
            Logic.Physics.Point point4 = new Icarus.Logic.Physics.Point();
            point4.x = 150;
            point4.y = 500;
            ball.AnimationId = "/enemy2/enemy2.png";
            ball.Location = point4;

            actors[2] = ball;
#endif

#if TESTING_TRIANGLE 

            Physics.Triangle  tri = new Physics.Triangle(new Physics.Point(100.0f, 100.0f, 0.0f),
                                                         new Physics.Point(200.0f, 200.0f, 0.0f),
                                                         new Physics.Point(200.0f, 100.0f, 0.0f));
            PhysicsRectangle rect = new PhysicsRectangle();
            rect.X = 170;
            rect.Y = 120 - 25;
            rect.Height = 25+25;
            rect.Width = 25;

            // should intersect y leg
            if(tri.IntersectsWith(rect))
            {
                int inkjas = 0234;
            }
            int asdf = 1234;


            rect.X = 170;
            rect.Y = 120;
            rect.Height = 25;
            rect.Width = 25+25;


            //should intersect x leg
            if(tri.IntersectsWith(rect))
            {
                int askdjf = 343;
            }
            int asdffff = 123233;

            tri.P3 = new Icarus.Logic.Physics.Point(100.0f, 200.0f, 0.0f);
    
            //should not intersect
            if(tri.IntersectsWith(rect))
            {
                int adskdjf = 343;
            }
            int asdfffdf = 123233;

            rect.X = 120-25;
            rect.Y = 170;
            rect.Height = 25;
            rect.Width = 25+25;

            //should intersect x legasdf
            if(tri.IntersectsWith(rect))
            {
                int adskddfjf = 343;
            }
            int asdfdfffdf = 123233;

            rect.X = 120;
            rect.Y = 170;
            rect.Height = 25+25;
            rect.Width = 25;

            //should intersect y leg
            if(tri.IntersectsWith(rect))
            {
                int adsdfskddfjf = 343;
            }
            int asdfdfdfffdf = 123233;



            
#endif
        #endregion
            
    }
}