##---------------------------------------------------------------------------##
##
## PySol -- a Python Solitaire game
##
## 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.
##
##---------------------------------------------------------------------------##

__all__ = [
    'AbstractFlowerGame',
    'Queue_Hint',
    'Flower_FoundationStack',
    'Hanafuda_SS_FoundationStack',
    'FlowerClock_Foundation',
    'Gaji_Foundation',
    'Pagoda_Foundation',
    'MatsuKiri_Foundation',
    'GreatWall_FoundationStack',
    'FourWinds_Foundation',
    'Queue_Foundation',
    'Flower_OpenStack',
    'Hanafuda_SequenceStack',
    'Oonsoo_SequenceStack',
    'FlowerClock_RowStack',
    'Gaji_RowStack',
    'Matsukiri_RowStack',
    'Samuri_RowStack',
    'GreatWall_RowStack',
    'FourWinds_RowStack',
    'Queue_BraidStack',
    'Queue_RowStack',
    'Queue_ReserveStack',
    'JapaneseGarden_RowStack',
    'HanafudaRK_RowStack',
    ]


import sys, math

from pysollib.util import *
from pysollib.mfxutil import kwdefault
from pysollib.stack import *
from pysollib.game import Game
from pysollib.layout import Layout
from pysollib.hint import AbstractHint, DefaultHint, CautiousDefaultHint
from pysollib.pysoltk import MfxCanvasText


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

class AbstractFlowerGame(Game):
    SUITS = (_("Pine"), _("Plum"), _("Cherry"), _("Wisteria"),
             _("Iris"), _("Peony"), _("Bush Clover"), _("Eularia"),
             _("Chrysanthemum"), _("Maple"), _("Willow"), _("Paulownia"))
    def shallHighlightMatch(self, stack1, card1, stack2, card2):
        return ((card1.suit == card2.suit)
                and ((card1.rank + 1 == card2.rank)
                or (card1.rank - 1 == card2.rank)))

class Queue_Hint(DefaultHint):
    pass



# /***********************************************************************
#  * Flower Foundation Stacks
#  ***********************************************************************/

class Flower_FoundationStack(AbstractFoundationStack):

    def __init__(self, x, y, game, suit, **cap):
        kwdefault(cap, max_cards=12, max_move=0, base_rank=ANY_RANK, base_suit=ANY_SUIT)
        apply(AbstractFoundationStack.__init__, (self, x, y, game, suit), cap)

    def updateText(self):
        AbstractFoundationStack.updateText(self)
        self.game.updateText()

    def isHanafudaSequence(self, s, strictness=1):
        for i in range(len(s) - 1):
            if s[i].suit != s[i + 1].suit:
                return 0
            if s[i].suit == 10 or strictness:
                a, b = s[i].rank, s[i + 1].rank
            else:
                a, b = self.swapTrashCards(s[i], s[i + 1])
            if a + 1 != b:
                return 0
        return cardsFaceUp(s)

    def swapTrashCards(self, carda, cardb):
        a, b = carda.rank, cardb.rank
        if a == 3 and b == 2:
            a, b = 2, 3
        elif a == 1 and b == 3:
            b = 2
        return a, b


class Hanafuda_SS_FoundationStack(Flower_FoundationStack):

    def acceptsCards(self, from_stack, cards):
        if not self.basicAcceptsCards(from_stack, cards):
            return 0
        stackcards = self.cards
        if not stackcards:
            return cards[0].rank == 3
        return self.isHanafudaSequence([cards[0], stackcards[-1]])

    def getBottomImage(self):
        return self.game.app.images.getSuitBottom(self.cap.suit)


class FlowerClock_Foundation(Flower_FoundationStack):

    def acceptsCards(self, from_stack, cards):
        if not self.basicAcceptsCards(from_stack, cards):
            return 0
        stackcards = self.cards
        if not stackcards:
            return cards[0].rank == 3
        if not stackcards[-1].suit == cards[0].suit:
            return 0
        return stackcards[-1].rank == cards[0].rank + 1


