using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using MathNet.Numerics.LinearAlgebra;
using MathNet.Numerics.LinearAlgebra.Double;
using Assets.Scripts.Interface;

public class PlayerMovement : FlatlandMovement,IInteractor,IGraber,IBody
{
    bool isinertial = true;
    bool isGrab = false;
    bool isInteracting = false;
    IInteractable interactiongObject;

    public GameObject background;

    public UIManager uiManager;

    private List<InteractiveObject> interactiveObjects;

    private Vector3 backgroundStartScale;

    // Start is called before the first frame update
    protected override void Start()
    {
        base.Start();
        v = new Vector3(0.0f, 0.0f, 0.0f);
        gamma = 1.0f;
        cnt = 0;
        orientation = new Vector3(0.0f, 0.0f, 1.0f);
        time = 0.0f;
        isinertial = true;
        alpha = new Vector3(0.0f, 0.0f, 0.0f);
        backgroundStartScale = background.transform.localScale;

        interactiveObjects = new List<InteractiveObject>();

    }

    protected override void Update()
    {
        base.Update();

        InteractiveObject obj = TryFindClosestInterativeObject();

        if (obj)
        {
            uiManager.InteractText = "G:"+obj.InteractType;
        }
        else
        {
            uiManager.InteractText = "";
        }


        if (Input.GetKeyDown("g"))
        {
            Interact();
        }
        
    }

    // Update is called once per frame
    protected override void FixedUpdate()
    {
        base.FixedUpdate();

        /*if (v.magnitude > 0.0f)
        {
            Time.fixedDeltaTime = 0.02f / (float)Constants.Gamma(v.magnitude);

            if (Time.fixedDeltaTime < 0.002f)
                Time.fixedDeltaTime = 0.002f;

        }*/

        var prevup = transform.up;
        var prevfor = transform.forward;
        var prevorient = orientation;
        transform.Translate((float)Constants.c * Vector3.up * Time.fixedDeltaTime * (float)gamma, Space.World); // move up by 1 second

        //transform.Translate(Vector3.forward * Time.fixedDeltaTime, Space.World);
        //transform.Translate(0.5f*Vector3.forward * Mathf.Cos(2*Mathf.PI*cnt/480) * Time.fixedDeltaTime, Space.World);
        //transform.Translate(0.5f*Vector3.left * Mathf.Sin(2 * Mathf.PI * cnt / 480) * Time.fixedDeltaTime, Space.World);
        //orientation = Vector3.forward * Mathf.Cos(2 * Mathf.PI * cnt / 480) + Vector3.left * Mathf.Sin(2 * Mathf.PI * cnt / 480);
        //v = (float)beta * Vector3.forward * Mathf.Cos(2 * Mathf.PI * cnt / 480) + (float)beta * Vector3.left * Mathf.Sin(2 * Mathf.PI * cnt / 480);
        //v = alpha.normalized * (alpha.magnitude * Time.time / Mathf.Sqrt(1.0f + (alpha.magnitude * Time.time) * (alpha.magnitude * Time.time)));
        //v = new Vector3(0.7f, 0.0f, 0.0f);

        if (alpha.magnitude > 0.0f)
        {
            isinertial = false;
            var dt = (Constants.c / alpha.magnitude) * MathNet.Numerics.Trig.Sinh(alpha.magnitude * Time.fixedDeltaTime / Constants.c);
            var atmp = (alpha.magnitude * dt)
                / Mathf.Sqrt(1 + (float)((alpha.magnitude * dt / Constants.c) * (alpha.magnitude * dt / Constants.c)));
            //var atmp = alpha.magnitude * Time.fixedDeltaTime;



            double[] vtmp = { (atmp * (alpha.x / alpha.magnitude)), 0.0, (atmp * (alpha.z / alpha.magnitude)) };
            var deltavnaive = V.DenseOfArray(vtmp);

            double[] tmp = {Constants.c * Constants.Gamma(deltavnaive.L2Norm()),
                      deltavnaive[0] * Constants.Gamma(deltavnaive.L2Norm()),
                      deltavnaive[2] * Constants.Gamma(deltavnaive.L2Norm())};

            var deltav = V.DenseOfArray(tmp);


            if (v.magnitude > 0.0f)
            {
                deltav = Constants.BoostMatrix(-v) * deltav;
            }

            var tt = deltav[0] / Constants.c;

            Vector3 finaldeltav = new Vector3((float)(deltav[1] / tt), 0.0f, (float)(deltav[2] / tt));

            var hmm = finaldeltav - v;

            v = finaldeltav;
        }
        else
        {
            isinertial = true;
        }

        if (v.magnitude >= Constants.c) // if floating point errors makes object ftl
            v = v.normalized * (float)(0.999995f * Constants.c);

        beta = v.magnitude / (float)Constants.c;

        gamma = Constants.Gamma(v.magnitude);



        transform.Translate(v * Time.fixedDeltaTime * (float)gamma, Space.World);

        var vt = v + (float)Constants.c * Vector3.up;

        vt.x = -vt.x;

        vt.z = -vt.z;

        vt.Normalize();


        transform.localScale = new Vector3(1.0f, 1.0f, 1.0f); // release child to change x'-axis scale

        var neworientation = (Quaternion.FromToRotation(Vector3.up, vt)) * orientation;

        theobject.transform.rotation = Quaternion.LookRotation(neworientation, vt);
        if (background.GetComponent<BackgroundMovement>().Toggle)
            background.transform.rotation = Quaternion.LookRotation(-vt, neworientation);

        theobject.transform.parent = null;
        if (background.GetComponent<BackgroundMovement>().Toggle)
            background.transform.parent = null;

        Vector3 newforward = new Vector3(vt.y * (v.x / v.magnitude), Mathf.Sqrt(vt.x * vt.x + vt.z * vt.z), vt.y * (v.z / v.magnitude));

        transform.rotation = Quaternion.LookRotation(newforward, vt);

        theobject.transform.parent = transform;
        if (background.GetComponent<BackgroundMovement>().Toggle)
        {
            background.transform.parent = transform;
            background.transform.localScale = backgroundStartScale;
        }

        theobject.transform.localScale = startScale;

        transform.localScale = new Vector3(1.0f, 1.0f, (float)gamma); // scale x'-axis scale (distance dilation)


        time += Time.fixedDeltaTime;
    }

