using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;

public sealed class LocalViewStateController : AbstractState
{
    [SerializeField]
    private AbstractState systemMapState = null;
    [SerializeField]
    private string mainMenuSceneId = "MainMenu";
    [SerializeField]
    private string winScreenSceneId = "WinScene";
    [SerializeField]
    private string loseScreenSceneId = "LoseScene";
    [SerializeField]
    private Profile profile = null;
    [SerializeField]
    private ShipViewProvider shipFactory = null;
    [SerializeField]
    private ObjectSelectionMarker objectSelectionMarker = null;
    [SerializeField]
    private OrbitalCameraControl orbitalCamera = null;
    [SerializeField]
    private NavPointSpaceView navPointSpaceView = null;
    [SerializeField]
    private NavPointSpaceView navPointSpaceViewPrev = null;
    [SerializeField]
    private LocalSpaceList localObjectsList = null; // scan list
    [SerializeField]
    private GameObject starMapPanel = null; // scan list
    [SerializeField]
    private GameObject nextPhasePanel = null; // scan list
    [SerializeField]
    private InteractionsController interactions = null;
    [SerializeField]
    private BattleController battle = null;
    [SerializeField]
    private DialogUI dialogUI = null;
    [SerializeField]
    private HyperJumpView hyperJumpView = null;
    [SerializeField]
    private PlanetsView planetsView = null;

    [SerializeField]
    private float defaultCameraFoW = 60;

    private IInteractableObject interactableObject = null;
    private StarShip StarShip => profile.Player.StarShip;
    private ShipView shipView = null;
    private NavigationPoint navigationPoint = null;

    protected override void OnEnterState()
    {
        navigationPoint = profile.Player.CurrentPoint;
        shipView.SetTransform(StarShip.Position, StarShip.Rotation);
        orbitalCamera.SetPosition(StarShip.Position);
        orbitalCamera.SetFieldOfView(defaultCameraFoW);

        const float systemScale = 10000f;
        planetsView.transform.localPosition = -navigationPoint.SystemPostition * systemScale;
        navPointSpaceViewPrev.transform.position = new Vector3(0, 0, 2000);
        navPointSpaceView.transform.position = new Vector3(0, 0, 0);
        navPointSpaceView.GenerateObjects(navigationPoint);
        SelectObject(null);

        starMapPanel.SetActive(true);
        nextPhasePanel.SetActive(false);
        dialogUI.Hide();

        if (profile.Player.HyperJump.HavePhases)
        {
            ShowHyperJumpPhase(profile.Player.HyperJump.MoveNext());
        }
        else
        {
            localObjectsList.SetButtons(GetObjectsList());
        }
        StarShip.Equipment.Ship.OnChangeEquipment += LoadShipView;
    }

    protected override void OnExitState()
    {
        orbitalCamera.SetFieldOfView(defaultCameraFoW);
        StarShip.Equipment.Ship.OnChangeEquipment -= LoadShipView;
    }

    private List<string> GetObjectsList() =>
        navigationPoint.Stantions.Select(stantion => $"{stantion.Name} ({stantion.TypeId})")
        .Concat(
            navigationPoint.Ships.Select(ship => $"{ship.Name}")
            ).ToList();

    private void ShowHyperJumpPhase(HyperJumpPhase jump)
    {
        navigationPoint = jump.To;
        (navPointSpaceViewPrev, navPointSpaceView) = (navPointSpaceView, navPointSpaceViewPrev); // use current as cache
        navPointSpaceViewPrev.GenerateObjects(jump.From);
        navPointSpaceView.GenerateObjects(jump.To);

        hyperJumpView.ShowHyperJumpPhase(StarShip, shipView, jump, navPointSpaceViewPrev, navPointSpaceView, planetsView, EndHyperJumpPhase);
        starMapPanel.SetActive(false);
        nextPhasePanel.SetActive(false);
        SelectObject(null);
        localObjectsList.SetButtons(null);
    }

    private void EndHyperJumpPhase()
    {
        if (profile.Player.HyperJump.HavePhases)
        {
            if (navigationPoint.HavePirates())
            {
                StartPiratesEvent();
            }
            else
            {
                localObjectsList.SetButtons(GetObjectsList());
                nextPhasePanel.SetActive(true);
            }
            return;
        }
        // logical
        profile.Player.MakeEconomyTurn();
        profile.SavePlayer();
        // view
        localObjectsList.SetButtons(GetObjectsList());
        nextPhasePanel.SetActive(false);
        starMapPanel.SetActive(true);
    }

