using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;

public class EntityController : MonoBehaviour {

    public enum DIRECTION
    {
        LEFT = -1,
        RIGHT = 1,
        NONE = 0,
    }

    public PlayerData data;

    private Rigidbody2D body;
    [HideInInspector] public SpriteRenderer view;
    private Animator viewAnimator;

    private bool jumpForceApplied = false;
    private bool stompForceApplied = false;

    [HideInInspector] public bool isJumping = false;
    [HideInInspector] public bool isGrounded = false;
    [HideInInspector] public bool isStomping = false;
    [HideInInspector] public bool isMoving = false;
    [HideInInspector] public bool isDead = false;

    private float deathTime = 0;
    private float lastShootTime = 0;
    private bool groundCollision = false;

    private DIRECTION sideWallCollision = DIRECTION.NONE;
    private DIRECTION movementDirection = DIRECTION.LEFT;
    private DIRECTION lookDirection = DIRECTION.RIGHT;

    public event Action OnEntityRevive;
    public event Action OnEntityDeath;
    public event Action OnEntityJump;
    public event Action OnEntityStomp;

    [HideInInspector] public Vector3 spawnPosition;
    CameraManager cameraManager;
    private void Awake()
    {
        if ( data == null )
        {
            Debug.Log("This player has no data");
        }

        if (body == null) body = GetComponent<Rigidbody2D>();

        if (data.view != null)
        {
            SpriteRenderer oldRend = GetComponentInChildren<SpriteRenderer>();
            if (oldRend != null) oldRend.gameObject.SetActive(false);

            Transform newView = Instantiate(data.view);
            newView.transform.SetParent(transform);
            newView.transform.localPosition = Vector3.zero;

            view = newView.GetComponent<SpriteRenderer>();
            viewAnimator = newView.GetComponent<Animator>();
        } else
        {
            SpriteRenderer oldRend = GetComponentInChildren<SpriteRenderer>();
            if (oldRend != null)
            {
                view = oldRend;
            }
        }

        cameraManager = FindObjectOfType<CameraManager>();
    }

    void Start ()
    {
        spawnPosition = transform.position;
    }

	void FixedUpdate ()
    {
        if (isDead) return;
        // solve character move direction
        //Vector2 startingPosition = new Vector2(transform.position.x, transform.position.y);
        Vector2 finalVelocity = Vector2.zero;

        // movement
        if (isMoving)
        {
            bool canMove = false;

            // esto previene que dejemos de movernos al tocar una pared
            switch (sideWallCollision)
            {
                case DIRECTION.LEFT:
                    if (movementDirection == DIRECTION.RIGHT) canMove = true;
                    break;
                case DIRECTION.RIGHT:
                    if (movementDirection == DIRECTION.LEFT) canMove = true;
                    break;
                case DIRECTION.NONE:
                    canMove = true;
                    break;
            }

            if (canMove)
            {
                finalVelocity += Vector2.right * (int)movementDirection * data.moveSpeed;
            }
        }

        if (isJumping)
        {
            if (!jumpForceApplied)
            {
                jumpForceApplied = true;
                finalVelocity += Vector2.up * data.jump_height;
            }
        }

        if (isStomping)
        {
            if (!stompForceApplied)
            {
                stompForceApplied = true;
                finalVelocity += Vector2.down * data.stomp_speed;
            }
        }

        // (finalMovementStep.sqrMagnitude > 0)

        finalVelocity.y += body.velocity.y;

        body.velocity = finalVelocity; //* Time.fixedDeltaTime;

        if (cameraManager.IsOutOfBounds(transform.position))
        {
            Death();
        }
    }

    public void Move(float value)
    {
        if (isDead) return;

        //Debug.LogWarning(value);
        if (value > 0)
        {
            isMoving = true;
            movementDirection = DIRECTION.RIGHT;

        }
        else if (value < 0)
        {
            isMoving = true;
            movementDirection = DIRECTION.LEFT;
        }
        else {
            isMoving = false;
            movementDirection = DIRECTION.NONE;
        }
        
        
        if (viewAnimator != null ) viewAnimator.SetBool("isMoving", isMoving);
    }

    public void ToggleLookDirection()
    {
        if (isDead) return;

        lookDirection = lookDirection == DIRECTION.RIGHT ? DIRECTION.LEFT : DIRECTION.RIGHT;
        view.flipX = lookDirection == DIRECTION.LEFT;

        if (data.sfx_step != null) cameraManager.PlaySound(data.sfx_step);
    }

    public void Jump()
    {
        if (isDead) return;

        if (isGrounded && !isJumping)
        {
            if (OnEntityJump != null) OnEntityJump(); 
            isJumping = true;
            if (viewAnimator != null) viewAnimator.SetBool("isJumping", isJumping);
            if (data.sfx_jump != null) cameraManager.PlaySound(data.sfx_jump);
        }
    }

    public void Stomp()
    {
        if (isDead) return;

        if (!isGrounded && !isStomping)
        {
            if (OnEntityStomp != null) OnEntityStomp();
            isStomping = true;
            if (viewAnimator != null) viewAnimator.SetBool("isStomping", isStomping);
            if (data.sfx_stompStart != null) cameraManager.PlaySound(data.sfx_stompStart);

        }
    } 

