## 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
##
## 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
    from pysoltk import MfxCanvasText, getFont


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

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


# /***********************************************************************
# // Streets and Alleys
# ************************************************************************/

class StreetsAndAlleys(Game):
    Hint_Class = BeleagueredCastleType_Hint

    #
    # game layout
    #

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

        # set window
        # (set size so that at least 13 cards are fully playable)
        w = max(3*l.XS, l.XS+(playcards-1)*l.XOFFSET)
        x0 = l.XM
        x1 = x0 + w + 2*l.XM
        x2 = x1 + l.XS + 2*l.XM
        x3 = x2 + w + l.XM
        self.setSize(x3, l.YM + 4*l.YS)

        # create stacks
        x, y, = x1, l.YM
        for i in range(4):
            s.foundations.append(SS_FoundationStack(x, y, self, i, max_move=0))
            y = y + l.YS
        for x, y in ((x0, l.YM), (x2, l.YM)):
            for i in range(4):
                stack = RK_RowStack(x, y, self, max_move=1, max_accept=1)
                stack.CARD_XOFFSET, stack.CARD_YOFFSET = l.XOFFSET, 0
                s.rows.append(stack)
                y = y + l.YS
        x, y = self.width - l.XS, self.height - l.YS
        s.talon = InitialDealTalonStack(x, y, self)

        # define stack-groups
        l.defaultStackGroups()

    #
    # game overrides
    #

    def startGame(self):
        for i in range(4):
            self.s.talon.dealRow(frames=0)
        self.startDealSample()
        for i in range(3):
            self.s.talon.dealRowAvail()
        assert len(self.s.talon.cards) == 0


# /***********************************************************************
# // Beleaguered Castle
# ************************************************************************/

class BeleagueredCastle(StreetsAndAlleys):
    def _shuffleHook(self, cards):
        # move Aces to bottom of the Talon (i.e. last cards to be dealt)
        return self._shuffleHookMoveToBottom(cards, lambda c: (c.rank == 0, c.suit))

    def startGame(self):
        for i in range(4):
            self.s.talon.dealRow(frames=0)
        self.startDealSample()
        for i in range(2):
            self.s.talon.dealRow()
        self.s.talon.dealRow(rows=self.s.foundations)
        assert len(self.s.talon.cards) == 0


# /***********************************************************************
# // Citadel
# ************************************************************************/

class Citadel(StreetsAndAlleys):
    def _shuffleHook(self, cards):
        # move Aces to top of the Talon (i.e. first cards to be dealt)
        return self._shuffleHookMoveToTop(cards, lambda c: (c.rank == 0, c.suit))

    # move cards to the Foundations during dealing
    def startGame(self):
        frames = 4
        talon = self.s.talon
        self.startDealSample()
        talon.dealRow(rows=self.s.foundations, frames=frames)
        while talon.cards:
            for r in self.s.rows:
                self.flipMove(talon)
                for s in self.s.foundations:
                    if s.acceptsCards(self, talon.cards[-1:]):
                        self.moveMove(1, talon, s, frames=frames)
                        break
                else:
                    self.moveMove(1, talon, r, frames=frames)
                if not talon.cards:
                    break


# /***********************************************************************
# // Fortress
# ************************************************************************/

class Fortress_RowStack(BasicRowStack):
    def acceptsCards(self, from_stack, cards):
        if not BasicRowStack.acceptsCards(self, from_stack, cards):
            return 0
        if self.cards:
            # check the suit
            if self.cards[-1].suit != cards[0].suit:
                return 0
            # check the rank
            r1, r2 = self.cards[-1].rank, cards[0].rank
            return (r1 + 1) % self.cap.mod == r2 or (r2 + 1) % self.cap.mod == r1
        return 1


