using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Barotrauma
{
    partial class TestGameMode : GameMode
    {
        public Action OnRoundEnd;

        public bool SpawnOutpost;

        public OutpostGenerationParams OutpostParams;
        public LocationType OutpostType;

        public EventPrefab TriggeredEvent;

        private List<Event> scriptedEvent;

        private GUIButton createEventButton;

        public LevelGenerationParams BackgroundParams { get; private set; }
        public Vector2 WaterParticleOffset;

        public override void Start()
        {
            base.Start();
            
            CrewManager.InitSinglePlayerRound();
            foreach (Submarine submarine in Submarine.Loaded)
            {
                submarine.NeutralizeBallast();
                switch (submarine.Info.Type)
                {
                    case SubmarineType.Outpost:
                    case SubmarineType.OutpostModule:
                    case SubmarineType.Wreck:
                    case SubmarineType.BeaconStation:
                        //normally the body would be made static during level generation,
                        //but in the test mode we load the outpost/wreck/beacon as if it was a normal sub and need to do this manually
                        submarine.PhysicsBody.BodyType = FarseerPhysics.BodyType.Static;
                        if (submarine.Info.ShouldBeRuin)
                        {
                            submarine.Info.Type = SubmarineType.Ruin;
                        }
                        submarine.TeamID = submarine.Info.IsOutpost ? CharacterTeamType.FriendlyNPC : CharacterTeamType.None;
                        break;
                }
            }
                
            if (SpawnOutpost)
            {
                GenerateOutpost(Submarine.MainSub);
            }

            if (TriggeredEvent != null)
            {
                scriptedEvent = new List<Event> { TriggeredEvent.CreateInstance(GameMain.GameSession.EventManager.RandomSeed) };
                GameMain.GameSession.EventManager.PinnedEvent = scriptedEvent.Last();

                createEventButton = new GUIButton(new RectTransform(new Point(128, 64), GUI.Canvas, Anchor.TopCenter) { ScreenSpaceOffset = new Point(0, 32) }, TextManager.Get("create"))
                {
                    OnClicked = delegate 
                    {
                        scriptedEvent.Add(TriggeredEvent.CreateInstance(GameMain.GameSession.EventManager.RandomSeed));
                        GameMain.GameSession.EventManager.PinnedEvent = scriptedEvent.Last();
                        return true;
                    }
                };
            }

            if (Level.Loaded == null)
            {
                BackgroundParams ??= LevelGenerationParams.LevelParams.Where(lp => !lp.AllowedBiomeIdentifiers.Contains("endzone")).GetRandom(Rand.RandSync.Unsynced);
                GameMain.LightManager.AmbientLight = BackgroundParams.AmbientLightColor;
            }
        }

        public override void AddToGUIUpdateList()
        {
            base.AddToGUIUpdateList();
            createEventButton?.AddToGUIUpdateList();
        }

        public override void End(CampaignMode.TransitionType transitionType = CampaignMode.TransitionType.None)
        {
            GameMain.GameSession.EventManager.PinnedEvent = null;
            OnRoundEnd?.Invoke();
        }

        public override void Update(float deltaTime)
        {
            base.Update(deltaTime);

            if (scriptedEvent != null)
            {
                foreach (Event sEvent in scriptedEvent.Where(sEvent => !sEvent.IsFinished))
                {
                    sEvent.Update(deltaTime);
                }
            }

            BackgroundParams?.UpdateWaterParticleOffset(ref WaterParticleOffset, BackgroundParams.WaterParticleVelocity, deltaTime);
        }

        private void GenerateOutpost(Submarine submarine)
        {
            Submarine outpost = OutpostGenerator.Generate(OutpostParams ?? OutpostGenerationParams.OutpostParams.GetRandomUnsynced(), OutpostType ?? LocationType.Prefabs.GetRandomUnsynced());
            outpost.SetPosition(Vector2.Zero);

            float closestDistance = 0.0f;
            DockingPort myPort = null, outPostPort = null;
            foreach (DockingPort port in DockingPort.List)
            {
                if (port.IsHorizontal || port.Docked) { continue; }
                if (port.Item.Submarine == outpost)
                {
                    outPostPort = port;
                    continue;
                }
                if (port.Item.Submarine != submarine) { continue; }

                //the submarine port has to be at the top of the sub
                if (port.Item.WorldPosition.Y < submarine.WorldPosition.Y) { continue; }

                float dist = Vector2.DistanceSquared(port.Item.WorldPosition, outpost.WorldPosition);
                if ((myPort == null || dist < closestDistance || port.MainDockingPort) && !(myPort?.MainDockingPort ?? false))
                {
                    myPort = port;
                    closestDistance = dist;
                }
            }

            if (myPort != null && outPostPort != null)
            {
                Vector2 portDiff = myPort.Item.WorldPosition - submarine.WorldPosition;
                Vector2 spawnPos = (outPostPort.Item.WorldPosition - portDiff) - Vector2.UnitY * outPostPort.DockedDistance;

                submarine.SetPosition(spawnPos);
                myPort.Dock(outPostPort);
                myPort.Lock(true);
            }

            if (Character.Controlled != null)
            {
                Character.Controlled.TeleportTo(outpost.GetWaypoints(false).GetRandomUnsynced(point => point.SpawnType == SpawnType.Human).WorldPosition);
            }
        }
    }
}
