
// Initialize rendering window
SetupWindow(1024, 768, "fTetris");

world = new World();
fb = new Framebuffer(1024, 768, 0, PixelFormatR8G8B8A8, true, true);

function BoardCell(world, x, y)
{
    this.Contents = 0;
    this.Block = new WorldObject(world, "data/primitives/smooth_cube.dae");
    
    this.Block.SetProgram("data/shaders/envmapping_with_color.cg");
    this.Parameters = {};
    this.Parameters.DiffuseMap = "data/textures/shiny.png";
    this.Parameters.Color = [1,1,1,1];
    this.Block.SetParameters(this.Parameters);
    this.Block.SetPosition(x*10, y*10, 0);
    
    this.Show = function() 
    { 
		this.Block.SetScale(1,1,1); 
	}
	
    this.Hide = function() 
    { 
		this.Block.SetScale(0.2,0.2,0.2); 
		this.Parameters.Color = [0.8, 0.8, 0.8, 1];
		this.Block.SetParameters(this.Parameters);
	}
    
    this.Hide();
    return this;
}

board = [];
for (var x = 0; x < 8; x++) 
{
    board[x] = [];
    for (var y = 0; y < 16; y++) 
    {
        board[x][y] = new BoardCell(world, x, y);
    }
}


// Creating a library of the possible pieces
pieces = [];
pieces[0] = [ [1, 0], [1, 0], [1, 1] ];
pieces[1] = [ [1, 1], [1, 1] ];
pieces[2] = [ [0, 1], [1, 1], [0, 1] ];
pieces[3] = [ [1, 1, 1, 1] ];

function randomColor() { return [0.75 + Math.random()*0.25, 0.75 + Math.random()*0.25, 0.75 + Math.random()*0.25, 1.0]; }

currentPiece = pieces[0];
currentPieceX = 0;
currentPieceY = 14;
currentColor = new randomColor();

tickLength = 0.45;
lastTick = 0.0;
score = 0;
level = 1;
levelProgress = 0;

layer = new Layer();
layer.SetBlendMode(BlendModeReplace);
font = new Font("data/fonts/arial.ttf:20");
scoreText = new Text(layer, font, "", 50, 30, 500, 100);

music = new AudioStream("data/lul.ogg");
music.PlayLoop();


// called when a line is full and should be removed
function removeLine(y)
{
    for (var i = y; i < 15; i++)
    {
        for (var x = 0; x < 8; x++)
        {
            board[x][i].Contents = board[x][i+1].Contents;
            board[x][i].Parameters.Color = board[x][i+1].Parameters.Color;
            board[x][i].Block.SetParameters(board[x][i].Parameters);
            if (board[x][i].Contents == 1) board[x][i].Show();
            else board[x][i].Hide();
        }
    }
    
    levelProgress++;
    if (levelProgress > 10)
    {
        level++;
        levelProgress = 0;
    }
}

// called when the current piece collides with other blocks or the bottom line
function pieceBlocked()
{
    // Convert piece blocks to static blocks
    for (var x = 0; x < board.length; x++)
    {
        for (var y = 0; y < board[x].length; y++)
        {
            if (board[x][y].Contents == 2) 
            { 
                board[x][y].Contents = 1; 
            }
        }
    }
    
    // Check if a full line is filled
    var combo = 0;
    for (var y = 15; y >= 0; y--)
    {
        var full = true;
        for (var x = 0; x < 8; x++)
        {
            if (board[x][y].Contents != 1)
            {
                full = false;
                break;
            }
        }
        if (full)
        { 
            score += level * 10;
            combo++; removeLine(y); 
        }
    }
    if (combo > 1) score += combo*combo*10*level;
    
    // Insert new piece
    currentPiece = pieces[Math.floor(Math.random()*pieces.length)];
    currentColor = new randomColor();
    currentPieceX = 0;
    currentPieceY = 14;
}

// Rotates the current piece
function rotatePiece()
{
    newPiece = [];
    for (var y = 0; y < currentPiece[0].length; y++) newPiece[y] = [];
    for (var x = 0; x < currentPiece.length; x++)
    {
        for (var y = 0; y < currentPiece[x].length; y++)
        {
            newPiece[currentPiece[0].length-y-1][x] = currentPiece[x][y];
        }
    }
    currentPiece = newPiece;
}

