## vim:ts=4:et:nowrap
##
##---------------------------------------------------------------------------##
##
## PySol -- a Python Solitaire game
##
## Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer
## Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer
## Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer
## Copyright (C) 1999 Matthew Hohlfeld <hohlfeld@cs.ucsd.edu>
##
## 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 2 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; see the file COPYING.
## If not, write to the Free Software Foundation, Inc.,
## 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
##
## Markus F.X.J. Oberhumer
## <markus@oberhumer.com>
## http://www.oberhumer.com/pysol
##
##---------------------------------------------------------------------------##


# imports
import sys

# PySol imports
if sys.modules.has_key("pysoltk"):
    from gamedb import registerGame, GameInfo, GI
    from util import *
    from stack import *
    from game import Game
    from layout import Layout
    from hint import AbstractHint, DefaultHint, CautiousDefaultHint


# /***********************************************************************
# //
# ************************************************************************/

class LarasGame_Hint(CautiousDefaultHint):
    # FIXME: demo is not too clever in this game
    pass


# /***********************************************************************
# //
# ************************************************************************/

class LarasGame_Talon(WasteTalonStack):
    # Deal a card to each of the RowStacks. Return number of cards dealt.
    def dealRow(self, rows=None, flip=1, reverse=0, frames=-1):
        if rows is None: rows = self.game.s.rows
        old_state = self.game.enterState(self.game.S_DEAL)
        temp = self.dealToStacks(rows, flip, reverse, frames)
        if len(self.cards) > 2:
            self.game.moveMove(1,self,self.game.s.waste,frames=frames)
            self.game.moveMove(1,self,self.game.s.waste,frames=frames)
        self.game.leaveState(old_state)
        return temp+2

    def dealToStacks(self, stacks, flip=1, reverse=0, frames=-1):
        if len(self.cards) == 0:
            return 0
#        assert len(self.cards) >= len(stacks)
#          if reverse:
#              stacks = stacks[:]
#              stacks.reverse()
        for r in stacks:
            if len(self.cards) == 0:
                return 0
            assert not self.getCard().face_up
            assert r is not self
            if flip:
                self.game.flipMove(self)
            self.game.moveMove(1,self,r,frames=frames)
# dealing has extra rules in this game:
# if card rank == card location then add one card to waste
# if card rank == ACE then add two cards to waste
# if card rank == JACK, QUEEN, KIND then add one card to waste
# when done with one pass through stacks add two cards to waste
            if r.getCard().rank == r.id:
                if len(self.cards) == 0:
                    return 0
                self.game.moveMove(1,self,self.game.s.waste,frames=frames)
            if r.getCard().rank == ACE:
                if len(self.cards) < 2:
                    return 0
                self.game.moveMove(1,self,self.game.s.waste,frames=frames)
                self.game.moveMove(1,self,self.game.s.waste,frames=frames)
            if r.getCard().rank == JACK or r.getCard().rank == QUEEN or r.getCard().rank == KING:
                if len(self.cards) == 0:
                    return 0
                self.game.moveMove(1,self,self.game.s.waste,frames=frames)
        return len(stacks)

# dealcards needs to be fixed for the undo bug: when a deal is undone,
# and then the next deal is done, the cards get moved onto the wrong
# stack

    def dealCards(self, sound=0):
        num_cards = 0
        waste = self.game.s.waste
        if self.cards:
            curr_rank = self.getCard().rank
            for i in range(self.game.NUMRESERVES):
                if len(self.game.s.reserves[i].cards) > 0:
                    self.game.moveMove(1, self.game.s.reserves[i], self.game.s.rows[self.game.old_rank], frames=4, shadow=0)
            self.game.flipMove(self)
            self.game.moveMove(1,self,self.game.s.reserves[0],frames=4,shadow=0)
            res_begin = len(self.game.s.rows[curr_rank].cards)+1
            for i in range(len(self.game.s.rows[curr_rank].cards)):
                self.game.moveMove(1, self.game.s.rows[curr_rank], self.game.s.reserves[res_begin-(i+1)], frames=4, shadow=0)
