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

namespace mgaia.Utils.LayerSystem
{
    public class ScrollingArea : MonoBehaviour
    {
        private const float ATENUATOR_BEGINNING_VALUE = 0.8f; //1.0f
        private const float ATENUATOR_MIN_VALUE = 0.4f; //0.5f
        private const float OVERFLOW_DIVISOR = 2.0f; //3.0f
        private const float DEFAULT_ABS_DECELERATION = 600; //200
        private const float KEEP_HISTORY_TIME = 0.15f; //0.25f
        private const float UPDATE_ARRAY_TIME = 1.0f;
        private const float MIN_MOVEMENT = 0.05f; //0.1f
        private const int MIN_ARRAY_VALUE_QTT = 2;

        [SerializeField]
        private GameObject ScrollAreaBottomLeft;
        [SerializeField]
        private GameObject ScrollAreaTopRight;
        [SerializeField]
        private bool HorizontalScrollEnabled = true;
        [SerializeField]
        private bool VerticalScrollEnabled = true;
        [SerializeField]
        private bool SpringEffectEnabled = true;
        [SerializeField]
        private float AbsoluteDeceleration = DEFAULT_ABS_DECELERATION;

        private Vector3 mMaxPosition;
        private Vector3 mMinPosition;
        private float mCurrentVerticalAbsDeceleration = 200, mCurrentHorizontalAbsDeceleration = 200;
        private float mVerticalSpeed = 0.0f, mHorizontalSpeed = 0.0f;
        private float mSpeedVerticalSign, mSpeedHorizontalSign;
        private Vector3? mTouchBeganPosition = null;
        private Vector3 mTransfLocalPositionOnTouchBegan;
        private bool mMovementBegan = false;
        private float mElapsed, mAuxiliarElapsed;
        private float mLastOverflowValue_Vertical, mLastOverflowValue_Horizontal;
        private List<KeyValuePair<float, Vector3>> mTimePositionArray = new List<KeyValuePair<float, Vector3>>();
        private bool mAlreadyRecalculatedVerticalDeceleration = false, mAlreadyRecalculatedVerticalSpeed = true;
        private bool mAlreadyRecalculatedHorizontalDeceleration = false, mAlreadyRecalculatedHorizontalSpeed = true;
        private bool mIsTouching = false;

        private void Start()
        {
            Assert.AssertTrue(AbsoluteDeceleration.Compare(0.0f) >= 0, "Absolute Deceleration value must be >= 0");
            Assert.AssertTrue(HorizontalScrollEnabled || VerticalScrollEnabled, "At least one of the scroll flags must be true.");
            Assert.AssertTrue(ScrollAreaTopRight != null && ScrollAreaBottomLeft != null, "Some gameobject is missing its assignment");
            Assert.AssertTrue(ScrollAreaTopRight.transform.parent == transform.parent && ScrollAreaBottomLeft.transform.parent == transform.parent, "The Area Bottom and Top objects must be brothers of the ScrollingArea object");
            Assert.AssertTrue(
                ScrollAreaTopRight.transform.localPosition.y.Compare(ScrollAreaBottomLeft.transform.localPosition.y) >= 0 && ScrollAreaTopRight.transform.localPosition.x.Compare(ScrollAreaBottomLeft.transform.localPosition.x) >= 0,
                "TopAreaPosition must be higher than or equals to BottomAreaPosition.");


            Vector3 scrollingAreaTotalSize = gameObject.GetSize(true, true);
            Vector3 scrollingMaxViewSize = new Vector3
            (
                Mathf.Abs(ScrollAreaTopRight.transform.localPosition.x - ScrollAreaBottomLeft.transform.localPosition.x),
                Mathf.Abs(ScrollAreaTopRight.transform.localPosition.y - ScrollAreaBottomLeft.transform.localPosition.y),
                0.0f
            );
            Vector3 scrollingEffectiveSize = new Vector3
            (
                Mathf.Min(scrollingAreaTotalSize.x, scrollingMaxViewSize.x),
                Mathf.Min(scrollingAreaTotalSize.y, scrollingMaxViewSize.y),
                0.0f
            );
            Vector3 scrollingAreaExtraSize = scrollingAreaTotalSize - scrollingMaxViewSize;
            mMinPosition = ScrollAreaTopRight.transform.localPosition - GetTopRightAreaBorder();
            mMaxPosition = mMinPosition + new Vector3
            (
                 (scrollingAreaExtraSize.x.Compare(0.0f) == 1 ? scrollingAreaExtraSize.x : 0.0f),
                 (scrollingAreaExtraSize.y.Compare(0.0f) == 1 ? scrollingAreaExtraSize.y : 0.0f),
                 0.0f
            );

            BoxCollider collider = gameObject.AddComponent<BoxCollider>();
            collider.size = new Vector3(scrollingAreaTotalSize.x, scrollingAreaTotalSize.y, 1.0f);
            collider.center = new Vector3
            (
                GetTopRightAreaBorder().x - scrollingAreaTotalSize.x / 2,
                GetTopRightAreaBorder().y - scrollingAreaTotalSize.y / 2,
                0.0f
            );

            Assert.AssertTrue(mMaxPosition.x.Compare(mMinPosition.x) >= 0 && mMaxPosition.y.Compare(mMinPosition.y) >= 0, "MaxPosition must be higher than or equals to MinPosition");
        }