class Fortress(Game):
    Layout_Method = Layout.klondikeLayout
    Talon_Class = InitialDealTalonStack
    Foundation_Class = SS_FoundationStack
    RowStack_Class = StackWrapper(Fortress_RowStack, max_accept=1)
    Hint_Class = BeleagueredCastleType_Hint

    #
    # game layout
    #

    def createGame(self, **layout):
        # create layout
        l, s = Layout(self), self.s
        kwdefault(layout, rows=10, waste=0, texts=0, playcards=16)
        apply(self.Layout_Method, (l,), layout)
        self.setSize(l.size[0], l.size[1])
        # create stacks
        s.talon = self.Talon_Class(l.s.talon.x, l.s.talon.y, self)
        if l.s.waste:
            s.waste = WasteStack(l.s.waste.x, l.s.waste.y, self)
        for r in l.s.foundations:
            s.foundations.append(self.Foundation_Class(r.x, r.y, self, suit=r.suit))
        for r in l.s.rows:
            s.rows.append(self.RowStack_Class(r.x, r.y, self))
        # default
        l.defaultAll()
        return l


    #
    # game overrides
    #

    def startGame(self):
        for i in range(3):
            self.s.talon.dealRow(frames=0)
        self.startDealSample()
        for i in range(3):
            self.s.talon.dealRowAvail()
        assert len(self.s.talon.cards) == 0


# /***********************************************************************
# // Chessboard
# ************************************************************************/

class Chessboard_Foundation(SS_FoundationStack):
    def __init__(self, x, y, game, suit, **cap):
        kwdefault(cap, mod=13, min_cards=1, max_move=0)
        apply(SS_FoundationStack.__init__, (self, x, y, game, suit), cap)

    def acceptsCards(self, from_stack, cards):
        if not self.cards:
            if len(cards) != 1 or not cards[0].face_up:
                return 0
            if cards[0].suit != self.cap.base_suit:
                return 0
            for s in self.game.s.foundations:
                if s.cards:
                    return cards[0].rank == s.cards[0].rank
            return 1
        return SS_FoundationStack.acceptsCards(self, from_stack, cards)


class Chessboard_RowStack(Fortress_RowStack):
    def canDropCards(self, stacks):
        if self.game.demo:
            return Fortress_RowStack.canDropCards(self, stacks)
        for s in self.game.s.foundations:
            if s.cards:
                return Fortress_RowStack.canDropCards(self, stacks)
        return (None, 0)


class Chessboard(Fortress):
    Foundation_Class = Chessboard_Foundation
    RowStack_Class = StackWrapper(Chessboard_RowStack, max_accept=1, mod=13)

    def createGame(self):
        l = Fortress.createGame(self)
        tx, ty, ta, tf = l.getTextAttr(self.s.foundations[-1], "e")
        font = getFont("canvas_card", cardw=l.CW)
        self.texts.info = MfxCanvasText(self.canvas, tx + l.XM, ty, anchor=ta, font=font)

    def updateText(self):
        if self.preview > 1:
            return
        t = ""
        for s in self.s.foundations:
            if s.cards:
                t = RANKS[s.cards[0].rank]
                break
        self.texts.info.config(text=t)


# register the game
registerGame(GameInfo(146, StreetsAndAlleys, "Streets and Alleys",
                      GI.GT_BELEAGUERED_CASTLE | GI.GT_OPEN, 1, 0))
registerGame(GameInfo(34, BeleagueredCastle, "Beleaguered Castle",
                      GI.GT_BELEAGUERED_CASTLE | GI.GT_OPEN, 1, 0))
registerGame(GameInfo(145, Citadel, "Citadel",
                      GI.GT_BELEAGUERED_CASTLE | GI.GT_OPEN, 1, 0))
registerGame(GameInfo(147, Fortress, "Fortress",
                      GI.GT_BELEAGUERED_CASTLE | GI.GT_OPEN, 1, 0))
registerGame(GameInfo(148, Chessboard, "Chessboard",
                      GI.GT_BELEAGUERED_CASTLE | GI.GT_OPEN, 1, 0))

