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

[SelectionBase]
public class PlayerController : MonoBehaviour {
    public float Speed = 5;
    public float Gravity = 7;

    public enum State
    {
        Walking,
        Raking
    }

    private State state = State.Walking;

    private CharacterController charC;
    private AnimationHandler anim;
	// Use this for initialization
	void Start () {
        charC = GetComponent<CharacterController>();
        anim = GetComponent<AnimationHandler>();
	}

    private float velY = 0;

    public float RunCycleTime = 0.8f;
    public float RunFrameTime = 0.3f;

    public float RunFrameSpeedBoost = 1.4f;

    private float runCycle = 0.0f;

    public float PushRadius = 0.6f;
    public float PushForce = 300.0f;
    public float PushPower = 2.0f;

    private float rakeTime = 0.0f;
    public float TimeToPushRake = 0.3f;
	
	// Update is called once per frame
	void Update () {
        Vector3 pos = transform.position;

        Vector3 dir = new Vector3();
        if (input != null)
        {
            if (charC.isGrounded && state == State.Walking)
            {
                dir.x = input.Axis(InputSource<PlayerController>.InputAxis.MoveX);
                dir.z = input.Axis(InputSource<PlayerController>.InputAxis.MoveY);

                if (dir.magnitude > 0.3f)
                {
                    dir.Normalize();
                    float speed = Speed;
                    if (runCycle < RunFrameTime)
                    {
                        speed *= RunFrameSpeedBoost;
                    }
                    dir *= speed;

                    float angle = Mathf.Atan2(dir.z, dir.x);

                    if (angle < 0) angle += Mathf.PI * 2;

                    if (angle < Mathf.PI / 2.0f) anim.SetDir(AnimationHandler.Direction.UpRight);
                    if (angle < Mathf.PI && angle >= Mathf.PI / 2.0f) anim.SetDir(AnimationHandler.Direction.UpLeft);
                    if (angle < Mathf.PI * 3.0f / 2.0f && angle >= Mathf.PI) anim.SetDir(AnimationHandler.Direction.DownLeft);
                    if (angle >= Mathf.PI * 3.0f / 2.0f) anim.SetDir(AnimationHandler.Direction.DownRight);

                    //Debug.Log("ANGLE: " + angle);
                    runCycle += Time.deltaTime;
                    while (runCycle >= RunCycleTime)
                    {
                        runCycle -= RunCycleTime;
                    }
                    int frame = runCycle < RunFrameTime ? 1 : 0;
                    anim.SetFrame(frame);
                }
                else
                {
                    dir.x = 0;
                    dir.z = 0;

                    anim.SetFrame(0);
                    runCycle = 0.0f;
                }
            }
        }

        dir.y = velY;

        velY -= Gravity * Time.deltaTime;

        gameObject.layer = LayerMask.NameToLayer("player_controller_no_collide");
        charC.Move(dir * Time.deltaTime);
        gameObject.layer = LayerMask.NameToLayer("player_controller");

        if (charC.isGrounded)
        {
            velY = 0;
        }

        Vector3 moved = transform.position - pos;
        if (moved.magnitude > 0.7f * Time.deltaTime)
        {
            int layer = LayerMask.GetMask("leaf");
            foreach (Collider c in Physics.OverlapSphere(transform.position, PushRadius, layer))
            {
                LeafPhysics leaf = c.GetComponentInParent<LeafPhysics>();
                if (leaf != null)
                {
                    Vector3 toLeaf = leaf.transform.position - transform.position;
                    float dist = toLeaf.magnitude;
                    float power = Mathf.Pow(1.0f - dist / PushRadius, PushPower) * Time.deltaTime * PushForce;
                    //toLeaf.Normalize();
                    toLeaf.y += UnityEngine.Random.Range(0.4f, 1.0f);
                    leaf.Body.AddForce(toLeaf * (9.0f + UnityEngine.Random.Range(0, 9.0f)) * power);
                }
            }
        }

        if (state == State.Walking)
        {
            Vector2 rake = new Vector2(input.Axis(InputSource<PlayerController>.InputAxis.RakeX), input.Axis(InputSource<PlayerController>.InputAxis.RakeY));
            if (rake.magnitude < 0.2f)
            {
                rakeTime = 0;
            }
            else
            {
                rakeTime += Time.deltaTime;
            }

            if (rake.magnitude > 0.7f && rakeTime < TimeToPushRake)
            {
                state = State.Raking;

                float angle = Mathf.Atan2(rake.y, rake.x);

                if (angle < 0) angle += Mathf.PI * 2;

                AnimationHandler.Direction rakeDir = AnimationHandler.Direction.UpRight;

                if (angle < Mathf.PI / 2.0f) rakeDir = AnimationHandler.Direction.UpRight;
                if (angle < Mathf.PI && angle >= Mathf.PI / 2.0f) rakeDir = AnimationHandler.Direction.UpLeft;
                if (angle < Mathf.PI * 3.0f / 2.0f && angle >= Mathf.PI) rakeDir = AnimationHandler.Direction.DownLeft;
                if (angle >= Mathf.PI * 3.0f / 2.0f) rakeDir = AnimationHandler.Direction.DownRight;

                anim.SetState("Rake", rakeDir, 0);
            }
        }
	}

    private InputSource<PlayerController> input = null;
    private InputManager.PlayerInfo playerInfo;
    public void SetInput(InputSource<PlayerController> newPlayerInput, InputManager.PlayerInfo playerInfo)
    {
        this.input = newPlayerInput;
        this.playerInfo = playerInfo;
    }
}
