var state = new Kiwi.State('state');


state.preload = function() {
    this.addJSON('level1', 'data/levels/level-01.json');
    this.addJSON('level2', 'data/levels/level-02.json');
    this.addJSON('level3', 'data/levels/level-03.json');
    this.addJSON('level4', 'data/levels/level-04.json');
    this.addJSON('level5', 'data/levels/level-05.json');
    this.addJSON('level6', 'data/levels/level-06.json');
    this.addJSON('level7', 'data/levels/level-07.json');
    this.addJSON('level8', 'data/levels/level-08.json');
    this.addJSON('level9', 'data/levels/level-09.json');
    this.addJSON('level10', 'data/levels/level-10.json');
    this.addJSON('level11', 'data/levels/level-11.json');
    this.addJSON('level12', 'data/levels/level-12.json');
    this.addSpriteSheet('base', './data/images/gfx64/tiles.png', 64, 64);
    this.addSpriteSheet('character', './data/images/gfx64/marble_black.png', 80, 80 );
    this.addSpriteSheet('oneWay', './data/images/gfx64/st_oneway.png', 64, 64 );
    this.addSpriteSheet('finishMarker', './data/images/gfx64/finish_marker.png', 64, 64 );
    this.addSpriteSheet('button', './data/images/gfx64/button.png', 128, 64 );
    this.addSpriteSheet('teleport', './data/images/gfx64/st_spitter_idle.png', 64, 64);
    this.addSpriteSheet('box', './data/images/gfx64/st_box_wood.png', 64, 64);
    this.addSpriteSheet('switch', './data/images/gfx64/st_switch.png', 64, 64);
    this.addSpriteSheet('laserBeam', './data/images/gfx64/it_laser.png', 64, 64);
    this.addSpriteSheet('laser', './data/images/gfx64/st_laser.png', 64, 64);
}

state.velocityX = 64;
state.velocityY = 64;

state.create = function() {
    var level = levels[this.game.levelIndex - 1];
    this.tilemap = new Kiwi.GameObjects.Tilemap.TileMap(this, 'level' + this.game.levelIndex.toString(), this.textures.base);
    
    this.character = new Kiwi.GameObjects.Sprite(this, this.textures.character, 0, 0);
    this.character.initialX = 2*64;
    this.character.initialY = 64;
    this.character.initialVelocityX = 0;
    this.character.initialVelocityY = this.velocityY;
    // Hitbox is detecting collision in future step.
    // This is little bit counter intuitive. 
    // You have to make collision box smaller at least one step.    
    this.character.box.hitbox = new Kiwi.Geom.Rectangle( 12, 12, 58, 58 );
    this.character.physics = this.character.components.add( new Kiwi.Components.ArcadePhysics( this.character, this.character.box ) );

    this.character.animation.add('walking', [ 0, 1 ], 0.2, true);
    this.character.animation.add('idle', [ 2, 3, 4, 5, 6, 5, 4, 3 ], 0.2, true);
    this.character.animation.add('failed', [ 11, 10, 9, 8, 7, 8, 9, 10], 0.2, true);

    this.redirectorGroup = new Kiwi.Group( this );
        
    this.finishMarker = new Kiwi.GameObjects.Sprite(this, this.textures.finishMarker, 6*64, 4*64);  
    this.finishMarker.animation.add('idle', [ 0, 1, 2, 3, 2, 1 ], 0.3, true);
    this.finishMarker.animation.play('idle', true);

    // Ground layer
    this.addChild(this.tilemap.layers[0]);

    // Load level specific data
    level.create(this);
    
    // Fix character coordinates and speed
    this.resetCharacter();

    this.addChild(this.finishMarker);

    // Walls layer
    this.addChild(this.tilemap.layers[1]);
    
    // Add action objects
    this.addChild(this.redirectorGroup);

    this.addChild(this.character);

    // Sky layer
    this.addChild(this.tilemap.layers[2]);

    // Create collision layer
    for(var i = 21; i < this.tilemap.tileTypes.length; i++) {
        this.tilemap.tileTypes[i].allowCollisions = Kiwi.Components.ArcadePhysics.ANY;
    }
        
    this.keyboard = this.game.input.keyboard;

    this.leftKey = this.keyboard.addKey(Kiwi.Input.Keycodes.LEFT, true);
    this.rightKey = this.keyboard.addKey(Kiwi.Input.Keycodes.RIGHT, true);
    //Prevent the down key from scrolling the page
    this.keyboard.addKey(Kiwi.Input.Keycodes.DOWN, true);
    
    this.stageState = 'init';
    
    this.myButton = new Kiwi.GameObjects.Textfield( this, "Start", 6*64+16, 60, "#000", 32, 'normal', 'Impact' );
    this.myButtonSprite = new Kiwi.GameObjects.Sprite(this, this.textures.button, 6*64, 50);
    this.myButtonSprite.input.onUp.add( this.buttonReleased, this );    
        
    this.character.input.onUp.add( this.buttonReleased, this );
}