    public bool IsInertial
    {
        get
        {
            return isinertial;
        }       
    }
    //interactor 구현

    public void Interact()
    {
        if (isInteracting)
        {
            OnDisInteract(interactiongObject);
        }
        else
        {
            OnInteract(TryFindClosestInterativeObject());
        }
        
    }

    public IGraber IsGraber
    {
        get
        {
            return this;
        }
    }

    public void OnInteract(IInteractable interactable)
    {
        if(interactable == null)
        {
            return;
        }
        interactiongObject = interactable;
        interactable.OnInteract(this);
    }

    public void OnDisInteract(IInteractable interactable)
    {
        if (interactable == null)
        {
            return;
        }
        interactable.OnDisInteract(this);
    }


    //충돌
    public override void OnCollisionEnterchild(Collision collision)
    {
        base.OnCollisionEnterchild(collision);
    }

    public override void OnCollisionExitchild(Collision collision)
    {
        base.OnCollisionExitchild(collision);
    }

    private InteractiveObject TryFindClosestInterativeObject()
    {
        if(interactiveObjects.Count == 0)
        {
            return null;
        }
        InteractiveObject result = interactiveObjects[0];

        foreach(var obj in interactiveObjects)
        {
            if(SpaceLength(result.transform.position,transform.position) >= SpaceLength(obj.transform.position, transform.position))
            {
                result = obj;
            }
        }

        return result;
    }

    //grab 구현
    public void OnGrab(IGrabable grabed)
    {
        isGrab = true;
        isInteracting = true;
    }


    public void OnDisGrab(IGrabable grabed)
    {
        isGrab = false;
        isInteracting = false;
    }

    public void OnTriggerEnterArm(Collider other)
    {
        if (other.transform.parent != null)
        {
            InteractiveObject x = other.transform.parent.gameObject.GetComponent<InteractiveObject>();
            if (x)
            {
                //상호작용가능한 물건일때
                //Debug.Log(x.InteractType);
                interactiveObjects.Add(x);
                //uiManager.InteractText = x.InteractType;
            }
        }
    }

    public void OnTriggerExitArm(Collider other)
    {
        if (other.transform.parent != null)
        {
            //Debug.Log("Collision");
            InteractiveObject x = other.transform.parent.gameObject.GetComponent<InteractiveObject>();
            if (x)
            {
                //상호작용가능한 물건일때
                //Debug.Log(x.InteractType);
                interactiveObjects.Remove(x);
                //uiManager.InteractText = x.InteractType;
            }
        }
    }

    public Vector3 GraberV
    {
        get
        {
            return v;
        }
    }

    public bool IsGrab
    {
        get
        {
            return isGrab;
        }
    }
}