#                self.game.moveMove(1, self.game.s.rows[curr_rank], self.game.s.reserves[self.game.NUMRESERVES-(i+1)], frames=4, shadow=0)
            self.game.old_rank = curr_rank
        elif waste.cards and self.round != self.max_rounds:
            num_cards = len(waste.cards)
            self.game.turnStackMove(waste, self, update_flags=1)
        return num_cards


class LarasGame_RowStack(OpenStack):
    def __init__(self, x, y, game, **cap):
        apply(OpenStack.__init__, (self, x, y, game), cap)
        self.CARD_YOFFSET = 1


class LarasGame_ReserveStack(OpenStack):
    pass


# /***********************************************************************
# // Lara's Game
# ************************************************************************/

class LarasGame(Game):
    Hint_Class = LarasGame_Hint

    #
    # game layout
    #
    NUMRESERVES = 20

    def createGame(self):
        # create layout
        l, s = Layout(self, XOFFSET=10), self.s

        # set window
        self.setSize(l.XM + self.NUMRESERVES*l.XS/2, l.YM + 6*l.YS)

        # extra settings
        self.old_rank = 0

        # create stacks
        x = l.XM + l.XS
        y = l.YM + l.YS
        for i in range(13):
            s.rows.append(LarasGame_RowStack(x, y, self, max_move=1))
            x = x + l.XS
            if i == 4 or i == 9:
                x = l.XM + l.XS
                y = y + l.YS
        x = l.XM
        y = l.YM
        for i in range(4):
            s.foundations.append(SS_FoundationStack(x, y, self, i, dir=-1, base_rank=KING))
            y = y + l.YS
        x = l.XM + l.XS + l.XS
        y = l.YM
        for i in range(4):
            s.foundations.append(SS_FoundationStack(x, y, self, i))
            x = x + l.XS
        for i in range(2):
            x, y = l.XM, l.YM + (5-i)*l.YS
            for i in range(self.NUMRESERVES / 2):
                s.reserves.append(LarasGame_ReserveStack(x, y, self))
                x = x + l.XS
        x = l.XM + 5*l.XS
        y = l.YM + 3*l.YS
        s.talon = LarasGame_Talon(x, y, self, max_rounds=1)
        l.createText(s.talon, "se")
        x = x - l.XS
        s.waste = WasteStack(x, y, self)

        # define stack-groups (not default)
        self.sg.openstacks = s.foundations + s.rows
        self.sg.talonstacks = [s.talon] + [s.waste]
        self.sg.dropstacks = s.rows + s.reserves + [s.waste]

    #
    # game overrides
    #

    def startGame(self):
        frames = 0
        for i in range(8):
            if not self.s.talon.cards:
                break
            if i == 4:
                self.startDealSample()
                frames = 4
            ##print i, len(self.s.talon.cards)
            self.s.talon.dealRow(frames=frames)
        self.moveMove(len(self.s.waste.cards),self.s.waste,self.s.talon,frames=0)

    def shallHighlightMatch(self, stack1, card1, stack2, card2):
        return (card1.suit == card2.suit and
                (card1.rank + 1 == card2.rank or card2.rank + 1 == card1.rank))

    def getHighlightPilesStacks(self):
        return ()

    def _autoDeal(self, sound=1):
        return 0

    #
    # FIXME
    #

    def canUndo(self):
        return 0

    def canSaveGame(self):
        return 0

    def saveGame(self, filename, binmode=1):
        self.notYetImplemented()

    def _restoreGameHook(self, game):
        self.old_rank = game.loadinfo.old_rank

    def _loadGameHook(self, p):
        self.loadinfo.addattr(old_rank=0)    # register extra load var.
        self.loadinfo.old_rank = p.load()

    def _saveGameHook(self, p):
        p.dump(self.old_rank)


# register the game
registerGame(GameInfo(37, LarasGame, "Lara's Game",
                      GI.GT_2DECK_TYPE | GI.GT_CONTRIB | GI.GT_ORIGINAL, 2, 0))