state.buttonReleased = function(sprite) {   
    if (this.stageState == 'init') {
        this.myButton.text = '...'; 
        this.activateScene();
    } else if (this.stageState == 'stop') {
        this.resetStage();
        this.myButton.text = 'Start'
        this.stageState = 'init';
    } else if (this.stageState == 'complete') {
        game.levelStatus = 'complete';
        game.states.switchState('levelSelector');
    }
}

state.resetCharacter = function () {
    this.character.physics.velocity.x = 0;
    this.character.physics.velocity.y = 0;
    this.character.x = this.character.initialX - 8;
    this.character.y = this.character.initialY - 8;
}

/**
 * Reset all objects that should be back in the original position.
 * Dragable redirectors are left on original place.
 */
state.resetStage = function() {
    this.resetCharacter();
    var redirectors = this.redirectorGroup.members;
    for ( var i = 0; i < redirectors.length; i++ ) {
        var redirector = redirectors[i];
        if (redirector.type == 'box') {
            redirector.x = redirector.initialX;
            redirector.y = redirector.initialY;
        } else if (redirector.type == 'switch') {
            redirector.cellIndex = redirector.initialCellIndex;
        } else if (redirector.type == 'laserBeam') {
            redirector.visible = redirector.initialVisible;
        } else if (redirector.type == 'laser') {
            redirector.animation.play(redirector.initialAnimation, true);
        }
    }
}

state.startedDrag = function(sprite) {
    sprite.formerX = sprite.x;
    sprite.formerY = sprite.y;   
}

/**
 * Reverse effect of drag operaion. Set original coordinates.
 */
state.resetDrag = function(sprite) {
    sprite.x = sprite.formerX;
    sprite.y = sprite.formerY;
}

/**
 * Get tile index from specified coordinates.
 */
state.getTileIndex = function(x, y) {
    var tile = this.tilemap.layers[0].getTileFromXY(x / 64 , y / 64);
    if (tile == null) {
        return 0;
    }
    return tile.index;
}

/**
 * Perform adjustments of coordinates for drop operation.
 * Make sure that object is dropped on valid place and check
 * collision with other objects. In case of failure revert
 * to former coordinates.
 */
state.stoppedDrag = function(sprite) {
    if (sprite.x % 64 > 32) {
        sprite.x += 64;
    }
    if (sprite.y % 64 > 32) {
        sprite.y += 64;
    }
    sprite.x = sprite.x - sprite.x % 64;
    sprite.y = sprite.y - sprite.y % 64;
    
    // Make sure that we drop tile only on valid ground
    if (this.getTileIndex(sprite.x, sprite.y) == 0) {
        this.resetDrag(sprite);
    } else {
        // Make sure that we are not dropping on another redirector object
        var redirectors = this.redirectorGroup.members;
        for ( var i = 0; i < redirectors.length; i++ ) {
            var redirector = redirectors[i];
            
            // Skip comparision of self
            if (sprite === redirector) {
                continue;
            }
            
            if ((sprite.x == redirector.x) && (sprite.y == redirector.y)) {
                this.resetDrag(sprite);
                break;
            }
        }
    }
}

state.activateScene = function () {
    this.character.physics.velocity.x = this.character.initialVelocityX;
    this.character.physics.velocity.y = this.character.initialVelocityY;
    this.stageState = 'running';
}

state.update = function () {
    //Update all the gameobjects
    Kiwi.State.prototype.update.call(this);

    //Update physics
    this.checkCollision();

    this.updateCharacterAnimation();
}

state.updateCharacterAnimation = function () {
    
    if(( this.character.physics.velocity.y != 0 ) || (this.character.physics.velocity.x != 0)) {
        this.character.animation.play('walking', false);
    } else {
        if (this.stageState == 'stop') {
            this.character.animation.play('failed', false); 
        } else {
            this.character.animation.play('idle', false);
        }
    }
}

