using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using TestBoids.Entities;
using TestBoids.Collisions;
using Ika.Graphics;

namespace TestBoids.Scene
{
    public class SceneManager
    {
        Grid m_grid;
        public Grid Grid
        {
            get { return m_grid; }
            set { m_grid = value; }
        }

        List<EnergyEntity> m_energyList = new List<EnergyEntity>();
        public List<EnergyEntity> EnergyList
        {
            get { return m_energyList; }
            set { m_energyList = value; }
        }

        List<Entity> m_emptyList;

        public int CellCount
        {
            get { return m_grid.GridCells.Count; }
        }

        public int EntityCount
        {
            get { return m_grid.EntityList.Count; }
        }

        private EntityFactory m_factory;
        public EntityFactory Factory
        {
            get { return m_factory; }
            set { m_factory = value; }
        }	

        public List<Entity> this[Point p]
        {
            get
            {
                GridCell cell;
                m_grid.GridCells.TryGetValue(p, out cell);
                if (cell != null)
                {
                    return cell.EntityList;
                }
                else
                {
                    return m_emptyList;
                }
            }
        }

        // Changed !!!
        private Vector2 m_cameraPosition;
        public Vector2 CameraPosition
        {
            get { return m_cameraPosition; }
            set { m_cameraPosition = value; }
        }
        // Changed !!!
        public Vector2 WorldToScreen(Vector2 world)
        {
            return world - CameraPosition;
        }
        // Changed !!!
        public Vector2 ScreenToWorld(Vector2 screen)
        {
            return screen + CameraPosition;
        }	

        public SceneManager()
        {
            m_grid = new Grid(40);
            m_emptyList = new List<Entity>();
        }

        public Entity AddEntity(Entity e)
        {
            e.Scene = this;
            m_grid.AddEntity(e);
            return e;
        }

        public void RemoveEntity(Entity e)
        {
            m_grid.RemoveEntity(e);
        }

        public void UpdatePosition(Entity e, Vector2 newPos)
        { 
            m_grid.UpdatePosition(e, newPos);
        }

        public void Update(float dt)
        {
            Entity e;
            int i = 0;
            while (i < m_grid.EntityList.Count)
            {
                e = m_grid.EntityList[i];
                e.Update(dt);
                if (e.isDynamic())
                {
                    Vector2 newPos = e.Position + e.Speed * dt;
                    UpdatePosition(e, newPos);
                }

                i++;
            }
        }

        public IEnumerator<Entity> GetEnumerator()
        {
           return m_grid.EntityList.GetEnumerator();
        }

        public List<Entity> FindInRadius(Vector2 center, float radius)
        {
            List<Entity> m_result = new List<Entity>();

            int nbrCell = (int)(radius / m_grid.Cellsize) + 1;            
            Point centerIndex;
            centerIndex.X = (int)Math.Floor(center.X / m_grid.Cellsize);
            centerIndex.Y = (int)Math.Floor(center.Y / m_grid.Cellsize);

            for (int i = -nbrCell; i <= nbrCell; i++)
            {
                for (int j = -nbrCell; j <= nbrCell; j++)
                {
                    Point grid = new Point(i + centerIndex.X, j + centerIndex.Y);
                    foreach (Entity e in this[grid])
                    {
                        if(e is Boid && ((Boid)e).isAlive)
                        if (Vector2.Distance(e.Position, center) < radius)
                        {
                            m_result.Add(e);
                        }
                    }
                }
            }

            return m_result;
        }

        private Boid m_selectedEntity = null;
        public Boid SelectedEntity
        {
            get { return m_selectedEntity; }
            set { m_selectedEntity = value; }
        }

        private Vector2 m_selectedEntityGoal;
        public Vector2 SelectedEntityGoal
        {
            get { return m_selectedEntityGoal; }
            set { m_selectedEntityGoal = value; }
        }

        public Boid EntityCollideBoid(Entity e)
        {
            Point cell ;
            int nbrCell = 2;
            for (int i = -nbrCell; i <= nbrCell; i++)
            {
                for (int j = -nbrCell; j <= nbrCell; j++)
                {
                    cell = new Point((int)(e.Position.X / Grid.Cellsize)  + i, (int)(e.Position.Y / Grid.Cellsize) + j);

                    foreach (Entity b in this[cell])
                    { 
                        if (b.GetType() == typeof(Boid) && ((Boid)b).isAlive && e.isCollideWith(b)) return (Boid)b;
                    }
                }
            }
            return null;
        }

        public Boid FindBoidInCell(Vector2 pos)
        {
            Point cell ;
            PixelCollision pixel = new PixelCollision(new Vector2(), pos);

            for (int i = -1; i <= 1; i++)
            {
                for (int j = -1; j <= 1; j++)
                {
                    cell = new Point((int)(pos.X / Grid.Cellsize) + i, (int)(pos.Y / Grid.Cellsize) + j);
                    foreach (Entity e in this[cell])
                    {
                        if (e is Boid && ((Boid)e).isAlive && pixel.isCollideWith(e.Collision)) return (Boid)e;
                    }
                }
            }

            return null;
        }
    }
}