    public void Shoot()
    {
        if (isDead) return;

        if (Time.time - data.bullet_rate < lastShootTime) return;
        if (viewAnimator != null) viewAnimator.SetTrigger("Fire");
        lastShootTime = Time.time;
        Transform t = Instantiate(data.bullet);
        Vector2 spawnPosition = new Vector2(transform.position.x, transform.position.y + view.GetComponent<Collider2D>().bounds.size.y / 2) + (Vector2.right * (int)lookDirection );
        t.position = spawnPosition;
        BulletController bullet = t.GetComponent<BulletController>();
        bullet.direction = lookDirection;
        bullet.owner = this;

        if (data.vfx_shoot != null)
        {
            Instantiate(data.vfx_shoot, spawnPosition, Quaternion.identity, null);
        }
        if (data.sfx_shoot != null) cameraManager.PlaySound(data.sfx_shoot);
    }

    public void Death()
    {
        deathTime = Time.time;
        isDead = true;
        view.gameObject.SetActive(false);

        ResetMovementFlags();

        if (OnEntityDeath != null) OnEntityDeath();

        if (data.vfx_death != null)
        {
            Instantiate(data.vfx_death, transform.position, Quaternion.identity, null);
        }

        if (data.sfx_death != null) cameraManager.PlaySound(data.sfx_death);

    }

    public void Revive()
    {
        isDead = false;
        view.gameObject.SetActive(true);
        body.position = spawnPosition;

        if (OnEntityRevive != null) OnEntityRevive();

        if (cameraManager.IsOutOfBounds(spawnPosition))
        {
            cameraManager.UI.SetWindow(UI_TYPE.RESTART, true);
        }
    }

    public void Update()
    {
        if (isDead && data.respawnOnDeath)
        {
            if (Time.time - data.respawnTimer > deathTime)
            {
                Revive();
            }
        }
    }


    ContactPoint2D[] collisionContacts;

    public void OnCollisionStay2D(Collision2D collision)
    {
        EvaluateCollision(collision);
    }
    public void OnCollisionEnter2D(Collision2D collision)
    {
        EvaluateCollision(collision);
    }
    public void OnCollisionExit2D(Collision2D collision)
    {
        EvaluateCollision(collision);
    }

    private void EvaluateCollision(Collision2D collision)
    {
        if (collisionContacts == null) collisionContacts = new ContactPoint2D[10];
        sideWallCollision = DIRECTION.NONE;

        if (collision.collider.gameObject.layer == LayerMask.NameToLayer("DEATH"))
        {
            EntityController entity = collision.collider.GetComponentInParent<EntityController>();
            BulletController bullet = collision.collider.GetComponentInParent<BulletController>();

            if (entity != null)
            {
                if (isStomping && entity.data.canBeStomped)
                {
                    entity.Death();
                } else
                {
                    Death();
                    return;
                }
            } else if (bullet != null && !data.deflectBullets)
            {
                Debug.Log("Boing");
                //bullet.direction = bullet.direction == DIRECTION.LEFT ? DIRECTION.RIGHT : DIRECTION.LEFT;
            } else
            {
                Death();
                return;
            }

        }

        for (int i = 0; i < collisionContacts.Length; i++)
        {
            collisionContacts[i] = new ContactPoint2D();
        }

        if (collision.GetContacts(collisionContacts) > 0)
        {
            for (int i = 0; i < collisionContacts.Length; i++)
            {
                ContactPoint2D contact = collisionContacts[i];
                if (!contact.enabled) continue;

                float upAgle = Vector2.Angle(contact.normal, Vector2.up);
                float rightAngle = Vector2.Angle(contact.normal, Vector2.right);
                float leftAngle = Vector2.Angle(contact.normal, Vector2.left);

                if (upAgle <= 15f && !isGrounded)
                {
                    if (isStomping)
                    {
                        if (data.sfx_stompEnd != null) cameraManager.PlaySound(data.sfx_stompEnd);

                        if (data.vfx_stomp)
                        {
                            Instantiate(data.vfx_stomp, contact.point, Quaternion.identity, null);
                        }
                        
                        TilemapCollisionManager.i.DestroyTileAt(contact.point - contact.normal * 0.05f);
                    }


                    groundCollision = true;
                }

                if (rightAngle <= 0.5f)
                {
                    sideWallCollision = DIRECTION.LEFT;
                }
                else if ( leftAngle <= 0.5f)
                {
                    sideWallCollision = DIRECTION.RIGHT;
                }
            }
        }
        else
        {
            isGrounded = false;
        }
    }

    private void LateUpdate()
    {
        if (groundCollision)
        {
            if (data.sfx_grounded != null) cameraManager.PlaySound(data.sfx_grounded);

            GroundCollision();
            groundCollision = false;
        }
    }

    private void GroundCollision()
    {
        isGrounded = true;
        isJumping = false;
        isStomping = false;
        jumpForceApplied = false;
        stompForceApplied = false;

        if (viewAnimator && view.gameObject.activeSelf) {
            viewAnimator.SetBool("isJumping", isJumping);
            viewAnimator.SetBool("isStomping", isStomping);
        }
        
    }

    private void ResetMovementFlags()
    {
        isGrounded = false;
        isJumping = false;
        isStomping = false;
        jumpForceApplied = false;
        stompForceApplied = false;
        body.velocity = Vector2.zero;
        
    }

}