state.turnOff = function(items) {
    for (var i=0; i!=items.length; i++) {
        var item = items[i];
        if (item.type == 'laserBeam') {
            item.visible = false;
        } else if (item.type == 'switch') {
            item.cellIndex = 2;
        } else {
            item.animation.play('idle');
        }
    }
}

state.turnOn = function(items) {
    for (var i=0; i!=items.length; i++) {
        var item = items[i];
        if (item.type == 'laserBeam') {
            item.visible = true;
        } else if (item.type == 'switch') {
            item.cellIndex = 0;        
        } else {
            item.animation.play('burning');
        }
    }
}

/**
 * Create effect based on type of redirector.
 */
state.applyRedirectorMechanics = function(tileX, tileY, redirector) {
    var isRecalculationRequired = false;
    var redirectorTileX = Math.round(redirector.x/64);
    var redirectorTileY = Math.round(redirector.y/64);
    
    // Box has hit regions around. Compute based on vector of player
    if (redirector.type == 'box') {
        tileX+=this.character.physics.velocity.x/64;
        tileY+=this.character.physics.velocity.y/64;
    } 

    // Check for hit
    if ((tileX != redirectorTileX ) || (tileY != redirectorTileY)) {
        return;
    }
    
    if (redirector.type == 'vector') {
        this.character.physics.velocity.x = redirector.affectVelocityX;
        this.character.physics.velocity.y = redirector.affectVelocityY;
    } else if (redirector.type == 'teleport') {
        this.character.x = redirector.affectedX - 8;
        this.character.y = redirector.affectedY - 8;
    } else if (redirector.type == 'box') {
        // Move tile when it is possible
        if (this.getTileIndex(redirector.x + this.character.physics.velocity.x, redirector.y + this.character.physics.velocity.y) != 0) {
            redirector.x += this.character.physics.velocity.x;
            redirector.y += this.character.physics.velocity.y;
        }
        // Bounce
        this.character.physics.velocity.x = -this.character.physics.velocity.x;
        this.character.physics.velocity.y = -this.character.physics.velocity.y;
        
        // Bounce from box should relaunch direction calculation
        // This is for the case when box lies close to redirector 
        isRecalculationRequired = true;                
    } else if (redirector.type == 'switch') {
        // Flip the switch
        if (redirector.cellIndex == 0) {
            redirector.cellIndex = 2;
            this.turnOff(redirector.linkedItems);
        } else {
            redirector.cellIndex = 0;
            this.turnOn(redirector.linkedItems);
        }
    } else if (redirector.type == 'laserBeam') {
        if (redirector.visible) {
            this.character.physics.velocity.x = 0;
            this.character.physics.velocity.y = 0;
        }
    }
    
    // Correction of coordinates
    this.character.y = Math.round(this.character.y);
    this.character.x = Math.round(this.character.x);
    return isRecalculationRequired;
}

/**
 * Resolve collisions between the character and the first layer.
 */ 
state.checkCollision = function () {
    this.tilemap.layers[1].physics.overlapsTiles( this.character, true );
    
    if ((this.character.physics.velocity.x == 0) && (this.character.physics.velocity.y == 0)) {
        if (this.stageState == 'running') {
            this.stageState = 'stop';
            this.myButton.text = 'Restart'
        }
        return;
    }

    // Make collision detection only when ball is fully on the same tile
    var positionX = Math.round(this.character.x) + 8;
    var positionY = Math.round(this.character.y) + 8;
    if ((Math.round(positionX) % 64 != 0) || (Math.round(positionY)% 64 != 0)) {
        return;
    }

    var isRecalculationRequired = false;
    var redirectors = this.redirectorGroup.members;
    for ( var i = 0; i < redirectors.length; i++ ) {
        if (this.applyRedirectorMechanics(Math.round(positionX/64), Math.round(positionY/64), redirectors[i])) {
            isRecalculationRequired = true;
        }
    } 
    
    // Make the second round of calculation in case of hit of box
    if (isRecalculationRequired) {
        for ( var i = 0; i < redirectors.length; i++ ) {
            this.applyRedirectorMechanics(Math.round(positionX/64), Math.round(positionY/64), redirectors[i]);
        } 
    }
        
    if (((Math.round(positionX/64) == Math.round(this.finishMarker.x/64) )) && (Math.round(positionY/64) == Math.round(this.finishMarker.y/64))) {
        this.stageState = 'complete';
        this.myButton.text = 'Next->'
        this.addChild( this.myButtonSprite );
        this.addChild( this.myButton );

        this.character.physics.velocity.x = 0;
        this.character.physics.velocity.y = 0;
    }

}
