using UnityEngine;
using Me.StefS.UnityUtility.MVC;
using System.Collections;
using Me.StefS.UnityUtility;
using System;
using Me.StefS.Utility;
using Random = UnityEngine.Random;

public class GravityPullController : Controller
{
    [SerializeField]
    private GameObject[] planetTemplates;

    [SerializeField]
    private float massToScaleRatio;

    [SerializeField]
    private GameObject shipTemplate;

    [SerializeField]
    private GameObject trailTemplate;

    private float shipStartMomentum = 5;

    [SerializeField]
    private Material lineMaterial;

    private Ship ship;

    private Level level;

    private Ship trailShip;

    PlayerModel playerModel;
    private Touch[] touches;
    private Vector2 initialTouchPosition;

    protected override Model DefaultObjectWhenNoModelIsPresent()
    {
        PlayerModel playerModel = new PlayerModel();
        playerModel.Level = 1;
        return playerModel;
    }

    protected override SceneType GeteSceneForWhichThisControllerIs()
    {
        return SceneType.TheAwakeningScene;
    }

    protected override void InitializeModel(Model model)
    {
        this.playerModel = (PlayerModel)model;
    }

    protected override IEnumerator OnStart()
    {
        level = SpaceLevelGenerator.getLevel(this.playerModel.Level);
        Random.seed = playerModel.Level;
        foreach (Planet planet in level.GetPlanets())
        {
            CreatePlanet(planet);
        }

        yield return null;
    }

    public void Update()
    {

        TouchInput();

        if (this.ship == null)
        {
            return;
        }

        CalculateSceneEnd();

        this.ship.ApplyForce(GravityPullCalculator.calculatePull(level.GetPlanets(), this.ship.location), Time.deltaTime);
    }

    private void CalculateSceneEnd()
    {
        if (this.isShipInOrbit(this.ship, this.level.TargetPlanet)
            || this.isShipOutOfScreen()
           )
        {
            Navigator.GoToScene(SceneType.AtmosphereEntranceScene, this.playerModel);
        }

        foreach (Planet planet in this.level.otherPlanets)
        {
            if (this.isShipInOrbit(ship, planet))
            {
                Navigator.GoToScene(SceneType.GravityPullScene, this.playerModel);
            }
        }
    }

    private bool isShipOutOfScreen() {
        if(this.ship.location.magnitude > 200){
            return true;
        }else{
            return false;
        }
    }

    private bool isShipInOrbit(Ship ship, Planet targetPlanet)
    {
        if ((ship.location)
            .VectorTo(targetPlanet.position).sqrMagnitude < GravityPullCalculator.calculateRadius(targetPlanet.mass))
        {
            return true;
        }

        return false;
    }

    private void Fling(Vector3 flingVector)
    {
        CreateShip(level.StartPlanet);
        this.ship.Initialize(level.StartPlanet.position, flingVector);
    }

    private void CreateShip(Planet homePlanet)
    {
        GameObject shipGameObject = Instantiate(shipTemplate);
        this.ship = shipGameObject.AddComponent<Ship>();
        this.ship.transform.localScale = Ship.SCALE.XYZ();
        this.ship.transform.position = homePlanet.position * Constants.DISTANCE_SCALE;
    }

    private void CreateTrail(Planet homePlanet)
    {
        GameObject trailGameObject = Instantiate(trailTemplate);
        this.trailShip = trailGameObject.AddComponent<Ship>();
        this.trailShip.transform.localScale = Ship.SCALE.XYZ();
        this.trailShip.transform.position = homePlanet.position * Constants.DISTANCE_SCALE;
    }

    private void CreatePlanet(Planet planet)
    {
        GameObject planetGameObject = Instantiate(planetTemplates[Random.Range(0, planetTemplates.Length)]);
        planetGameObject.transform.localPosition = planet.position * Constants.DISTANCE_SCALE;
        float scale = GravityPullCalculator.calculateRadius(planet.mass) * Constants.DISTANCE_SCALE;
        planetGameObject.transform.localScale = (scale).XYZ();
    }


    LineRenderer line;

    private void TouchInput()
    {
        if(this.ship != null){
            return;
        }

        touches = Input.touches;
        if (Input.touchCount >= 1)
        {
            Touch touch = touches[0];
            Vector2 currentTouchPosition = getWorldPoint(touch.position);

            Debug.Log(currentTouchPosition);

            switch (touch.phase)
            {
                case TouchPhase.Began:
                    createLine();
                    initialTouchPosition = currentTouchPosition;
                    break;
                case TouchPhase.Moved:
                    if (line != null)
                    {
                        Vector2 force = GetForce(currentTouchPosition);
                        Vector2 startPosition = this.level.StartPlanet.position * Constants.DISTANCE_SCALE;
                        line.SetPosition(0, startPosition);
                        line.SetPosition(1, startPosition + force);
                    }
                    break;
                case TouchPhase.Ended:
                    if (line != null) {
                        Destroy(line.gameObject);
                        Fling(GetForce(currentTouchPosition) * Constants.FLING_SCALE);
                    }
                    break;
            }
        }
    }

    private Vector2 GetForce(Vector2 currentTouchPosition)
    {
        Vector2 force = initialTouchPosition - currentTouchPosition;
        Debug.Log("mag: " + force.magnitude + " sqrtmag" + force.sqrMagnitude);
        if (force.sqrMagnitude > Constants.MAX_FORCE * Constants.MAX_FORCE)
        {
            force = force.normalized * Constants.MAX_FORCE;
        }

        return force;
    }

    private void createLine()
    {
        this.line = (new GameObject()).AddComponent<LineRenderer>();
        this.line.material = lineMaterial;
        this.line.startWidth = Constants.LINE_WIDTH;
        this.line.endWidth = Constants.LINE_WIDTH;
        this.line.numCornerVertices = 5;
        this.line.numCapVertices = 10;
        //  this.line.

    }

    private void UpdateTrail(Vector2 flingVector)
    {
        if (this.trailShip != null)
        {
            Destroy(this.trailShip.gameObject);
        }

        CreateTrail(level.StartPlanet);
        this.trailShip.Initialize(level.StartPlanet.position, flingVector);
    }

    private Vector2 getWorldPoint(Vector3 screenPoint)
    {
        if (!Camera.main.orthographic)
        {
            //RaycastHit hit;
            //Physics.Raycast(Camera.main.ScreenPointToRay(screenPoint), out hit);
            //return hit.point;
            screenPoint.z = Camera.main.farClipPlane;
            screenPoint = Camera.main.ScreenToWorldPoint(screenPoint);
            screenPoint.Normalize();
            return screenPoint * 10;
        }
        return Camera.main.ScreenToWorldPoint(screenPoint);
    }
}