        private Vector3 GetTopRightAreaBorder()
        {
            float maxX, maxY;
            maxX = maxY = Mathf.NegativeInfinity;
            GetComponentsInChildren<MeshRenderer>().ForEach(meshRenderer =>
            {
                Vector3 meshTopRight = meshRenderer.bounds.max;
                Vector3 meshTopRightLocalPosition = transform.InverseTransformPoint(meshTopRight);
                maxX = Mathf.Max(maxX, meshTopRightLocalPosition.x);
                maxY = Mathf.Max(maxY, meshTopRightLocalPosition.y);
            });
            return new Vector3(maxX, maxY, transform.localPosition.z);
        }

        private Vector3 GetBottomLeftAreaBorder()
        {
            float minX, minY;
            minX = minY = Mathf.Infinity;
            GetComponentsInChildren<MeshRenderer>().ForEach(meshRenderer =>
            {
                Vector3 meshBottomLeft = meshRenderer.bounds.min;
                Vector3 meshBottomLeftLocalPosition = transform.InverseTransformPoint(meshBottomLeft);
                minX = Mathf.Min(minX, meshBottomLeftLocalPosition.x);
                minY = Mathf.Min(minY, meshBottomLeftLocalPosition.y);
            });
            return new Vector3(minX, minY, transform.localPosition.z);
        }

        private float GetAbsMaxAtenuatedOverflow()
        {
            return -OVERFLOW_DIVISOR * (ATENUATOR_MIN_VALUE - ATENUATOR_BEGINNING_VALUE) * ATENUATOR_MIN_VALUE;
        }

        public void OnTouchBegan(Vector3 position, Vector3 hitPosition, bool isAnyButtonPressed)
        {
            Assert.AssertTrue(mTouchBeganPosition == null, "ScrollingArea received a TouchBegan call while another touch was already being processed");
            mTouchBeganPosition = position;
            mTransfLocalPositionOnTouchBegan = transform.localPosition;
            mVerticalSpeed = mHorizontalSpeed = 0.0f;
            mCurrentVerticalAbsDeceleration = mCurrentHorizontalAbsDeceleration = AbsoluteDeceleration;
            mAlreadyRecalculatedVerticalDeceleration = mAlreadyRecalculatedHorizontalDeceleration = false;
            mAlreadyRecalculatedVerticalSpeed = mAlreadyRecalculatedHorizontalSpeed = true;
            mIsTouching = true;
            mElapsed = 0.0f;
            mAuxiliarElapsed = 0.0f;
            mTimePositionArray.Clear();
            mTimePositionArray.Add(new KeyValuePair<float, Vector3>(mElapsed, position));
            mMovementBegan = !isAnyButtonPressed;
        }

