using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerController : MonoBehaviour{
    const string GROUND = "Ground";
    const int MAXHP = 200;
    const int MAXFRAMENOTGROUNDED = 180;
    const int MAXDIST = 46;

    public float movementSpeed = 1;
    public float rotationSpeed = 1;
    public float baseRotationSpeed = 1;
    public float aimRadiusMin = 2;
    public float aimRadiusMax = 7;
    [Space]
    public Transform shootingTower;
    public Transform rotatingBase;
    public Transform aimTarget;
    public Transform armFire;
    public Transform middleFirePoint;
    [Space]
    public Weapon weapon; 
    public GameObject particles;
    public GameObject damageParticle;
    public float coolDown = 0.5f;
    public float distToGround = 0.2f;
    public int id = -1;  
    
    
    InputMaster inputs;
    Rigidbody rb;
    GameObject weaponObject;
    Transform camTransform;

    Vector3 movementDir;
    Quaternion baseRotation;
    Vector3 targetPos;

    Vector3 worldOrigin;

    int hp;
    float cooldownRem = 0;
    bool isGrounded = false;

    int countFrame = 0;

    void Awake() {
        inputs = new InputMaster();
    }

    void Start() {
        rb = GetComponent<Rigidbody>();   
        camTransform = LevelManager.Instance.cam; 
        worldOrigin = LevelManager.Instance.gameObject.transform.position;

        baseRotation = Quaternion.identity;
        hp = MAXHP;
        inputs.Enable(); 

    }

    void OnEnable() {
        inputs.Enable();
    }

    void OnDisable() {
        inputs.Disable();    
    }


    public void Update(){

        float d = Vector3.Distance(transform.position, worldOrigin);

        if(countFrame >= MAXFRAMENOTGROUNDED){
            die();
        }

        if(!isGrounded && d >= MAXDIST){
            countFrame ++;
        }else{
            countFrame = 0;
        }

        
        transform.position += movementDir * Time.deltaTime * movementSpeed;
        var t = Mathf.Clamp01(Time.deltaTime * rotationSpeed);
        if (t>0)
            rotatingBase.rotation = Quaternion.Lerp(rotatingBase.rotation, baseRotation, t);

        Vector3 nextPos = aimTarget.position + targetPos * Time.deltaTime * rotationSpeed;
        float distanceFromTarget = Vector3.Distance(transform.position, nextPos);
        if( distanceFromTarget < aimRadiusMax && distanceFromTarget > aimRadiusMin){
            aimTarget.position = nextPos;
        }else{
            if(distanceFromTarget < aimRadiusMin){
                aimTarget.position = nextPos - (targetPos) * -1;
            }else if (distanceFromTarget > aimRadiusMax){
                var heading = nextPos - transform.position;
                var distance = heading.magnitude;
                var direction = heading / distance;
                aimTarget.position = transform.position + direction * aimRadiusMax;
            }
        }
        shootingTower.LookAt(aimTarget.position);
        
        

        cooldownRem -= Time.deltaTime;
        
    }

    Vector3 getCameraRelativeDirection(Vector2 dir){
        if(camTransform!= null){
            Vector2 direction = new Vector2(dir.x, dir.y);
            direction = Vector2.ClampMagnitude(direction, 1);

            Vector3 camF = camTransform.forward;
            Vector3 camR = camTransform.right;

            camF.y = 0;
            camR.y = 0;

            camF = camF.normalized;
            camR = camR.normalized;

            return camF * direction.y + camR * direction.x;
        }
        return Vector3.zero;
    }


    void OnMovement(InputValue iv){
        movementDir = getCameraRelativeDirection(iv.Get<Vector2>());
        var relativeDir = movementDir;
        
        relativeDir.y = 0; 

        if(relativeDir != Vector3.zero){
            baseRotation = Quaternion.LookRotation(relativeDir);
        }
    }

    void OnAim(InputValue iv){
        targetPos = getCameraRelativeDirection(iv.Get<Vector2>());
    }
    
    void OnShoot(){
        if(cooldownRem <= 0){
            if(weapon != null){
                weapon.shoot(
                    armFire.position,
                    middleFirePoint.position,
                    aimTarget.position,
                    id
                );
                cooldownRem = coolDown;
            }
        }
    }

    void OnTriggerEnter(Collider other) {
        var tempWeapon = other.gameObject.GetComponent<Weapon>();
        if(tempWeapon != null){
            if(weapon != null){
                Destroy(weaponObject);
            }

            weaponObject = Instantiate(tempWeapon.weaponPrefab, armFire);
            weaponObject.transform.position = armFire.position;
            weapon = tempWeapon;

            other.gameObject.SetActive(false);
        }
    }

     void OnCollisionEnter(Collision theCollision){
        if (theCollision.collider.CompareTag(GROUND)){
            isGrounded = true;
        }
    }
 
    void OnCollisionExit(Collision theCollision){
        if (theCollision.collider.CompareTag(GROUND)){
                isGrounded = false;
            }
    }

    void OnTriggerExit(Collider other){
        if (other.CompareTag(GROUND)){
                isGrounded = false;
            }
    }

    public void takeDamage(int amount){
        hp -= amount;

        GameObject p = Instantiate(damageParticle, transform.position + Vector3.up * 3, Quaternion.identity);
        Destroy(p, 1);

        if(hp <= 0){
            die();
        }
    }

    public void repair(int amount){
        hp += amount;
        if(hp <= MAXHP){
            hp = MAXHP;
        }
    }

    public void die(){
        GameObject p = Instantiate(particles, transform.position + Vector3.up, Quaternion.identity);
        Destroy(p, 3);
        Destroy(gameObject);
        LevelManager.Instance.gameOver(id);
    }


    public Vector3 clampMagnitude(Vector3 v, float min, float max){
        double sm = v.sqrMagnitude;
        if(sm > (double)max * (double)max) return v.normalized * max;
        else if(sm < (double)min * (double)min) return v.normalized * min;
        return v;
    }

}