class Gaji_Foundation(Flower_FoundationStack):

    def __init__(self, x, y, game, suit, **cap):
        kwdefault(cap, max_move=1, min_cards=1, max_accept=1, base_suit=ANY_SUIT)
        apply(Flower_FoundationStack.__init__, (self, x, y, game, suit), cap)
        self.CARD_YOFFSET = self.game.app.images.CARD_YOFFSET

    def acceptsCards(self, from_stack, cards):
        if not self.basicAcceptsCards(from_stack, cards):
            return 0
        stackcards = self.cards
        return ((((stackcards[-1].suit + 1) % 12) == cards[0].suit)
                    and (stackcards[-1].rank == cards[0].rank))

    def getBottomImage(self):
        return self.game.app.images.getLetter(self.cap.base_rank)


class Pagoda_Foundation(Flower_FoundationStack):

    def acceptsCards(self, from_stack, cards):
        if not self.basicAcceptsCards(from_stack, cards):
            return 0
        stackcards = self.cards
        if not stackcards:
            return cards[0].rank == 3
        a, b = stackcards[-1].rank, cards[0].rank
        if len(stackcards) < 4:
            return a - 1 == b
        elif len(stackcards) > 4:
            return a + 1 == b
        else:
            return a == b

    def getBottomImage(self):
        return self.game.app.images.getSuitBottom(self.cap.suit)


class MatsuKiri_Foundation(Flower_FoundationStack):

    def __init__(self, x, y, game, suit, **cap):
        kwdefault(cap, max_move=0, max_cards=48, max_accept=4, min_accept=4)
        apply(AbstractFoundationStack.__init__, (self, x, y, game, suit), cap)
        self.CARD_YOFFSET = self.game.app.images.CARDH / 10

    def acceptsCards(self, from_stack, cards):
        if not self.basicAcceptsCards(from_stack, cards):
            return 0
        if not self.isHanafudaSequence(cards, 0):
            return 0
        stackcards = self.cards
        if not stackcards:
            return cards[0].suit == 0
        return stackcards[-1].suit + 1 == cards[0].suit

##     def getBottomImage(self):
##         return self.game.app.images.getBraidBottom()


class GreatWall_FoundationStack(Flower_FoundationStack):

    def __init__(self, x, y, game, suit, **cap):
        kwdefault(cap, max_cards=48, max_move=1, min_accept=1, max_accept=1)
        apply(Flower_FoundationStack.__init__, (self, x, y, game, suit), cap)
        self.CARD_YOFFSET = self.game.app.images.CARDH / 20

    def acceptsCards(self, from_stack, cards):
        if not self.basicAcceptsCards(from_stack, cards):
            return 0
        stackcards = self.cards
        if stackcards:
            return ((stackcards[-1].suit + 1) % 12 == cards[0].suit
                    and cards[0].rank == self.cap.base_rank)
        else:
            return cards[0].suit == 0

    def getBottomImage(self):
        return self.game.app.images.getLetter(self.cap.base_rank)


class FourWinds_Foundation(Flower_FoundationStack):

    def acceptsCards(self, from_stack, cards):
        if not self.basicAcceptsCards(from_stack, cards):
            return 0
        stackcards = self.cards
        if not (cards[0].rank == self.cap.base_rank):
            return 0
        if not stackcards:
            return (cards[0].suit == 0)
        else:
            return (cards[0].suit == stackcards[-1].suit + 1)

##     def getBottomImage(self):
##         return self.game.app.images.getLetter(self.cap.base_rank)


