#  gcompris - connect4
#
# Copyright (C) 2005, 2008 Laurent Lacheny
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 3 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, see <http://www.gnu.org/licenses/>.

#
# The IA code comes from the project 4stattack by Jeroen Vloothuis
# http://forcedattack.sourceforge.net/
#

import gobject
import goocanvas
import gcompris
import gcompris.utils
import gcompris.skin
import gcompris.bonus
import gcompris.anim
import gtk
import gtk.gdk
import cairo
from gcompris import gcompris_gettext as _

from  connect4p import rules
from  connect4p import human
from  connect4p import minmax
from  connect4p import board
# ----------------------------------------
#

class Gcompris_connect4:
    """Connect 4 stones"""


    def __init__(self, gcomprisBoard):
        self.gcomprisBoard = gcomprisBoard

    def start(self):
        self.boardSize = 490.0
        self.nbColumns = 7
        self.nbLines = 6
        self.stoneSize = 70.0
        self.maxVictory = 3
        self.maxLevel = 4
        self.boardItem = None
        self.timerAnim = 0
        self.humanVictory = 0
        self.endAnimCallback = None
        self.gcomprisBoard.level=1
        self.gcomprisBoard.maxlevel=9
        self.gcomprisBoard.sublevel=1
        self.gcomprisBoard.number_of_sublevel=1
        self.mode = (self.gcomprisBoard.mode == "2players") and 2 or 1
        if self.mode == 1:
            board_bar = gcompris.BAR_LEVEL
            gcompris.bar_set_level(self.gcomprisBoard)
        else:
            board_bar = 0
        # Create our rootitem. We put each canvas item in it so at the end we
        # only have to kill it. The canvas deletes all the items it contains automaticaly.
        self.rootitem = goocanvas.Group(parent =  self.gcomprisBoard.canvas.get_root_item())

        board_bar = board_bar | gcompris.BAR_REPEAT
        gcompris.bar_set(board_bar)
        gcompris.bar_location(gcompris.BOARD_WIDTH - 230, -1, 0.7)

        selector = \
            goocanvas.Image(
                parent = self.rootitem,
            pixbuf = gcompris.utils.load_pixmap("connect4/back.png"),
            x=0.0,
            y=0.0
            )
        selector.connect("motion_notify_event", self.columnItemEvent, 0)
        selector.connect("button_press_event", self.columnItemEvent, 0)

        if self.mode == 1:
            self.prof = \
                goocanvas.Image(
                    parent = self.rootitem,
                pixbuf = gcompris.utils.load_pixmap("connect4/tux-teacher.png"),
                x=10,
                y=350.0
                )
            self.prof.connect("button_press_event", self.profItemEvent, 0)
            gcompris.utils.item_focus_init(self.prof, None)

        self.timericon = gcompris.anim.CanvasItem( gcompris.anim.Animation("connect4/sablier.txt"),
            self.rootitem )
        self.timericon.goocanvas.props.visibility = goocanvas.ITEM_INVISIBLE

        self.player_stone = None

        for i in range(2):
            goocanvas.Image(
                parent = self.rootitem,
                pixbuf = gcompris.utils.load_pixmap("connect4/stone_%d.png" % (i+1)),
                x= 25,
                y=175 + i * 73 )
        self.score_item = \
            goocanvas.Text(
                parent = self.rootitem,
                font=gcompris.skin.get_font("gcompris/board/huge bold"),
                x=60,
                y=250,
                anchor = gtk.ANCHOR_CENTER,
                fill_color_rgba=0xFF0000FFL)

        self.update_scores((0, 0))

        self.newGame()

    def end(self):
        if self.timerAnim:
            gobject.source_remove(self.timerAnim)

        del self.timericon

        # Remove the root item removes all the others inside it
        self.rootitem.remove()
        self.boardItem.remove()

    def ok(self):
        pass

    def repeat(self):
        if max(self.scores) >= self.maxVictory:
            if self.scores[0] >= self.maxVictory and self.mode == 1:
                if self.gcomprisBoard.level < self.maxLevel:
                    self.set_level(self.gcomprisBoard.level+1)
                else:
                    self.end()
                    gcompris.end_board()
            else:
                self.update_scores([0, 0])
                self.newGame()
        else:
            self.newGame()

    def config(self):
        pass
    def update_scores(self, scores=None):
        if scores: self.scores = list(scores)
        txt = str(self.scores[0]) + "\n\n" + str(self.scores[1])
        self.score_item.props.text = txt

    def stone_init(self):
        if self.player_stone:
            self.player_stone.remove()
        self.player_stone = \
            goocanvas.Image(
                parent = self.rootitem,
            pixbuf = gcompris.utils.load_pixmap("connect4/stone_%d.png"
                                                % self.cur_player),
            x=0,
            y=-20 )
        self.update_stone2()
    def update_stone2(self):
        self.player_stone.set_property("x", (gcompris.BOARD_WIDTH - self.boardSize) /2 +
            self.keyb_column * self.boardSize / self.nbColumns)

    def key_press(self, keyval, commit_str, preedit_str):
        if self.mode == 1 and self.cur_player == 2: # AI playing
            return False
        if self.cur_player == 0 or self.timerAnim:  # Game over or Timer animate
            return False
        key = gtk.gdk.keyval_name(keyval)
        if key == "Left":
            self.keyb_column = max(self.keyb_column-1, 0)
            self.update_stone2()
        elif key == "Right":
            self.keyb_column = min(self.keyb_column+1, self.nbColumns-1)
            self.update_stone2()
        elif key =="Down" or commit_str == " ":
            if self.play(self.player1, self.cur_player, self.keyb_column):
                self.player_stone.props.visibility = goocanvas.ITEM_INVISIBLE
        return False

    def pause(self, pause):
        pass

  # Called by gcompris when the user click on the level icon
    def set_level(self, level):
        if level > self.maxLevel: level = 1
        self.gcomprisBoard.level=level
        self.gcomprisBoard.sublevel=1
        gcompris.bar_set_level(self.gcomprisBoard)
        self.player1.setDifficulty(level)
        self.player2.setDifficulty(level)
        self.update_scores([0, 0])
        self.newGame()

  # End of Initialisation
  # ---------------------

    def newGame(self):
        if self.timerAnim:
            gobject.source_remove(self.timerAnim)
            self.timerAnim = None
        if self.boardItem:
            self.boardItem.remove()
        self.boardItem = \
            goocanvas.Group(
                parent = self.gcomprisBoard.canvas.get_root_item()
                )
        self.boardItem.translate((gcompris.BOARD_WIDTH-self.boardSize)/2.0,
                                 50.0)
        self.player1 = human.Human(self.gcomprisBoard.level)
        if self.mode == 1:
            self.player2 = minmax.MinMax(self.gcomprisBoard.level, self.refreshScreen)
        self.board = board.Board()
        self.gamewon = False
        self.winLine = None
        try:
            del self.redLine
        except:
            pass
        self.keyb_column = 0
        self.cur_player = 1
        self.stone_init()
        self.update_stone2()

        self.timericon.goocanvas.props.visibility = goocanvas.ITEM_INVISIBLE
        if self.mode == 1:
            self.prof.props.visibility = goocanvas.ITEM_VISIBLE

    def columnItemEvent(self, widget, target, event, columns):
        (x, y)= self.gcomprisBoard.canvas.\
            convert_from_item_space(widget, event.x, event.y)
        if self.mode == 1 and self.cur_player == 2: # AI playing
            return False
        if self.cur_player == 0 or self.timerAnim:  # Game over or Timer animate
            return False
        if event.type == gtk.gdk.BUTTON_PRESS and event.button == 1:
            column = int((x - (gcompris.BOARD_WIDTH-self.boardSize)/2.0) // self.stoneSize)
            if 0 <= column < self.nbColumns:
                if self.play(self.player1, self.cur_player, column):
                    self.timericon.goocanvas.props.visibility = goocanvas.ITEM_VISIBLE
                    self.player_stone.props.visibility = goocanvas.ITEM_INVISIBLE
        elif event.type == gtk.gdk.MOTION_NOTIFY:
            column = int((x - (gcompris.BOARD_WIDTH-self.boardSize)/2.0) // self.stoneSize)
            if 0 <= column < self.nbColumns:
                self.keyb_column = column
                self.update_stone2()
        return False

    def profItemEvent(self, widget, target, event, column):
        if event.type == gtk.gdk.BUTTON_PRESS:
            self.cur_player=1
            self.prof.props.visibility = goocanvas.ITEM_INVISIBLE
            self.player_stone.props.visibility = goocanvas.ITEM_INVISIBLE
            self.play_next()

    def play_next(self):
        if self.cur_player == 1:
            self.cur_player = 2
            if self.mode == 1:
                # IA play
                self.timericon.goocanvas.props.visibility = goocanvas.ITEM_VISIBLE
                self.play(self.player2, 2, 0)
            else:
                # player 2
                self.stone_init()
        else:
            # player 1
            self.cur_player = 1
            self.stone_init()

    def refreshScreen(self):
        gtk.main_iteration(block=False)
        pass

    def play(self, player, numPlayer, column):
        if self.mode == 1:
            self.prof.props.visibility = goocanvas.ITEM_INVISIBLE
        move = player.doMove(self.board, numPlayer, column)

        if isinstance(move, int) and rules.isMoveLegal(self.board, move):
            self.board.move(move, numPlayer)
            self.drawBoard(self.board)
            return True
        return False

    def drawBoard(self, board):
        stone = self.board.state[self.board.last_move][-1]
        x = self.board.last_move
        y = len(self.board.state[self.board.last_move])-1
        file = "connect4/stone_%d.png" % stone

        self.stone = \
            goocanvas.Image(
                parent = self.boardItem,
            pixbuf = gcompris.utils.load_pixmap(file),
            x=x*(self.boardSize/self.nbColumns),
            y=0
            )
        self.stone.connect("button_press_event", self.columnItemEvent, 0)
        self.stone.connect("motion_notify_event", self.columnItemEvent, 0)
        self.countAnim = self.nbLines-y
        self.timerAnim = gobject.timeout_add(200, self.animTimer)
        self.timericon.goocanvas.props.visibility = goocanvas.ITEM_VISIBLE

    def animTimer(self):
        self.countAnim -= 1
        if self.countAnim > 0:
            y= self.stone.get_property('y')
            self.stone.set_property('y', y + (self.boardSize/self.nbColumns))
            self.timerAnim = gobject.timeout_add(200, self.animTimer)
        else:
            self.timerAnim = None
            self.timericon.goocanvas.props.visibility = goocanvas.ITEM_INVISIBLE
            self.winLine = rules.isWinner(self.board, self.cur_player)
            if self.winLine:
                self.winner(self.cur_player)
            elif rules.isBoardFull(self.board):
                self.winner(0)
            else:
                self.play_next()

    def winner(self, player):
        self.gamewon = True
        self.cur_player = 0

        # It's a draw, no line to draw
        if player == 0:
            gcompris.bonus.display(gcompris.bonus.DRAW, gcompris.bonus.FLOWER)
            return

        # Display the winner line
        self.scores[player-1] += 1
        self.update_scores()

        points = goocanvas.Points([(self.winLine[0][0]*(self.boardSize/self.nbColumns)+self.stoneSize/2,
                                    (self.boardSize/self.nbColumns)*(self.nbLines-1-self.winLine[0][1])+self.stoneSize/2),
                                   (self.winLine[1][0]*(self.boardSize/self.nbColumns)+self.stoneSize/2,
                                    (self.boardSize/self.nbColumns)*(self.nbLines-1-self.winLine[1][1])+self.stoneSize/2)
                                   ])

        self.redLine = \
            goocanvas.Polyline(
            parent = self.boardItem,
            stroke_color_rgba=0xFF0000FFL,
            points = points,
            line_width = 8,
            line_cap = cairo.LINE_CAP_ROUND
            )
        if player == 1 or self.mode == 2:
            gcompris.bonus.display(gcompris.bonus.WIN, gcompris.bonus.FLOWER)
        elif player == 2:
            gcompris.bonus.display(gcompris.bonus.LOOSE, gcompris.bonus.FLOWER)