        /// <summary>
        /// Returns true if the touch movement caused the scroll movement to begin, false otherwise.
        /// </summary>
        public bool OnTouchMoved(Vector3 position)
        {
            Assert.AssertTrue(mTouchBeganPosition != null, "ScrollingArea received a TouchMoved call without receiving the TouchBegan first");
            Vector3 distanceFromTouchBegan = CalculateDistance(position, mTouchBeganPosition.Value);
            if (!mMovementBegan)
            {
                bool hMovement = (Mathf.Abs(distanceFromTouchBegan.x).Compare(MIN_MOVEMENT) >= 0) && HorizontalScrollEnabled;
                bool vMovement = (Mathf.Abs(distanceFromTouchBegan.y).Compare(MIN_MOVEMENT) >= 0) && VerticalScrollEnabled;

                if (hMovement || vMovement)
                {
                    mMovementBegan = true;
                    transform.localPosition = new Vector3
                    (
                        HorizontalScrollEnabled ? Mathf.Clamp(transform.localPosition.x + distanceFromTouchBegan.x, mMinPosition.x, mMaxPosition.x) : transform.localPosition.x,
                        VerticalScrollEnabled ? Mathf.Clamp(transform.localPosition.y + distanceFromTouchBegan.y, mMinPosition.y, mMaxPosition.y) : transform.localPosition.y,
                        transform.localPosition.z
                    );
                    return true;
                }
            }
            else if (mMovementBegan)
            {
                Vector3 newPosition = mTransfLocalPositionOnTouchBegan + distanceFromTouchBegan;

                float overflowValueX = 0.0f, atenuatorX = 0.0f, atenuatedOverflowX = 0.0f;
                if (newPosition.x.Compare(mMaxPosition.x) > 0)
                {
                    overflowValueX = newPosition.x - mMaxPosition.x;
                    newPosition.x = newPosition.x - overflowValueX;
                    atenuatorX = ATENUATOR_BEGINNING_VALUE - overflowValueX / OVERFLOW_DIVISOR;
                    atenuatedOverflowX = overflowValueX * atenuatorX;
                    if (atenuatorX <= ATENUATOR_MIN_VALUE)
                    {
                        atenuatedOverflowX = GetAbsMaxAtenuatedOverflow();
                    }
                }
                else if (newPosition.x.Compare(mMinPosition.x) < 0)
                {
                    overflowValueX = newPosition.x - mMinPosition.x;
                    newPosition.x = newPosition.x - overflowValueX;
                    atenuatorX = ATENUATOR_BEGINNING_VALUE + overflowValueX / OVERFLOW_DIVISOR;
                    atenuatedOverflowX = overflowValueX * atenuatorX;
                    if (atenuatorX <= ATENUATOR_MIN_VALUE)
                    {
                        atenuatedOverflowX = -GetAbsMaxAtenuatedOverflow();
                    }
                }

                float overflowValueY = 0.0f, atenuatorY = 0.0f, atenuatedOverflowY = 0.0f;
                if (newPosition.y.Compare(mMaxPosition.y) > 0)
                {
                    overflowValueY = newPosition.y - mMaxPosition.y;
                    newPosition.y = newPosition.y - overflowValueY;
                    atenuatorY = ATENUATOR_BEGINNING_VALUE - overflowValueY / OVERFLOW_DIVISOR;
                    atenuatedOverflowY = overflowValueY * atenuatorY;
                    if (atenuatorY <= ATENUATOR_MIN_VALUE)
                    {
                        atenuatedOverflowY = GetAbsMaxAtenuatedOverflow();
                    }
                }
                else if (newPosition.y.Compare(mMinPosition.y) < 0)
                {
                    overflowValueY = newPosition.y - mMinPosition.y;
                    newPosition.y = newPosition.y - overflowValueY;
                    atenuatorY = ATENUATOR_BEGINNING_VALUE + overflowValueY / OVERFLOW_DIVISOR;
                    atenuatedOverflowY = overflowValueY * atenuatorY;
                    if (atenuatorY <= ATENUATOR_MIN_VALUE)
                    {
                        atenuatedOverflowY = -GetAbsMaxAtenuatedOverflow();
                    }
                }

                if (!SpringEffectEnabled)
                {
                    atenuatedOverflowX = atenuatedOverflowY = 0.0f;
                }

                transform.localPosition = new Vector3
                (
                    HorizontalScrollEnabled ? (newPosition.x + atenuatedOverflowX) : transform.localPosition.x,
                    VerticalScrollEnabled ? (newPosition.y + atenuatedOverflowY) : transform.localPosition.y,
                    transform.localPosition.z
                );
            }

            if (mTimePositionArray.Count > 0)
            {
                bool flag1 = VerticalScrollEnabled && !mTimePositionArray[mTimePositionArray.Count - 1].Value.y.Aproximate(position.y);
                bool flag2 = HorizontalScrollEnabled && !mTimePositionArray[mTimePositionArray.Count - 1].Value.x.Aproximate(position.x);
                if (mTimePositionArray.Count == 0 || (flag1 || flag2))
                {   // adiciona se tiver havido movimento
                    mTimePositionArray.Add(new KeyValuePair<float, Vector3>(mElapsed, position));
                }
            }
            return false;
        }

