﻿using UnityEngine;
using System.Collections;

public class complexMovement : MonoBehaviour
{

    [HideInInspector]
    public float angle = 90;

    public float _timeForward = 1;
    public float _timeBackward = 1;

    public rotationType _movementType = rotationType.backAndForth;

    public triggerType _trigger = triggerType.startMovingAtAwake;

    public EasingSide _easingSide = EasingSide.easeIn;
    public EasingType _easingForward = EasingType.Linear;
    public EasingType _easingBackward = EasingType.Linear;

   // public float delayAtEnd = 0;
   // public float delayAtStart = 0;

    public float delay = 0;
    public float delayAtBeginning = 0;

    [Tooltip("Where on movement this object starts (0-1)")]
   // public float startAtLocation = 0;

    [HideInInspector]
    public Transform _transform;
    [HideInInspector]
    public float _startAngle;
    //private float _stopAngle;
    //private int direction = 1;

    //current state of the object
    private rotationPhase _phase;
    private float _location = 0;
    //private float _phaseStartedAt;

    private float lastStartCycle;
    private float lastEndCycle;

    //saved state
    private movementState _checkpointState = new movementState();
    
    private float startNextPhase = 0;
    private float fullCycle;
    private float startTime;


    // Use this for initialization
    public void Start()
    {
       // _phaseStartedAt = Time.time;
        _transform = transform;
        _startAngle = _transform.rotation.eulerAngles.z;
        //_stopAngle = _startAngle + angle;

        //check triggers 
        switch (_trigger) { 
            case triggerType.startMovingAtAwake:
                if (delayAtBeginning <= 0) {
                    movementStart();
                }
                else
                {
                    _phase = rotationPhase.notStarted;
                    startTime = Time.time;
                    Invoke("movementStart", delayAtBeginning);

                }                 
                break;   
            case triggerType.whenTriggered:
              //  _phase = rotationPhase.notStarted;
                _phase = rotationPhase.notTriggered;
                break;

        }

       // _checkpointState.phase = _phase;
        //_checkpointState.location = _location;
        
  //      SubscribeToEvents(true);

        //set checkpoint state at the beginning
  //      placeCheckpoint();


    }
    
    void movementStart()
    {
        _phase = rotationPhase.goingForward;
 
        if (delay > 0)
        {
            //startNextPhase = _timeForward * (1 - startAtLocation) + delay;
            fullCycle = _timeForward + _timeBackward + delay * 2;
            InvokeRepeating("endCycle", _timeForward + delay, fullCycle);
            InvokeRepeating("startCycle", fullCycle, fullCycle);
            
            //laitetaan "edelliset" cyclejen ajat checkpointtien laskutoimituksia varten
            lastStartCycle = Time.time;
            lastEndCycle = Time.time + (_timeForward + delay - fullCycle);

        }

    }

    void OnEndLevel() 
    {
      //  SubscribeToEvents(false);    
    }


    //trigger function
    public void activateObject()
    {
        if (_phase == rotationPhase.notTriggered)
        {

            if (delayAtBeginning <= 0)
            {
                movementStart();
            }
            else
            {
                _phase = rotationPhase.notStarted;
                startTime = Time.time;
                Invoke("movementStart", delayAtBeginning);

            }
        }
    }

    public void setAngle(float a)
    {
        angle = a;
    }
       

    // Update is called once per frame
    //public void Update () {
    public float calculateLocation()
    {

        EasingType tempEasing = EasingType.Linear;
        switch (_phase)
        {
            case rotationPhase.goingForward:
                float amount = Time.deltaTime / _timeForward;
                _location += amount;
                tempEasing = _easingForward;

                //jos location on 1:ssä aloitetaan paluuvaihe
                if (_location >= 1)
                {
                    if (delay > 0)
                    {
                        _phase = rotationPhase.onPauseAtEnd;
  
                        //lasketaan phasen aloitusaika
                        //siihen täytyy lisätä ylimennyt liikkuminen, jotta liike pysyy synkissä
                   //     _phaseStartedAt = Time.time - ((_location - 1) * _timeForward);

                        //Debug.Log(((_location - 1) * _timeForward));

                        _location = 1;
                    }
                    else
                    {
                        startBackwardPhase();
                    }
                    if (_movementType == rotationType.onlyMoveOnce) _phase = rotationPhase.stopped;
                    
                }

                break;
            case rotationPhase.goingBackward:
                _location -= Time.deltaTime / _timeBackward;
                tempEasing = _easingBackward;

                if (_location <= 0)
                {
                    if (delay > 0)
                    {
                        _phase = rotationPhase.onPauseAtStart;
                        
                  //      _phaseStartedAt = Time.time - (-_location * _timeBackward);
                        _location = 0;
                    }
                    else
                    {
                        startForwardPhase();
                    }
                }

                break;

        }

        float locWithEasing = _location;

        //calculate easing
        if (tempEasing != EasingType.Linear)
        {

            switch (_easingSide)
            {
                case EasingSide.easeIn:
                    locWithEasing = Easing.EaseIn(_location, tempEasing);
                    break;
                case EasingSide.easeOut:
                    locWithEasing = Easing.EaseOut(_location, tempEasing);
                    break;
                case EasingSide.easeInOut:
                    locWithEasing = Easing.EaseInOut(_location, tempEasing);
                    break;

            }
        }
        return locWithEasing;
    }

