// @flow
import type { Vec3 } from './basicTypes';
import type { TrackTile } from './trackTile';
import type { PlayerState } from './player';

const extend = require('xtend');
const distance = require('gl-vec3/distance');
const { jumpMove } = require('./animations');
const { getTilePath } = require('./track');
const tileSize = 8 * 8 / 10;

const changeTile = ({ state, afterTileState, time, tiles, trackOffset }) => {
    let result = extend({}, state);
    const nextTileInfo = getTilePath({
        playerAngle: result.angleZ,
        state: extend({}, afterTileState),
        track: tiles,
        trackOffset
    });
    const newMoveAnim = extend(result.animations.move, {
        update: nextTileInfo.update,
        duration: nextTileInfo.duration,
        startTime: time
    });
    const newAnimations = extend(result.animations, { move: newMoveAnim });
    result.currentTile = nextTileInfo.tile || null;
    result.animations = newAnimations;
    return result;
};

let counter = 0;
type Update = ({
    state: PlayerState,
    time: number,
    tiles: TrackTile[],
    trackOffset: Vec3,
    tick: { cancel: Function }
}) => PlayerState;
const update: Update = ({ state, time, tiles, trackOffset, tick }) => {
    let nextState = extend({}, state);
    const moveAnim = nextState.animations.move;
    const jumpAnim = nextState.animations.jump;
    const moveProgress = (time - moveAnim.startTime) / moveAnim.duration;
    const jumpProgress = (time - jumpAnim.startTime) / jumpAnim.duration;

    const jumpSimulation = jumpMove({ initialState: nextState });
    const landingState = jumpSimulation({ state: nextState, progress: 1 });
    const stateAfterJump = changeTile({
        state: nextState,
        afterTileState: landingState,
        time,
        tiles,
        trackOffset
    });
    const angleDiff = stateAfterJump.currentTile
        ? Math.abs(
              stateAfterJump.currentTile.angle - nextState.currentTile.angle
          )
        : 0;
    nextState.canJump =
        !jumpAnim.enabled &&
        nextState.angleZ % 90 === 0 &&
        angleDiff % 180 === 0;

    if (!jumpAnim.enabled && moveAnim.enabled) {
        nextState = moveAnim.update({
            state: nextState,
            progress: Math.min(1, moveProgress)
        });
        if (moveProgress > 1) {
            nextState = changeTile({
                state: nextState,
                afterTileState: moveAnim.update({
                    state: nextState,
                    progress: 1.1
                }),
                time,
                tiles,
                trackOffset
            });
        }
    }

    if (jumpAnim.enabled) {
        nextState = jumpAnim.update({
            state: nextState,
            progress: Math.min(jumpProgress, 1)
        });
        if (jumpProgress >= 1) {
            const landedState = changeTile({
                state: nextState,
                afterTileState: nextState,
                time,
                tiles,
                trackOffset
            });
            const landedAngleDiff = landedState.currentTile
                ? Math.abs(
                      landedState.currentTile.angle -
                          nextState.currentTile.angle
                  )
                : 0;

            // check for invalid cases

            // no tile available to land
            if (landedState.currentTile === null) {
                if (jumpProgress > 2) {
                    nextState.isDead = true;
                }
                nextState = jumpAnim.update({
                    state: nextState,
                    progress: jumpProgress
                });
                return nextState;
            }

            // wrong landing angle (shouldnt be poissible)
            if (landedAngleDiff % 180 !== 0) {
                console.log(
                    'wrong landing angle',
                    landedState.currentTile.angle,
                    nextState.currentTile.angle
                );
                tick.cancel();
                return state;
            }

            // it is safe to land, end jump
            console.log('landed');

            // change move start time to
            const sameDirection = landedAngleDiff !== 180;
            let nextTileStartTime = time;
            const tilesDistance = distance(
                nextState.currentTile.offset,
                landedState.currentTile.offset
            );
            const landBeforeEnd = tilesDistance < tileSize * 1.1;
            if (landBeforeEnd) {
                if (sameDirection) {
                    nextTileStartTime = nextState.animations.move.startTime;
                } else {
                    const remainingTime =
                        landedState.animations.move.duration *
                        (1 - Math.min(1, moveProgress));
                    nextTileStartTime = time - remainingTime;
                }
            } else {
                const extraTime =
                    landedState.animations.move.duration *
                    (Math.min(2, moveProgress) - 1);
                if (sameDirection) {
                    nextTileStartTime = time - extraTime;
                } else {
                    nextTileStartTime =
                        time - landedState.animations.move.duration + extraTime;
                }
            }
            nextState = landedState;
            nextState.animations.move.startTime = nextTileStartTime;
            nextState.animations.jump.enabled = false;
        }
        // console.log({nextState})

        // // debug
        // if (counter > 80){
        // tick.cancel();
        // }else{
        // counter++;
        // }
    }
    return nextState;
};

module.exports = update;

@@ -1814,52 +1814,56 @@
     
-const angleDiff = stateAfterJump.currentTile
+nextState.canJump =%0A        !jumpAnim.enabled &&
 %0A   
@@ -1871,34 +1871,47 @@
     
-? Math.abs(%0A      
+nextState.angleZ %25 90 === 0 &&%0A
         
 stat
@@ -1902,24 +1902,26 @@
  &&%0A        
+(!
 stateAfterJu
@@ -1938,191 +1938,118 @@
 Tile
-.angle - nextState.currentTile.angle%0A          )%0A        : 0;%0A    nextState.canJump =%0A        !jumpAnim.enabled &&%0A        nextState.angleZ %25 90 === 0 &&%0A        angleDiff %25 180 === 0
+ %7C%7C%0A            (stateAfterJump.currentTile &&%0A                stateAfterJump.currentTile.name === 'forward'))
 ;%0A%0A 
@@ -3664,190 +3664,72 @@
  // 
-wrong landing angle (shouldnt be poissible)%0A            if (landedAngleDiff %25 180 !== 0) %7B%0A                console.log(%0A                    'wrong landing angle',%0A                   
+landed on a no-landeable tile%0A            const landedTileName =
  lan
@@ -3753,14 +3753,13 @@
 ile.
-angle,
+name;
 %0A   
@@ -3771,92 +3771,83 @@
     
-        nextState.currentTile.angle%0A                );%0A                tick.cancel()
+if (landedTileName !== 'forward') %7B%0A                nextState.isDead = true
 ;%0A  
@@ -3867,17 +3867,21 @@
  return 
-s
+nextS
 tate;%0A  
@@ -3940,43 +3940,8 @@
 jump
-%0A            console.log('landed');
 %0A%0A  

fix bug of player landing on a non-straight tile and rotating