        public void OnTouchEnded(Vector3 position)
        {
            Assert.AssertTrue(mTouchBeganPosition != null, "ScrollingArea received a OnTouchEnded call without receiving the TouchBegan first");
            mIsTouching = false;
            mTouchBeganPosition = null;
            mMovementBegan = false;
            UpdateTimePositionsArray();
            if (VerticalScrollEnabled)
            {
                if (transform.localPosition.y.Compare(mMaxPosition.y) > 0)
                {
                    float deltaS = (mMaxPosition.y - transform.localPosition.y) / LayerManager.ScreenHeightNormalizer;
                    mVerticalSpeed = -Mathf.Sqrt(Mathf.Abs(2 * mCurrentVerticalAbsDeceleration * deltaS));
                }
                else if (transform.localPosition.y.Compare(mMinPosition.y) < 0)
                {
                    float deltaS = (mMinPosition.y - transform.localPosition.y) / LayerManager.ScreenHeightNormalizer;
                    mVerticalSpeed = Mathf.Sqrt(Mathf.Abs(2 * mCurrentVerticalAbsDeceleration * deltaS));
                }
                else if (mTimePositionArray.Count >= MIN_ARRAY_VALUE_QTT)
                {
                    mVerticalSpeed = (mTimePositionArray[mTimePositionArray.Count - 1].Value.y - mTimePositionArray[0].Value.y) / (mElapsed - mTimePositionArray[0].Key);
                }
                else
                {
                    mVerticalSpeed = 0.0f;
                }
                mSpeedVerticalSign = Mathf.Sign(mVerticalSpeed);
            }
            if (HorizontalScrollEnabled)
            {
                if (transform.localPosition.x.Compare(mMaxPosition.x) > 0)
                {
                    float deltaS = (mMaxPosition.x - transform.localPosition.x) / LayerManager.ScreenWidthNormalizer;
                    mHorizontalSpeed = -Mathf.Sqrt(Mathf.Abs(2 * mCurrentHorizontalAbsDeceleration * deltaS));
                }
                else if (transform.localPosition.x.Compare(mMinPosition.x) < 0)
                {
                    float deltaS = (mMinPosition.x - transform.localPosition.x) / LayerManager.ScreenWidthNormalizer;
                    mHorizontalSpeed = Mathf.Sqrt(Mathf.Abs(2 * mCurrentHorizontalAbsDeceleration * deltaS));
                }
                else if (mTimePositionArray.Count >= MIN_ARRAY_VALUE_QTT)
                {
                    mHorizontalSpeed = (mTimePositionArray[mTimePositionArray.Count - 1].Value.x - mTimePositionArray[0].Value.x) / (mElapsed - mTimePositionArray[0].Key);
                }
                else
                {
                    mHorizontalSpeed = 0.0f;
                }
                mSpeedHorizontalSign = Mathf.Sign(mHorizontalSpeed);
            }

            mTimePositionArray.Clear();
            mAlreadyRecalculatedVerticalSpeed = mAlreadyRecalculatedHorizontalSpeed = false;
        }