class Queue_Foundation(AbstractFoundationStack):
    def __init__(self, x, y, game, suit, **cap):
        kwdefault(cap, mod=12, dir=0, base_suit=ANY_SUIT, max_move=0)
        apply(AbstractFoundationStack.__init__, (self, x, y, game, suit), cap)

    def acceptsCards(self, from_stack, cards):
        if not AbstractFoundationStack.acceptsCards(self, from_stack, cards):
            return 0
        if not self.cards:
            return cards[0].suit == self.game.base_card.suit
        stack_dir = self.game.getFoundationDir()
        if stack_dir == 0:
            card_dir = (cards[0].suit - self.cards[-1].suit) % 12
            return card_dir in (1, 11)
        else:
            return (self.cards[-1].suit + stack_dir) % 12 == cards[0].suit

    def getBottomImage(self):
        return self.game.app.images.getLetter(self.cap.base_rank)




# /***********************************************************************
#  * Flower Row Stacks
#  ***********************************************************************/

class Flower_OpenStack(OpenStack):

    def __init__(self, x, y, game, yoffset, **cap):
        kwdefault(cap, max_move=99, max_cards=99, max_accept=99, base_rank=0, dir=1)
        apply(OpenStack.__init__, (self, x, y, game), cap)
        self.CARD_YOFFSET = yoffset

    def isHanafudaSequence(self, cards, strictness=1):
        c1 = cards[0]
        for c2 in cards[1:]:
            if c1.suit != c2.suit:
                return 0
            if c1.suit == 10 or strictness:
                a, b = c1.rank, c2.rank
            else:
                a, b = self.swapTrashCards(c1, c2)
            if a + self.cap.dir != b:
                return 0
            c1 = c2
        return 1

    def swapTrashCards(self, carda, cardb):
        a, b = carda.rank, cardb.rank
        if a == 3 and b == 2:
            a, b = 2, 3
        elif a == 1 and b == 3:
            b = 2
        return a, b


class Hanafuda_SequenceStack(Flower_OpenStack):

    def acceptsCards(self, from_stack, cards):
        if (not self.basicAcceptsCards(from_stack, cards)
            or not self.isHanafudaSequence(cards)):
            return 0
        stackcards = self.cards
        if not len(stackcards):
            return cards[0].rank == 0 or self.cap.base_rank == ANY_RANK
        return self.isHanafudaSequence([stackcards[-1], cards[0]])


class Oonsoo_SequenceStack(Flower_OpenStack):

    def acceptsCards(self, from_stack, cards):
        if (not self.basicAcceptsCards(from_stack, cards)
            or not self.isHanafudaSequence(cards, 0)):
            return 0
        stackcards = self.cards
        if not len(stackcards):
            return cards[0].rank == 0 or self.cap.base_rank == ANY_RANK
        return self.isHanafudaSequence([stackcards[-1], cards[0]], 0)


class FlowerClock_RowStack(Flower_OpenStack):

    def acceptsCards(self, from_stack, cards):
        if not self.basicAcceptsCards(from_stack, cards):
            return 0
        stackcards = self.cards
        if not len(stackcards):
            return 1
        return stackcards[-1].rank + 1 == cards[0].rank


class Gaji_RowStack(Flower_OpenStack):

    def acceptsCards(self, from_stack, cards):
        if not self.basicAcceptsCards(from_stack, cards):
            return 0
        stackcards = self.cards
        if ((not len(stackcards))
                or ((stackcards[-1].suit == 10) and (stackcards[-1].rank == 3))
                or ((cards[0].suit == 10) and (cards[0].rank == 3))):
            return 1
        elif stackcards[-1].suit != cards[0].suit:
            return 0
        a, b = self.swapTrashCards(stackcards[-1], cards[0])
        return a + 1 == b


