using System.Collections.Generic;
using UnityEngine;

public class WormController : Simpleton<WormController> {
    public Mode GameMode;

    public enum Mode {
        Wave,
        Fire
    }

    public float RingsDistnace = 0.7f;
    public GameObject Head1Prefab;
    public GameObject Head2Prefab;
    public GameObject RingPrefab;
    public GameObject TailPrefab;
    public Sprite SelectedWormTexture;
    public int NumRings;

	// Wave controls.
    public float WaveSpeed;
    public float WaveForwardInflunceSpeed;
    public float MinWaveHeight;
    public float MaxWaveForce = 1800f;
    public float TimeToPassAllRings = 5f;
    private float spaceDownTime;
    private int preLunchableRing;
    private GameObject tail;
    private Vector3 headPosition;

    private GameObject[] rings;
    private List<Rigidbody2D> wormParts;
    private GameObject head;

    // Fire mode controls.
    public GameObject FireWormPrefab;
    public GameObject ProjectilePrefab;
    public int Steps = 100;
    private int currentStep;
    public int CurrentAngle { get; private set; }

	public void Start() {
        if (GameMode == Mode.Wave) {
            this.rings = new GameObject[NumRings];
            this.wormParts = new List<Rigidbody2D>();
            SpawnRings();
            SpawnHeadAndTail();
        } else {
            this.head = Instantiate(FireWormPrefab, Camera.main.ViewportToWorldPoint(new Vector3(.5f, 0, 10)), transform.rotation, transform);
            this.head.transform.localScale *= .5f;
            InvokeRepeating("RotateGun", 0, .01f);
        }
	}

	private void SpawnHeadAndTail() {
        var tailPosition = Camera.main.ViewportToWorldPoint(new Vector3(.05f, .1f, 10));
		tail = Instantiate<GameObject>(TailPrefab, tailPosition, transform.rotation, transform);
		tail.GetComponent<SpriteRenderer>().sortingOrder = -1;

        this.headPosition = rings[rings.Length-1].transform.position+ Vector3.up*0.7f+ Vector3.right*(RingsDistnace+1f);
		head = Instantiate<GameObject>(Head1Prefab, this.headPosition, transform.rotation, transform);
		head.GetComponent<SpriteRenderer>().sortingOrder = NumRings;
	}

    public void ReplaceToAngryHead() {
       // Instantiate(Head2Prefab, head.transform.position, transform.rotation, transform);
        head.GetComponent<SpriteRenderer>().sprite = Head2Prefab.GetComponent<SpriteRenderer>().sprite;
        Invoke("ReplaceToNormalHead", 1);
    }
    public void ReplaceToNormalHead()
    {
        // Instantiate(Head2Prefab, head.transform.position, transform.rotation, transform);
        head.GetComponent<SpriteRenderer>().sprite = Head1Prefab.GetComponent<SpriteRenderer>().sprite;
    }
    private void SpawnRings() {
		for (int i = 0; i < NumRings; i++) {
			var position = Camera.main.ViewportToWorldPoint(new Vector3(.06f + RingsDistnace * (i + 1) / NumRings, .1f, 10));
			this.rings[i] = Instantiate(RingPrefab, position, transform.rotation, transform);
			this.rings[i].GetComponent<SpriteRenderer>().sortingOrder = i;
			wormParts.Add(rings[i].GetComponent<Rigidbody2D>());
            // this.rings[i].SetActive(false);  // TODO: Uncomment this line and open Main2 to see the sine wave.
		}
	}

    public void Update() {
        if (GameMode == Mode.Wave) {
            UpdateWave();
        } else {
            UpdateFire();
        }
    }
    private int UpdateLunchableRing()
    {
        rings[preLunchableRing].GetComponent<SpriteRenderer>().sprite= RingPrefab.GetComponent<SpriteRenderer>().sprite;
        preLunchableRing = (int)((NumRings) * (Time.fixedTime % TimeToPassAllRings) / TimeToPassAllRings);
        rings[preLunchableRing].GetComponent<SpriteRenderer>().sprite = SelectedWormTexture;
        return preLunchableRing;
    }

    // Used for GameMode == Mode.Wave.
    private void UpdateWave() {
        float tailHeightDifference = tail.transform.position.y - wormParts[0].transform.position.y;
        wormParts[0].AddForce(new Vector2(0, tailHeightDifference * Time.deltaTime * WaveSpeed * 2f));
        float headHeightDifference = headPosition.y - 2f - wormParts[wormParts.Count - 1].transform.position.y;
        wormParts[wormParts.Count - 1].AddForce(new Vector2(0, headHeightDifference * Time.deltaTime * WaveSpeed * 2f));
        float totalHeightDifference = 0;
        for (int i = 1; i < wormParts.Count; i++)
        {
            float heightDifference = wormParts[i - 1].transform.position.y - wormParts[i].transform.position.y;
            totalHeightDifference += Mathf.Abs(heightDifference);
            //  if (i > 6)
            //    {
            wormParts[i - 1].AddForce(new Vector2(0, -heightDifference * Time.deltaTime * WaveSpeed));
            //  }
            wormParts[i].AddForce(new Vector2(0, heightDifference * Time.deltaTime * WaveSpeed + wormParts[i - 1].velocity.y * WaveForwardInflunceSpeed));
            //    WormParts[i + 1].AddForce(new Vector2(0, WormParts[i].position.y* Time.deltaTime* WaveForwardInflunceSpeed));
        }
        //  print(tailHeightDifference / WormParts.Count);
        for (int i = 0; i < wormParts.Count; i++)
        {
            wormParts[i].position = new Vector2(wormParts[0].position.x + (0.36f - (totalHeightDifference / wormParts.Count) * 0.1f) * i, wormParts[i].position.y);
        }
        head.transform.position = new Vector3(rings[rings.Length - 1].transform.position.x+0.2f,(headPosition.y-0.8f), head.transform.position.z);
        if (Input.GetKeyDown(KeyCode.Space) || Input.GetMouseButtonDown(0))
        {
            spaceDownTime = Time.fixedTime;
        }
        UpdateLunchableRing();//do not delete this does something;
        if (Input.GetKeyUp(KeyCode.Space) || Input.GetMouseButtonDown(0))
        {
            StartWave();
        }
    }

    private void StartWave()
    {
        wormParts[Mathf.Clamp(preLunchableRing + 3, 0, NumRings - 1)].AddForce(Vector2.up * (Mathf.Min(MaxWaveForce, 1000f * (MinWaveHeight + Time.fixedTime - spaceDownTime))));
    }

    private void RotateGun() {
        this.currentStep = (this.currentStep + 1) % Steps;
        var rotateBetween0And1 = Mathf.Sin(Mathf.PI / Steps * this.currentStep);
        CurrentAngle = (int) (rotateBetween0And1 * 90 - 45);  // -45 to 45.
        Debug.Log(CurrentAngle);
        this.head.transform.rotation = Quaternion.AngleAxis(CurrentAngle + 70, Vector3.forward);
    }

    // Used for GameMode == Mode.Fire.
    private void UpdateFire() {
        if (Input.GetKeyDown(KeyCode.Space) || Input.GetMouseButtonDown(0)) {
            var projectile = Instantiate(ProjectilePrefab, this.head.transform.position, Quaternion.AngleAxis(CurrentAngle + 45f, Vector3.forward), transform);
            projectile.GetComponent<Rigidbody2D>().AddForce(Quaternion.AngleAxis(CurrentAngle + 90, Vector3.forward) * Vector3.right * 1000);
        }
    }
}