    private void StartPiratesEvent()
    {
        var interactable = navigationPoint.Ships.First();
        var viewRadius = navPointSpaceView.GetView(interactable)?.Radius ?? 1f;
        objectSelectionMarker.Show(interactable.Position, viewRadius);
        dialogUI.Show(
            "Give us your load, otherwise we will shoot you",
            new[] {
                "Surrender",
                "I will defend myself! <color=#AAAAAA>*break and fight</color>",
                "... <color=#AAAAAA>*break and flee</color>" },
            "Pirate",
            OnPirateEventResult);
    }

    private void OnPirateEventResult(int id)
    {
        switch (id) {
            case 0:
                StealCargo();
                break;
            case -1: // fight
            case 1:
                battle.StartBattle(shipView, navPointSpaceView.GetView(navigationPoint.Ships.First()) as ShipView, OnBattleEnded);
                break;
            case 2:
                battle.StartBattle(shipView, navPointSpaceView.GetView(navigationPoint.Ships.First()) as ShipView, OnBattleEnded);
                battle.TryFlee();
                break;
        }
    }

    private void OnBattleEnded(BattleResult result)
    {
        objectSelectionMarker.Hide();
        switch (result) {
            case BattleResult.Escape:
                NextPhase();
                break;
            case BattleResult.Win:
                localObjectsList.SetButtons(GetObjectsList());
                nextPhasePanel.SetActive(true);
                break;
            case BattleResult.Lose:
                LoseGame();
                break;
            case BattleResult.Surrender:
                localObjectsList.SetButtons(GetObjectsList());
                nextPhasePanel.SetActive(true);
                break;
        }
    }

    private void StealCargo()
    {
        dialogUI.Show(
            "Pirates unloaded as much cargo as could fit in their ship",
            null,
            "Pirate");
        profile.Player.PiratesStealCargo();
        NextPhase();
    }

    // Call from editor
    public void ShowSystemMapState()
    {
        StateMachine.SetState(systemMapState);
    }

    // Call from editor
    public void NextPhase()
    {
        var jumpPhase = profile.Player.HyperJump.MoveNext();
        if (jumpPhase == null)
            return;
        ShowHyperJumpPhase(jumpPhase);
    }

    public void ExitToMainMenu()
    {
        SceneManager.LoadScene(mainMenuSceneId);
    }

    public void WinGame()
    {
        SceneManager.LoadScene(winScreenSceneId);
    }

    public void LoseGame()
    {
        SceneManager.LoadScene(loseScreenSceneId);
    }

    private void SelectObject(IInteractableObject interactable)
    {
        interactableObject = interactable;
        if (interactableObject == null)
        {
            interactions.SetObject(null);
            objectSelectionMarker.Hide();
        } else
        {
            interactions.SetObject(interactableObject);
            var viewRadius = navPointSpaceView.GetView(interactable)?.Radius ?? 1f;
            objectSelectionMarker.Show(interactableObject.Position, viewRadius);
        }
    }

    private void SelectStantionFromUI(int objectId)
    {
        if (objectId >= 0 && objectId < navigationPoint.Stantions.Length)
        {
            SelectObject(navigationPoint.Stantions[objectId]);
            orbitalCamera.LookAt(interactableObject.Position, 0.3f, 10, -15);
        }
        var shipId = objectId - navigationPoint.Stantions.Length;
        if (shipId >= 0 && shipId < navigationPoint.Ships.Count)
        {
            SelectObject(navigationPoint.Ships[shipId]);
            orbitalCamera.LookAt(interactableObject.Position, 0.3f, 10, -15);
        }
    }

    private void Start()
    {
        localObjectsList.OnSelectObject += SelectStantionFromUI;
        orbitalCamera.LookAt(-StarShip.Position, 0, 10, -20);
        LoadShipView(StarShip.Equipment.Ship.Blueprint.Id);
        planetsView.SetPlanets(profile.Player.Planets.Select(planet => new PlanetsView.PlanetData(planet.SystemPosition, planet.Radius, planet.Color, planet == profile.Player.Planets.FirstOrDefault())).ToArray());
    }

    private void LoadShipView(string id)
    {
        if (shipView != null)
            Destroy(shipView.gameObject);
        shipView = shipFactory.CreateShipView(id, transform);
        shipView.SetTransform(StarShip.Position, StarShip.Rotation);
        shipView.Init(StarShip);
    }

    private void OnApplicationQuit()
    {
        profile.SavePlayer();
    }

    private void Update()
    {
        // select
        if (Input.GetMouseButtonUp(0) && EventSystem.current != null && !EventSystem.current.IsPointerOverGameObject()) // TODO fix for android
        {
            var ray = orbitalCamera.GetRay(Input.mousePosition);
            SelectObject(navPointSpaceView.Raycast(ray));
        }
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            ExitToMainMenu();
        }
        if (profile.Player.CheckVictoryCondition()) {
            enabled = false;
            WinGame();
        }
    }
}
