

import {LAYOUT_SIZE} from "@engine/model/impl/ui/generic/container";
import {Game} from "@engine/core/game";
import {Scene} from "@engine/model/impl/scene";
import {Rectangle} from "@engine/model/impl/ui/drawable/rectangle";
import {RenderableModel} from "@engine/model/renderableModel";
import {Color} from "@engine/core/renderer/color";
import {GameObject} from "@engine/model/impl/gameObject";
import {Point2d} from "@engine/core/geometry/point2d";
import {Keyboard} from "@engine/core/control/keyboard";
import {MathEx} from "@engine/core/mathEx";
import {AppLib} from "./appLib";

enum Direction {
    UP,DOWN,LEFT,RIGHT
}

export class Snake {

    private body:Rectangle[] = [];
    private game:Game;
    private scene:Scene;
    private layout:RenderableModel;
    private direction:Direction = Direction.LEFT;
    private lastUpdateTime:number;
    private moveDeltaTime:number = 200;
    private readonly SIZE:number = 20;
    private feed:Rectangle;
    public static instance:Snake;


    private createBodyCell():Rectangle{
        let r:Rectangle = new Rectangle(this.game);
        this.body.push(r);
        r.color = Color.RGB(20,200,20);
        r.setWH(this.SIZE,this.SIZE);
        r.fillColor = Color.RGB(20,20,20);
        this.layout.appendChild(r);
        return r;
    }

    private getLastCell():Rectangle{
        return this.body[this.body.length-1];
    }

    private getFirstCell():Rectangle{
        return this.body[0];
    }

    private enlarge(){
        let lastCell:Rectangle = this.getLastCell();
        let r:Rectangle = this.createBodyCell();
        let pos = r.pos;
        pos.setXY(lastCell.pos.x,lastCell.pos.y);
        let d:Direction = this.direction;
        if (d==Direction.LEFT) {
            pos.addX(this.SIZE);
        }
        else if (d==Direction.RIGHT) {
            pos.addX(this.SIZE);
        }
        else if (d==Direction.UP) {
            pos.addY(this.SIZE);
        }
        else if (d==Direction.DOWN) {
            pos.addY(-this.SIZE);
        }
    }

    private checkHeadOverFlow(){
        let cell:Rectangle = this.getFirstCell();
        let board:RenderableModel = this.layout;
        if (this.direction==Direction.LEFT || this.direction==Direction.RIGHT) {
            if (cell.pos.x<=0) cell.pos.x = board.width - cell.width;
            else if (cell.pos.x>=board.width - cell.width) cell.pos.x = 0;
        }
        if (this.direction==Direction.UP || this.direction==Direction.DOWN) {
            if (cell.pos.y<=0) cell.pos.y = board.height - cell.height;
            else if (cell.pos.y>=board.height - cell.height) cell.pos.y = 0;
        }
    }

    private setFeed(){
        let isFree = false;
        while (!isFree) {
            let x:number = (MathEx.random(0,this.layout.width/this.SIZE-1))*this.SIZE;
            let y:number = (MathEx.random(0,this.layout.height/this.SIZE-1))*this.SIZE;
            this.feed.pos.setXY(x,y);
            isFree = !this.isOverlapFeed();
        }
    }

    private isOverlapFeed():boolean{
        for (let i = 0; i < this.body.length; i++) {
            const cell = this.body[i];
            if (MathEx.overlapTest(cell.getRect(),this.feed.getRect())) {
                return true;
            }
        }
        return false;
    }

    private isHeadCollidedWithBody():boolean{
        let head:Rectangle = this.getFirstCell();
        for (let i = 1; i < this.body.length; i++) {
            const r = this.body[i].getRect();
            if (MathEx.overlapTest(head.getRect(),r)) return true;
        }
        return false;
    }

    private move(){
        this.checkHeadOverFlow();
        let d:Direction = this.direction;
        let point = new Point2d();
        if (d==Direction.LEFT) {
            point.setX(-this.SIZE);
        }
        else if (d==Direction.RIGHT) {
            point.setX(this.SIZE);
        }
        else if (d==Direction.UP) {
            point.setY(-this.SIZE);
        }
        else if (d==Direction.DOWN) {
            point.setY(this.SIZE);
        }
        point.add(this.getFirstCell().pos);
        for (let i = 0; i < this.body.length; i++) {
            let lastPoint:Point2d = this.body[i].pos.clone();
            this.body[i].pos.set(point);
            point.set(lastPoint);
        }
    }

    private listenToKeys(){
        if (this.game.keyboard.isJustPressed(this.game.keyboard.KEY.UP)) {
            if (this.direction!==Direction.DOWN) this.direction = Direction.UP;
        }
        else if (this.game.keyboard.isJustPressed(this.game.keyboard.KEY.DOWN)) {
            if (this.direction!==Direction.UP) this.direction = Direction.DOWN;
        }
        else if (this.game.keyboard.isJustPressed(this.game.keyboard.KEY.LEFT)) {
            if (this.direction!==Direction.RIGHT) this.direction = Direction.LEFT;
        }
        else if (this.game.keyboard.isJustPressed(this.game.keyboard.KEY.RIGHT)) {
            if (this.direction!==Direction.LEFT) this.direction = Direction.RIGHT;
        }
    }

    public update() {
        this.listenToKeys();
        let time:number = this.game.getTime();
        if (!this.lastUpdateTime) this.lastUpdateTime = time;
        let delta:number = time- this.lastUpdateTime;
        if (delta>this.moveDeltaTime) {
            this.lastUpdateTime = time;
            this.move();
            if (this.isOverlapFeed()) {
                this.enlarge();
                this.setFeed();
                AppLib.getInstance().incScore();
            }
            if (this.isHeadCollidedWithBody()) {
                let scene = this.game.repository.getArray('Scene').find(it=>it.name=='introScene');
                this.game.setCurrScene(scene);
                scene.prepared = true;
            }
        }
    }

    clear(){
        for (let i = 0; i < this.body.length; i++) {
            this.body[i].kill();
        }
        this.body = [];
        this.layout.appendChild(this.feed);
        this.direction = Direction.LEFT;
        this.lastUpdateTime = undefined;
        this.createBodyCell();
        this.getLastCell().pos.setXY(10*this.SIZE,10*this.SIZE);
        this.getFirstCell().color.setR(200);
        (this.getFirstCell().fillColor as Color).setR(100);
        this.enlarge();
        this.enlarge();
        this.setFeed();
    }

    constructor(game:Game,scene:Scene,layout:RenderableModel){
        this.game = game;
        this.scene = scene;
        this.layout = layout;
        this.feed = new Rectangle(game);
        this.feed.setWH(this.SIZE,this.SIZE);
        this.clear();
        Snake.instance = this;

    }


}