        private Vector3 CalculateDistance(Vector3 to, Vector3 from)
        {
            return new Vector3
            (
                (to.x - from.x) * LayerManager.ScreenWidthNormalizer,
                (to.y - from.y) * LayerManager.ScreenHeightNormalizer,
                0.0f
            );
        }

        private void Update()
        {
            if (mIsTouching)
            {
                mElapsed += Time.deltaTime;
                mAuxiliarElapsed += Time.deltaTime;
                if (mAuxiliarElapsed.Compare(UPDATE_ARRAY_TIME) >= 0)
                {
                    mAuxiliarElapsed -= UPDATE_ARRAY_TIME;
                    UpdateTimePositionsArray();
                }
            }
            if (mMovementBegan)
            {

            }
            else
            {
                if (SpringEffectEnabled)
                {
                    transform.localPosition = new Vector3
                    (
                        (!mHorizontalSpeed.Aproximate(0.0f) && HorizontalScrollEnabled) ? (transform.localPosition.x + mHorizontalSpeed * Time.deltaTime * LayerManager.ScreenWidthNormalizer) : (transform.localPosition.x),
                        (!mVerticalSpeed.Aproximate(0.0f) && VerticalScrollEnabled) ? (transform.localPosition.y + mVerticalSpeed * Time.deltaTime * LayerManager.ScreenHeightNormalizer) : (transform.localPosition.y),
                        transform.localPosition.z
                    );
                }
                else
                {
                    transform.localPosition = new Vector3
                    (
                        Mathf.Clamp
                        (
                            (!mHorizontalSpeed.Aproximate(0.0f) && HorizontalScrollEnabled) ?
                                (transform.localPosition.x + mHorizontalSpeed * Time.deltaTime * LayerManager.ScreenWidthNormalizer) :
                                (transform.localPosition.x),
                            mMinPosition.x,
                            mMaxPosition.x
                        ),
                        Mathf.Clamp
                        (
                            (!mVerticalSpeed.Aproximate(0.0f) && VerticalScrollEnabled) ?
                                (transform.localPosition.y + mVerticalSpeed * Time.deltaTime * LayerManager.ScreenHeightNormalizer) :
                                (transform.localPosition.y),
                            mMinPosition.y,
                            mMaxPosition.y
                        ),
                        transform.localPosition.z
                    );
                }

                if (!mHorizontalSpeed.Aproximate(0.0f) && HorizontalScrollEnabled)
                {
                    if (mSpeedHorizontalSign.Aproximate(1.0f))
                    {
                        if (!mAlreadyRecalculatedHorizontalDeceleration)
                        {
                            if (transform.localPosition.x.Compare(mMaxPosition.x) > 0)
                            {
                                mAlreadyRecalculatedHorizontalDeceleration = true;
                                float newPossibleDeceleration = (mHorizontalSpeed * mHorizontalSpeed) / (2 * Mathf.Abs(transform.localPosition.x - (mMaxPosition.x + GetAbsMaxAtenuatedOverflow())) / LayerManager.ScreenWidthNormalizer);
                                mCurrentHorizontalAbsDeceleration = Mathf.Max(mCurrentHorizontalAbsDeceleration, newPossibleDeceleration);
                                mCurrentHorizontalAbsDeceleration = Mathf.Max(mCurrentHorizontalAbsDeceleration, 5000.0f);
                            }
                        }
                        mHorizontalSpeed = Mathf.Clamp(mHorizontalSpeed - mSpeedHorizontalSign * Time.deltaTime * mCurrentHorizontalAbsDeceleration, 0.0f, Mathf.Infinity);
                    }
                    else
                    {
                        if (!mAlreadyRecalculatedHorizontalDeceleration)
                        {
                            if (transform.localPosition.x.Compare(mMinPosition.x) < 0)
                            {
                                mAlreadyRecalculatedHorizontalDeceleration = true;
                                float newPossibleDeceleration = (mHorizontalSpeed * mHorizontalSpeed) / (2 * Mathf.Abs(transform.localPosition.x - (mMinPosition.x - GetAbsMaxAtenuatedOverflow())) / LayerManager.ScreenWidthNormalizer);
                                mCurrentHorizontalAbsDeceleration = Mathf.Max(mCurrentHorizontalAbsDeceleration, newPossibleDeceleration);
                                mCurrentHorizontalAbsDeceleration = Mathf.Max(mCurrentHorizontalAbsDeceleration, 5000.0f);
                            }
                        }
                        mHorizontalSpeed = Mathf.Clamp(mHorizontalSpeed - mSpeedHorizontalSign * Time.deltaTime * mCurrentHorizontalAbsDeceleration, -Mathf.Infinity, 0.0f);
                    }
                }
                else
                {
                    if (!mAlreadyRecalculatedHorizontalSpeed)
                    {
                        mAlreadyRecalculatedHorizontalSpeed = true;
                        if (transform.localPosition.x.Compare(mMaxPosition.x) > 0)
                        {
                            mCurrentHorizontalAbsDeceleration = AbsoluteDeceleration;
                            float deltaS = Mathf.Abs(mMaxPosition.x - transform.localPosition.x) / LayerManager.ScreenWidthNormalizer;
                            mHorizontalSpeed = -Mathf.Sqrt(Mathf.Abs(2 * mCurrentHorizontalAbsDeceleration * deltaS));
                            mSpeedHorizontalSign = Mathf.Sign(mHorizontalSpeed);
                        }
                        else if (transform.localPosition.x.Compare(mMinPosition.x) < 0)
                        {
                            mCurrentHorizontalAbsDeceleration = AbsoluteDeceleration;
                            float deltaS = Mathf.Abs(mMinPosition.x - transform.localPosition.x) / LayerManager.ScreenWidthNormalizer;
                            mHorizontalSpeed = Mathf.Sqrt(Mathf.Abs(2 * mCurrentHorizontalAbsDeceleration * deltaS));
                            mSpeedHorizontalSign = Mathf.Sign(mHorizontalSpeed);
                        }
                    }
                }

                if (!mVerticalSpeed.Aproximate(0.0f) && VerticalScrollEnabled)
                {
                    if (mSpeedVerticalSign.Aproximate(1.0f))
                    {
                        if (!mAlreadyRecalculatedVerticalDeceleration)
                        {
                            if (transform.localPosition.y.Compare(mMaxPosition.y) > 0)
                            {
                                mAlreadyRecalculatedVerticalDeceleration = true;
                                float newPossibleDeceleration = (mVerticalSpeed * mVerticalSpeed) / (2 * Mathf.Abs(transform.localPosition.y - (mMaxPosition.y + GetAbsMaxAtenuatedOverflow())) / LayerManager.ScreenHeightNormalizer);
                                mCurrentVerticalAbsDeceleration = Mathf.Max(mCurrentVerticalAbsDeceleration, newPossibleDeceleration);
                                mCurrentVerticalAbsDeceleration = Mathf.Max(mCurrentVerticalAbsDeceleration, 5000.0f);
                            }
                        }
                        mVerticalSpeed = Mathf.Clamp(mVerticalSpeed - mSpeedVerticalSign * Time.deltaTime * mCurrentVerticalAbsDeceleration, 0.0f, Mathf.Infinity);
                    }
                    else
                    {
                        if (!mAlreadyRecalculatedVerticalDeceleration)
                        {
                            if (transform.localPosition.y.Compare(mMinPosition.y) < 0)
                            {
                                mAlreadyRecalculatedVerticalDeceleration = true;
                                float newPossibleDeceleration = (mVerticalSpeed * mVerticalSpeed) / (2 * Mathf.Abs(transform.localPosition.y - (mMinPosition.y - GetAbsMaxAtenuatedOverflow())) / LayerManager.ScreenHeightNormalizer);
                                mCurrentVerticalAbsDeceleration = Mathf.Max(mCurrentVerticalAbsDeceleration, newPossibleDeceleration);
                                mCurrentVerticalAbsDeceleration = Mathf.Max(mCurrentVerticalAbsDeceleration, 5000.0f);
                            }
                        }
                        mVerticalSpeed = Mathf.Clamp(mVerticalSpeed - mSpeedVerticalSign * Time.deltaTime * mCurrentVerticalAbsDeceleration, -Mathf.Infinity, 0.0f);
                    }
                }
                else
                {
                    if (!mAlreadyRecalculatedVerticalSpeed)
                    {
                        mAlreadyRecalculatedVerticalSpeed = true;
                        if (transform.localPosition.y.Compare(mMaxPosition.y) > 0)
                        {
                            mCurrentVerticalAbsDeceleration = AbsoluteDeceleration;
                            float deltaS = Mathf.Abs(mMaxPosition.y - transform.localPosition.y) / LayerManager.ScreenHeightNormalizer;
                            mVerticalSpeed = -Mathf.Sqrt(Mathf.Abs(2 * mCurrentVerticalAbsDeceleration * deltaS));
                            mSpeedVerticalSign = Mathf.Sign(mVerticalSpeed);
                        }
                        else if (transform.localPosition.y.Compare(mMinPosition.y) < 0)
                        {
                            mCurrentVerticalAbsDeceleration = AbsoluteDeceleration;
                            float deltaS = Mathf.Abs(mMinPosition.y - transform.localPosition.y) / LayerManager.ScreenHeightNormalizer;
                            mVerticalSpeed = Mathf.Sqrt(Mathf.Abs(2 * mCurrentVerticalAbsDeceleration * deltaS));
                            mSpeedVerticalSign = Mathf.Sign(mVerticalSpeed);
                        }
                    }
                }
            }
        }

