using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Assets.Scripts.Network
{
    public class World
    {
        public static World currentWorld;
        public static int MyID;
        public IList<WorldObject> Objects = new List<WorldObject>();
        public Player Me;//pointer to myself
        public House MyHouse;
        public LightHouse MyNearestLightHouse;
        public Vector2 MinBounds;
        public Vector2 MaxBounds;
        public static System.Random rnd = new System.Random();
        public bool IsFake = false;

        public float LocalWorldTime = 0;
        public float RemoteWorldTime = 0;

        Vector2 prevDirection = new Vector2();
        float timeSinceLastUpdate = 10;
        float prevAngle = -1;

        public WorldObject getObjectByID( int id )
        {
            for( int k = 0; k < Objects.Count; k++ )
            {
                if (Objects[k].ID == id)
                    return Objects[k];
            }
            return null;
        }

        public void FrameMove(float FrameTime, Vector2 direction, float ViewAngle)
        {
            timeSinceLastUpdate += FrameTime;
            RemoteWorldTime += FrameTime;
            LocalWorldTime = RemoteWorldTime - 0.1f;//100ms delay

            if( IsFake )
            {
                FrameMoveFake(FrameTime, direction, ViewAngle);
            }
            else
            {
                //send data away
                UpdatePlayers(FrameTime);


                //update orientation anyway
                if (ViewAngle != Me.Orientation)
                {
                    Me.Orientation = ViewAngle;
                    Me.Moved = true;
                    Me.UpdateAssignedObject(FrameTime);
                }

                if ( ( direction.x != prevDirection.x ) || (direction.y != prevDirection.y))// || ( prevAngle != ViewAngle) )
                {
                    if( true )// timeSinceLastUpdate > 0.05 )
                    {
                        prevAngle = ViewAngle;
                        prevDirection = direction;
                        timeSinceLastUpdate = 0;
                        Network._.SendMove(direction, ViewAngle);
                    }
                }


            }
        }

        public void UpdatePlayers(float FrameTime)
        {
            for (int k = 0; k < Objects.Count; k++)
            {
                WorldObject wo = Objects[k];
                switch (wo.Type)
                {
                    case WorldObject.WorldObjectType.wotPlayer:
                        {
                            Player pl = (Player)wo;
                            if (pl.Updates.Count == 0)
                                break;

                            bool gotUpdate = false;

                            for( int s = 1; s < pl.Updates.Count; s++ )
                            {
                                PlayerUpdate pu = pl.Updates[s];
                                if( pu.Time > LocalWorldTime )
                                {
                                    //found next packet - interpolate
                                    PlayerUpdate prevPacket = pl.Updates[s - 1];
                                    float packetTimeDiff = pu.Time - prevPacket.Time;
                                    float percentageShift = packetTimeDiff / (pu.Time - LocalWorldTime);

                                    pl.Position = Vector2.Lerp(prevPacket.Position, pu.Position, percentageShift );
                                    pl.Orientation = Mathf.Lerp(prevPacket.Orientation, pu.Orientation, percentageShift);
                                    gotUpdate = true;
                                    if (s > 1)
                                        //remove unnecessary packets
                                        pl.Updates.RemoveAt(0);
                                    break;
                                }
                            }
                            if( !gotUpdate )
                            {
                                PlayerUpdate pu = pl.Updates[pl.Updates.Count - 1];
                                pl.Position = pu.Position + pu.Direction * (LocalWorldTime - pu.Time) * pl.Speed;
                            }
                            if ((pl.Direction.x != 0) || (pl.Direction.y != 0))
                            {
                                pl.Position += pl.Direction * FrameTime;
                                pl.Moved = true;
                            }
                        }
                        break;
                }
                Objects[k].UpdateAssignedObject(FrameTime);
            }
        }

        public void FrameMoveFake( float FrameTime, Vector2 direction, float ViewAngle )
        {
            if (direction.x != 0 || direction.y != 0 )
            {
                Vector2 speed = direction;
                if (speed.SqrMagnitude() > 1)
                    speed.Normalize();
                speed *= Me.Speed;
                Me.Position += speed * FrameTime;
                Me.Moved = true;
            }

            if( ViewAngle != Me.Orientation )
            {
                Me.Orientation = ViewAngle;
                Me.Moved = true;
            }

            float nearestLightHouse = 999999999;
            if( MyNearestLightHouse != null )
            {
                nearestLightHouse = (MyNearestLightHouse.Position - Me.Position).sqrMagnitude;
            }
            LightHouse oldNear = MyNearestLightHouse;
            MyNearestLightHouse = null;

            bool is_in_home = false;

            for (int k = 0; k < Objects.Count; k++)
            {
                WorldObject wo = Objects[k];
                switch( wo.Type )
                {
                    case WorldObject.WorldObjectType.wotCrystal:
                        if (Me.Crystals.Count < Me.CrystalCapacity)
                        {
                            //can add crystal
                            Crystal cr = (Crystal)wo;
                            Vector2 dist = wo.Position - Me.Position;
                            if (dist.sqrMagnitude < cr.CaptureRadiusSqr)
                            {
                                //capture crystal
                                Me.Crystals.Add(cr);
                                cr.DeInstantiate();
                                Objects.RemoveAt(k);
                                k--;
                                Manager._.WorldEvent.Invoke(Me, Manager.WorldEventTypes.wetCrystalTake);
                            }
                        }
                        break;
                    case WorldObject.WorldObjectType.wotHouse:
                        {
                            House ho = (House)wo;
                            Vector2 dist = ho.Position - Me.Position;
                            if (dist.sqrMagnitude < ho.PlayerDetectRangeSqr)
                            {
                                if( ho.IsMine() )
                                {
                                    is_in_home = true;
                                    if( !Me.IsInHome )
                                    {
                                        Me.IsInHome = true;
                                        Manager._.WorldEvent.Invoke(Me, Manager.WorldEventTypes.wetHomeEnter);
                                    }
                                    //can drop crystals
                                    if (Me.Crystals.Count > 0)
                                    {
                                        while (Me.Crystals.Count > 0)
                                        {
                                            Crystal cr = Me.Crystals[Me.Crystals.Count - 1];
                                            Me.Crystals.RemoveAt(Me.Crystals.Count - 1);
                                            MyHouse.Energy.Level += cr.Energy;
                                            Manager._.WorldEvent.Invoke(Me, Manager.WorldEventTypes.wetCrystalUse);
                                        }
                                        if (MyHouse.Energy.Level > MyHouse.Energy.Capacity)
                                            MyHouse.Energy.Level = MyHouse.Energy.Capacity;
                                    }
                                }

                                //can recharge flashlight
                                if (Me.FlashLight.Capacity > Me.FlashLight.Level)
                                {
                                    float amount = ho.IsMine() ? WorldConfig.HouseRechargeFlashlightRate * FrameTime : WorldConfig.HouseRechargeEnemyFlashlightRate * FrameTime;
                                    if (amount > ho.Energy.Level)
                                        amount = ho.Energy.Level;
                                    Me.FlashLight.Level += amount;
                                    ho.Energy.Level -= amount;
                                }
                            }
                        }
                        break;
                    case WorldObject.WorldObjectType.wotLightHouse:
                        {
                            LightHouse ho = (LightHouse)wo;
                            Vector2 dist = ho.Position - Me.Position;
                            if (ho.Owner == Me)
                            {
                                if ((dist.sqrMagnitude < ho.LightRadiusSqr))
                                {
                                    if (nearestLightHouse > dist.sqrMagnitude)
                                    {
                                        nearestLightHouse = dist.sqrMagnitude;
                                        MyNearestLightHouse = ho;
                                    }
                                }
                            }
                            else
                            {
                                if( dist.sqrMagnitude < ho.AcquireRadiusSqr )
                                {
                                    ho.Owner = Me;
                                    ho.LighHouseColor = Me.PlayerColor;
                                    MyNearestLightHouse = ho;
                                    Manager._.WorldDoubleEvent.Invoke(Me, ho, Manager.WorldEventTypes.wetBeaconTake);
                                }
                            }
                        }
                        break;
                }
            }

            if (oldNear != MyNearestLightHouse)
            {
                if (oldNear != null)
                {
                    Manager._.WorldEvent.Invoke(oldNear, Manager.WorldEventTypes.wetBeaconLeave);
                }
                if (MyNearestLightHouse != null)
                {
                    Manager._.WorldEvent.Invoke(MyNearestLightHouse, Manager.WorldEventTypes.wetBeaconEnter);
                }
            }

            if (!is_in_home && Me.IsInHome)
            {
                Me.IsInHome = false;
                Manager._.WorldEvent.Invoke(Me, Manager.WorldEventTypes.wetHomeLeave);
            }

            for ( int k = 0; k < Objects.Count; k++ )
            {
                Objects[k].UpdateAssignedObject(FrameTime);
            }
        }

        public static World GetFakeWorld( Manager man )
        {
            rnd.Next();

            World w = new World();
            currentWorld = w;

            Player me = new Player();
            World.MyID = 1;
            me.ID = 1;
            me.PrefabID = 1;
            me.Speed = 10;
            me.Instantiate(Manager._);
            w.Me = me;
            w.Objects.Add(me);

            House myHouse = new House();
            myHouse.Owner = me.ID;
            myHouse.PrefabID = 2;
            myHouse.Position = new Vector2(UnityEngine.Random.value * 10.0f, UnityEngine.Random.value * 10.0f);
            myHouse.Instantiate(Manager._);
            w.MyHouse = myHouse;
            w.Objects.Add(myHouse);


            for( int k = 0; k < 3; k++ )
            {
                Crystal cr = new Crystal();
                cr.PrefabID = 3 + k;
                cr.Position = new Vector2(UnityEngine.Random.value * 30.0f, UnityEngine.Random.value * 30.0f);
                cr.Energy += k * 10;
                cr.Instantiate(Manager._);
                w.Objects.Add(cr);
            }

            for (int k = 0; k < 3; k++)
            {
                LightHouse cr = new LightHouse();
                cr.PrefabID = 6;

                const float minRange = 50f;
                const float maxRange = 100f;
                var v = UnityEngine.Random.insideUnitCircle;
                v = v.normalized * minRange + v * (maxRange - minRange);

                cr.Position = v;
                cr.Instantiate(Manager._);
                w.Objects.Add(cr);
            }

            w.IsFake = true;

            return w;
        }

        public void DestroyWorld()
        {
            IsFake = false;
            for( int k = 0; k < Objects.Count; k++ )
            {
                Objects[k].DeInstantiate();
            }
            Objects.Clear();
            Me = null;
            MyHouse = null;
            MyNearestLightHouse = null;
        }
    }
}