// Game over
function gameOver()
{
    
    scoreText.SetText("GAME OVER!\n Your score: " + score + "\n\nPress Esc to start again");
    
    while (!KeyDown(KeyEsc) && Update()) 
    {  
        ClearColor(0.4, 0.43, 0.65, 0); 
        layer.Render();
    }
        
    level = 1;
    score = 0;
    levelProgress = 0;
    for (var x = 0; x < 8; x++)
    {
        for (var y = 0; y < 16; y++)
        {
            board[x][y].Contents = 0;
            board[x][y].Hide();
        }
    }    
}

// Mainloop
while (Update()) 
{
	ClearColor(1,0,1,0.5);

    time = GetTime();
	//PushFramebuffer(fb);
    world.SetCameraPosition(40+Math.sin(time*0.9)*5, 30, 190+Math.cos(time*0.8)*5);
    world.SetCameraTarget(37, 80, 0);
    world.SetPerspective(60.0, 4.0/3.0, 1.0, 1000.0);
    fps = GetFPS();

    scoreText.SetText("Level: " + level + "\nScore: " + score + "\nFPS: " + fps);
   
    if (time-lastTick > tickLength)
    {
        lastTick = time;
        currentPieceY -= 1;
    }
    
    if (KeyTrigger(KeyLeftArrow))
    {
        currentPieceX--;
        if (currentPieceX < 0) currentPieceX = 0;
    }
    
    if (KeyTrigger(KeyRightArrow))
    {
        currentPieceX++;
        if (currentPieceX >= 8-currentPiece.length) currentPieceX = 8-currentPiece.length;
    }
    
    if (KeyTrigger(KeyUpArrow))
    {
        rotatePiece();
    }
   
    if (KeyDown(KeyDownArrow))
    {
        tickLength = 0.035;
    }
    else
    {
        tickLength = 1.5 / (level + 3.0);
    }
    
    // Make sure piece is inside board
    while (currentPieceY + currentPiece[0].length > 16) currentPieceY--;
    
    // Check if piece reached the bottom of the board
    if (currentPieceY < 0) pieceBlocked();  
    
    // Check if piece intersects static blocks
    for (var x = 0; x < currentPiece.length; x++)
    {
        for (var y = 0; y < currentPiece[x].length; y++)
        {
            if (currentPiece[x][y] == 1)
            {
                var lx = currentPieceX+x;
                var ly = currentPieceY+y;
                if (lx < 0) continue;
                if (ly < 0) continue;
                if (lx >= board.length) continue;
                if (ly >= board[0].length) continue;
                if (board[lx][ly].Contents == 1) { pieceBlocked(); break; }
            }
        }
    }
    
    // Remove piece from board
    for (var x = 0; x < board.length; x++)
    {
        for (var y = 0; y < board[x].length; y++)
        {
            if (board[x][y].Contents == 2) 
            { 
                board[x][y].Hide(); 
                board[x][y].Contents = 0; 
            }
        }
    }
    
    // Add piece in current location
    for (var x = 0; x < currentPiece.length; x++)
    {
        for (var y = 0; y < currentPiece[x].length; y++)
        {
            if (currentPiece[x][y] == 1)
            {
                var lx = currentPieceX+x;
                var ly = currentPieceY+y;
                if (lx < 0) continue;
                if (ly < 0) continue;
                if (lx >= board.length) continue;
                if (ly >= board[0].length) continue;
                board[lx][ly].Contents = 2;
                board[lx][ly].Show();
                board[lx][ly].Parameters.Color = currentColor;
                board[lx][ly].Block.SetParameters(board[lx][ly].Parameters);
            }
        }
    }
    
    // check if game over
    for (var x = 0; x < 8; x++) if (board[x][15].Contents == 1) { gameOver(); break; }

    ClearColor(0.25, 0.43, 0.65, 0.5);
    ClearDepth(1.0);
    world.Render(); 
    layer.Render();

	//PopFramebuffer();
	//fb.Blit();
}