        private void UpdateTimePositionsArray()
        {
            if (mTimePositionArray.Count >= MIN_ARRAY_VALUE_QTT)
            {
                int i;

                // elimina os tempos antigos
                for (i = mTimePositionArray.Count - 1; i >= 0; i--)
                {
                    if ((mElapsed - mTimePositionArray[i].Key).Compare(KEEP_HISTORY_TIME) >= 0)
                    {
                        break;
                    }
                }
                for (int j = i; j >= 0; j--)
                {
                    mTimePositionArray.RemoveAt(j);
                }

                // elimina valores anteriores a possivel mudança de direcao
                if (mTimePositionArray.Count >= MIN_ARRAY_VALUE_QTT)
                {
                    int lastVerticalSign = mTimePositionArray[mTimePositionArray.Count - 1].Value.y >= mTimePositionArray[mTimePositionArray.Count - 2].Value.y ? 1 : -1;
                    for (i = mTimePositionArray.Count - 2; i > 0; i--)
                    {
                        int sinal = mTimePositionArray[i].Value.y >= mTimePositionArray[i - 1].Value.y ? 1 : -1;
                        if (sinal != lastVerticalSign)
                        {
                            break;
                        }
                    }
                    for (int j = i - 1; j >= 0; j--)
                    {
                        mTimePositionArray.RemoveAt(j);
                    }
                }
            }
        }
    }
}