class Matsukiri_RowStack(Flower_OpenStack):

    def acceptsCards(self, from_stack, cards):
        if not self.basicAcceptsCards(from_stack, cards):
            return 0
        stackcards = self.cards
        if not stackcards:
            return cards[0].rank == 0
        if cards[0].suit != stackcards[-1].suit:
            return 0
        if stackcards[-1].suit == 10 or self.game.Strictness:
            a, b = stackcards[-1].rank, cards[0].rank
        else:
            a, b = self.swapTrashCards(stackcards[-1], cards[0])
        return a + 1 == b

    def canDropCards(self, stacks):
        pile = self.getPile()
        if not pile or len(pile) <= 3:
            return (None, 0)
        f = self.game.s.foundations[0]
        if not f.cards:
            suit = 0
        else:
            suit = f.cards[-1].suit + 1
        if not pile[-1].suit == suit or not self.isHanafudaSequence(pile[-4:], 0):
            return (None, 0)
        return (f, 4)


class Samuri_RowStack(Flower_OpenStack):

    def acceptsCards(self, from_stack, cards):
        if not self.basicAcceptsCards(from_stack, cards):
            return 0
        stackcards = self.cards
        if not stackcards:
            return cards[0].rank == 0
        return stackcards[-1].suit == cards[0].suit and stackcards[-1].rank + 1 == cards[0].rank


class GreatWall_RowStack(Flower_OpenStack):

    def acceptsCards(self, from_stack, cards):
        if not self.basicAcceptsCards(from_stack, cards):
            return 0
        stackcards = self.cards
        if not stackcards:
            return cards[0].rank == 0
        if cards[0].rank == stackcards[-1].rank:
            return stackcards[-1].suit == (cards[0].suit + 1) % 12
        a, b = self.swapTrashCards(stackcards[-1], cards[0])
        return a + 1 == b


class FourWinds_RowStack(Flower_OpenStack):

    def acceptsCards(self, from_stack, cards):
        if not self.basicAcceptsCards(from_stack, cards):
            return 0
        stackcards = self.cards
        if len(cards) - 1 or len(stackcards) >= 3:
            return 0
        if not stackcards:
            return 1
        return ((cards[0].rank == stackcards[-1].rank) and (cards[0].suit == stackcards[-1].suit - 1))

    def getBottomImage(self):
        return self.game.app.images.getReserveBottom()


class Queue_BraidStack(OpenStack):

    def __init__(self, x, y, game, yoffset):
        OpenStack.__init__(self, x, y, game)
        CW = self.game.app.images.CARDW
        self.CARD_YOFFSET = int(self.game.app.images.CARD_YOFFSET * yoffset)
        # use a sine wave for the x offsets
        # compensate for card width
        offset = self.game.app.images.CARDW / 1.7
        self.CARD_XOFFSET = []
        j = 1
        for i in range(20):
            self.CARD_XOFFSET.append(int(math.sin(j) * offset))
            j = j + .9


class Queue_RowStack(ReserveStack):

    def fillStack(self):
        if not self.cards and self.game.s.braid.cards:
            self.game.moveMove(1, self.game.s.braid, self)

    def getBottomImage(self):
        return self.game.app.images.getBraidBottom()


class Queue_ReserveStack(ReserveStack):

    def acceptsCards(self, from_stack, cards):
        if from_stack is self.game.s.braid or from_stack in self.game.s.rows:
            return 0
        return ReserveStack.acceptsCards(self, from_stack, cards)

    def getBottomImage(self):
        return self.game.app.images.getTalonBottom()


class JapaneseGarden_RowStack(Flower_OpenStack):

    def acceptsCards(self, from_stack, cards):
        if (not self.basicAcceptsCards(from_stack, cards)
                or not from_stack in self.game.s.rows):
            return 0
        stackcards = self.cards
        if not len(stackcards):
            return 1
        return stackcards[-1].rank + 1 == cards[0].rank


class HanafudaRK_RowStack(Flower_OpenStack):

    def acceptsCards(self, from_stack, cards):
        if (not self.basicAcceptsCards(from_stack, cards)
                or not isRankSequence(cards, dir=1)):
            return 0
        stackcards = self.cards
        if not len(stackcards):
            return 1
        return stackcards[-1].rank + 1 == cards[0].rank