    private void startBackwardPhase()
    {

        //käsitellään rotation tyypit
        switch (_movementType)
        {
            case rotationType.backAndForth:
                _phase = rotationPhase.goingBackward;
                _location = 2 - _location;
                break;
            case rotationType.continuous:
                //_phase = rotationPhase.goingBackward;
                _location -= 1;
                break;
            case rotationType.step:
                //_phase = rotationPhase.goingBackward;
                _location -= 1;
                _startAngle += angle;
                break;
            case rotationType.backAndForthReversedEasing:
                _phase = rotationPhase.goingBackward;
                _location = 2 - _location;
                //jos reversed easing on päällä käännetään easing direction suunnan vaihtuessa
                swapEasingDirection();
                break;
        }

    }

    private void startForwardPhase()
    {

        _phase = rotationPhase.goingForward;
        _location = -_location;
        //jos reversed easing on päällä käännetään easing direction suunnan vaihtuessa
        if (_movementType == rotationType.backAndForthReversedEasing) swapEasingDirection();

    }
	/*
    private void placeCheckpoint()
    {

        _checkpointState.phase = _phase;

        _checkpointState.location = _location;

//        Debug.Log("tallentaa " + _checkpointState.phase);


        if (delay > 0) {
            _checkpointState.timeToNextEndCycle = lastEndCycle - Time.time + fullCycle;
            _checkpointState.timeToNextStartCycle = lastStartCycle - Time.time + fullCycle;
        }

        if (_phase == rotationPhase.notStarted)
        {
            //dont save movement start time if this is trigger only object
        //    if (_trigger != triggerType.whenTriggered)
        //    {
                _checkpointState.timeUntilStartMoving = delayAtBeginning - (Time.time - startTime);
         //   }
                
           
        }
        

    }

    private void loadCheckpoint()
    {

        //  Debug.Log("moves object to respawn time");
     //   Debug.Log("phase " + _phase + "  location " + _checkpointState.location) ;

        _phase = _checkpointState.phase;

        if (_phase == rotationPhase.notTriggered)
        {
            _location = 0;

        }
        
        if (_phase == rotationPhase.goingBackward || _phase == rotationPhase.goingForward)
        {
            _location = _checkpointState.location;

        }
        if (_phase == rotationPhase.onPauseAtEnd)
        {
            _location = 1;
        }
        if (_phase == rotationPhase.onPauseAtStart)
        {
            _location = 0;
        }

        if (_phase == rotationPhase.notStarted)
        {
            //dont check the starting time if this is trigger only object
         //   if (_trigger != triggerType.whenTriggered)
         //   {
                CancelInvoke();
                Invoke("movementStart", _checkpointState.timeUntilStartMoving);
          //  }
        }

        if (delay > 0)
        {
            //StopAllCoroutines();
            CancelInvoke();

            InvokeRepeating("endCycle",  _checkpointState.timeToNextEndCycle, fullCycle);
            InvokeRepeating("startCycle",  _checkpointState.timeToNextStartCycle, fullCycle);

            //Debug.Log("started invokes again " + _checkpointState.timeToNextEndCycle);

        }

    }*/

    public void startCycle()
    {
        _phase = rotationPhase.goingForward;
        Debug.Log("forward");
        lastStartCycle = Time.time;

    }
    public void endCycle()
    {
        _phase = rotationPhase.goingBackward;
        Debug.Log("bakcward");
        lastEndCycle = Time.time;
    }

    private void swapEasingDirection()
    {
        if (_easingSide == EasingSide.easeIn) { _easingSide = EasingSide.easeOut; }
        else if (_easingSide == EasingSide.easeOut) _easingSide = EasingSide.easeIn;
    }

    /*
    private IEnumerator spinTransform() 
    {


    }*/
    public enum rotationPhase
    {
        notTriggered,
        notStarted,
        goingForward, goingBackward,
        onPauseAtStart, onPauseAtEnd,
        stopped

    }

    public enum rotationType
    {
        backAndForth, //once,
        backAndForthReversedEasing,
        continuous, step,
        onlyMoveOnce
    }

    public enum triggerType
    {
        startMovingAtAwake,
        whenTriggered
    }

    public class movementState
    {
        public rotationPhase phase;
        public float location = 0;
        //if object is in waiting state, save how it has waited
        public float timeWaited;
        public float timeToNextStartCycle;
        public float timeToNextEndCycle;

        //if object is not started moving yet
        public float timeUntilStartMoving;
    }

    /*
    public enum EasingType
    {
        Step,
        Linear,
        Sine,
        Quadratic,
        Cubic,
        Quartic,
        Quintic,
        SameAsForward
    }*/

    public enum EasingSide
    {
        easeIn,
        easeOut,
        easeInOut

    }
}
