=begin
**
** Copyright (C) 2004-2005 Trolltech AS. All rights reserved.
**
** This file is part of the example classes of the Qt Toolkit.
**
** This file may be used under the terms of the GNU General Public
** License version 2.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of
** this file.  Please review the following information to ensure GNU
** General Public Licensing requirements will be met:
** http://www.trolltech.com/products/qt/opensource.html
**
** If you are unsure which license is appropriate for your use, please
** review the following information:
** http://www.trolltech.com/products/qt/licensing.html or contact the
** sales department at sales@trolltech.com.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**

** Translated to QtRuby by Richard Dale
=end
    
require 'tetrixpiece.rb'

class TetrixBoard < Qt::Frame
    
    slots 'start()', 'pause()'
    
    signals 'scoreChanged(int)', 'levelChanged(int)', 'linesRemovedChanged(int)'
    
    BoardWidth = 10
    BoardHeight = 22
    
    def initialize(parent = nil)
        super(parent)
        setFrameStyle(Qt::Frame::Panel | Qt::Frame::Sunken)
        setFocusPolicy(Qt::StrongFocus)
        @isStarting = false
        @isPaused = false
        @board = []
        clearBoard()
    
        @curPiece = TetrixPiece.new
        @nextPiece = TetrixPiece.new
        @nextPiece.setRandomShape()
        @timer = Qt::BasicTimer.new
    end

    def shapeAt(x, y)
        return @board[(y * BoardWidth) + x]
    end

    def setShapeAt(x, y, shape)
        @board[(y * BoardWidth) + x] = shape
    end

    def timeoutTime() return 1000 / (1 + @level) end
    def squareWidth() return contentsRect().width() / BoardWidth end
    def squareHeight() return contentsRect().height() / BoardHeight end

    def nextPieceLabel=(label)
        @nextPieceLabel = label
    end
    
    def sizeHint()
        return Qt::Size.new(BoardWidth * 15 + frameWidth() * 2,
                      15 + frameWidth() * 2)
    end
    
    def minimumSizeHint()
        return Qt::Size.new(BoardWidth * 5 + frameWidth() * 2,
                      5 + frameWidth() * 2)
    end
    
    def start()
        if @isPaused
            return
        end
    
        @isStarting = true
        @isWaitingAfterLine = false
        @numLinesRemoved = 0
        @numPiecesDropped = 0
        @score = 0
        @level = 1
        clearBoard()
    
        emit linesRemovedChanged(@numLinesRemoved)
        emit scoreChanged(@score)
        emit levelChanged(@level)
    
        newPiece()
        @timer.start(timeoutTime(), self)
    end
    
    def pause()
        if !@isStarting
            return
        end
    
        @isPaused = !@isPaused
        if @isPaused
            @timer.stop()
        else
            @timer.start(timeoutTime(), self)
        end
        update()
    end
    
    def paintEvent(e)
        painter = Qt::Painter.new(self)
        rect = contentsRect()
    
        drawFrame(painter)
    
        if @isPaused
            painter.drawText(rect, Qt::AlignCenter, tr("Pause"))
            painter.end
            return
        end
    
        boardTop = rect.bottom() - BoardHeight*squareHeight()
    
        (0...BoardHeight).each do |i|
            (0...BoardWidth).each do |j|
                shape = shapeAt(j, BoardHeight - i - 1)
                if shape != TetrixPiece::NoShape
                    drawSquare(painter, rect.left() + j * squareWidth(),
                               boardTop + i * squareHeight(), shape)
                end
            end
        end
    
        if @curPiece.shape() != TetrixPiece::NoShape
            (0...4).each do |i|
                x = @curX + @curPiece.x(i)
                y = @curY - @curPiece.y(i)
                drawSquare(painter, rect.left() + x * squareWidth(),
                           boardTop + (BoardHeight - y - 1) * squareHeight(),
                           @curPiece.shape())
            end
        end
        painter.end
    end
    
    def keyPressEvent(event)
        if !@isStarting || @isPaused || @curPiece.shape == TetrixPiece::NoShape
            super
            return
        end
    
        case event.key
        when Qt::Key_Left:
            tryMove(@curPiece, @curX - 1, @curY)
        when Qt::Key_Right:
            tryMove(@curPiece, @curX + 1, @curY)
        when Qt::Key_Down:
            tryMove(@curPiece.rotatedRight, @curX, @curY)
        when Qt::Key_Up:
            tryMove(@curPiece.rotatedLeft, @curX, @curY)
        when Qt::Key_Space:
            dropDown()
        when Qt::Key_D:
            oneLineDown()
        else
            super(event)
        end
    end
    
    def timerEvent(event)
        if event.timerId == @timer.timerId
            if @isWaitingAfterLine
                @isWaitingAfterLine = false
                newPiece()
                @timer.start(timeoutTime(), self)
            else
                oneLineDown()
            end
        else
            super(event)
        end
    end
    
    def clearBoard()
        (0...BoardWidth*BoardHeight).each do |i|
            @board[i] = TetrixPiece::NoShape
        end
    end
    
    def dropDown()
        dropHeight = 0
        newY = @curY
        while newY > 0
            if !tryMove(@curPiece, @curX, newY - 1)
                break
            end
            newY -= 1
            dropHeight += 1
        end
        pieceDropped(dropHeight)
    end
    
    def oneLineDown()
        if !tryMove(@curPiece, @curX, @curY - 1)
            pieceDropped(0)
        end
    end
    
    def pieceDropped(dropHeight)
        (0...4).each do |i|
            x = @curX + @curPiece.x(i)
            y = @curY - @curPiece.y(i)
            setShapeAt(x, y, @curPiece.shape())
        end
    
        @numPiecesDropped += 1
        if @numPiecesDropped % 25 == 0
            @level += 1
            @timer.start(timeoutTime(), self)
            emit levelChanged(@level)
        end
    
        @score += dropHeight + 7
        emit scoreChanged(@score)
        removeFullLines()
    
        if !@isWaitingAfterLine
            newPiece()
        end
    end
    
    def removeFullLines()
        numFullLines = 0
        (BoardHeight - 1).downto(0) do |i|
            lineIsFull = true
    
            (0...BoardWidth).each do |j|
                if shapeAt(j, i) == TetrixPiece::NoShape
                    lineIsFull = false
                    break
                end
            end
    
            if lineIsFull
                numFullLines += 1
                (i...BoardHeight).each do |k|
                    (0...BoardWidth).each do |j|
                        setShapeAt(j, k, shapeAt(j, k + 1))
                    end
                end
                (0...BoardWidth).each do |j|
                    setShapeAt(j, BoardHeight - 1, TetrixPiece::NoShape)
                end
            end
        end
    
        if numFullLines > 0
            @numLinesRemoved += numFullLines
            @score += 10 * numFullLines
            emit linesRemovedChanged(@numLinesRemoved)
            emit scoreChanged(@score)
    
            @timer.start(500, self)
            @isWaitingAfterLine = true
            @curPiece.shape = TetrixPiece::NoShape
            update()
        end
    end
    
    def newPiece()
        @curPiece = @nextPiece
        @nextPiece.setRandomShape()
        showNextPiece()
        @curX = BoardWidth / 2 + 1
        @curY = BoardHeight - 1 + @curPiece.minY()
    
        if !tryMove(@curPiece, @curX, @curY)
            @curPiece.shape = TetrixPiece::NoShape
            @timer.stop()
            @isStarting = false
        end
    end
    
    def showNextPiece()
        if @nextPieceLabel.nil?
            return
        end
    
        dx = @nextPiece.maxX() - @nextPiece.minX() + 1
        dy = @nextPiece.maxY() - @nextPiece.minY() + 1
    
        pixmap = Qt::Pixmap.new(dx * squareWidth(), dy * squareHeight())
        painter = Qt::Painter.new(pixmap)
        painter.fillRect(pixmap.rect(), @nextPieceLabel.palette().background())
    
        (0...4).each do |i|
            x = @nextPiece.x(i) - @nextPiece.minX()
            y = @nextPiece.y(i) - @nextPiece.minY()
            drawSquare(painter, x * squareWidth(), y * squareHeight(),
                       @nextPiece.shape())
        end
        @nextPieceLabel.pixmap = pixmap
        painter.end
    end
    
    def tryMove(newPiece, newX, newY)
        (0...4).each do |i|
            x = newX + newPiece.x(i)
            y = newY - newPiece.y(i)
            if x < 0 || x >= BoardWidth || y < 0 || y >= BoardHeight
                return false
            end
            if shapeAt(x, y) != TetrixPiece::NoShape
                return false
            end
        end
    
        @curPiece = newPiece
        @curX = newX
        @curY = newY
        update()
        return true
    end
    
    def drawSquare(painter, x, y, shape)
        colorTable = [  0x000000, 0xCC6666, 0x66CC66, 0x6666CC,
                        0xCCCC66, 0xCC66CC, 0x66CCCC, 0xDAAA00 ]
    
        color = Qt::Color.fromRgb(colorTable[shape])
        painter.fillRect(x + 1, y + 1, squareWidth() - 2, squareHeight() - 2,
                         Qt::Brush.new(color))
    
        painter.pen = color.light
        painter.drawLine(x, y + squareHeight() - 1, x, y)
        painter.drawLine(x, y, x + squareWidth() - 1, y)
    
        painter.pen = color.dark
        painter.drawLine(x + 1, y + squareHeight() - 1,
                         x + squareWidth() - 1, y + squareHeight() - 1)
        painter.drawLine(x + squareWidth() - 1, y + squareHeight() - 1,
                         x + squareWidth() - 1, y + 1)
    end
end
