#!/usr/bin/env python
# -*- mode: python; coding: utf-8; -*-
# ---------------------------------------------------------------------------
#
# Copyright (C) 1998-2003 Markus Franz Xaver Johannes Oberhumer
# Copyright (C) 2003 Mt. Hood Playing Card Co.
# Copyright (C) 2005-2009 Skomoroh
#
# 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/>.
#
# ---------------------------------------------------------------------------

from pysollib.game import Game
from pysollib.gamedb import GI, GameInfo, registerGame
from pysollib.hint import AbstractHint
from pysollib.layout import Layout
from pysollib.mfxutil import kwdefault
from pysollib.mygettext import _
from pysollib.pysoltk import Card, MfxCanvasText
from pysollib.settings import TOOLKIT
from pysollib.stack import \
        AbstractFoundationStack, \
        InitialDealTalonStack, \
        OpenStack
from pysollib.util import ANY_SUIT


# ************************************************************************
# * Samegame
# ************************************************************************

class Samegame_Hint(AbstractHint):
    # FIXME: no intelligence whatsoever is implemented here

    def computeHints(self):
        game = self.game
        for r in game.s.rows:
            if r.cards:
                removeStacks = r.getRemoveStacks()
                score = 100 * len(removeStacks)
                if score > 100:
                    self.addHint(score, 1, r, game.s.foundations[0])


class Samegame_Foundation(AbstractFoundationStack):
    def __init__(self, x, y, game, suit=ANY_SUIT, **cap):
        kwdefault(cap, max_move=0, max_accept=0, max_cards=game.NCARDS)
        AbstractFoundationStack.__init__(self, x, y, game, suit, **cap)

    def acceptsCards(self, from_stack, cards):
        return 1

    def canSelect(self):
        return False  # Invisible, off-screen, unselectable


class Samegame_RowStack(OpenStack):
    def clickHandler(self, event):
        if len(self.cards) == 0:
            return False
        self.playMoveMove(1, self.game.s.foundations[0], sound=False)

    def rightclickHandler(self, event):
        return self.clickHandler(event)

    def moveMove(self, ncards, to_stack, frames=-1, shadow=-1):
        assert ncards == 1
        if to_stack in self.game.s.foundations:
            game = self.game
            removeStacks = self.getRemoveStacks()
            if len(removeStacks) < 2:
                return False

            old_state = game.enterState(game.S_FILL)
            game.updateStackMove(self, 2 | 16)
            for stack in removeStacks:
                game.moveMove(1, stack, game.s.foundations[0], frames=0)
            for stack in removeStacks:
                stack.fillStack()
            game.slideStacks()

            if not game.demo:
                game.playSample("drop", priority=200)

            game.updateStackMove(self, 1 | 16)  # for redo
            game.leaveState(old_state)
            return True
        return OpenStack.moveMove(self, ncards, to_stack, frames=frames,
                                  shadow=shadow)

    def fillStack(self):
        self.game.fillStack(self)

    def getRemoveStacks(self):
        removeStacks = [self]
        spotsChecked = 0
        while spotsChecked < len(removeStacks):
            adjacent = self.getAdjacent(removeStacks[spotsChecked].id)
            for adjacentStack in adjacent:
                if adjacentStack not in removeStacks and \
                        len(adjacentStack.cards) > 0 and \
                        adjacentStack.cards[0].suit == self.cards[0].suit:
                    removeStacks.append(adjacentStack)
            spotsChecked += 1
        return removeStacks

    def getAdjacent(self, playSpace):
        cols, rows = self.game.L
        s = self.game.s
        adjacentRows = []
        if playSpace % rows != (rows - 1):
            adjacentRows.append(s.rows[playSpace + 1])

        if playSpace % rows != 0:
            adjacentRows.append(s.rows[playSpace - 1])

        if playSpace + rows < (cols * rows):
            adjacentRows.append(s.rows[playSpace + rows])

        if playSpace - rows > -1:
            adjacentRows.append(s.rows[playSpace - rows])

        return adjacentRows


class AbstractSamegameGame(Game):
    Hint_Class = Samegame_Hint
    RowStack_Class = Samegame_RowStack

    NCOLORS = 3
    NCARDS = 144

    COLORS = (_("Blue"), _("Red"), _("Yellow"), _("Green"),
              _("Purple"), _("Orange"))

    def createGame(self):
        cols, rows = self.L
        assert cols*rows == self.NCARDS

        # start layout
        l, s = Layout(self), self.s
        # dx, dy = 3, -3

        cs = self.app.images.cs
        if cs.version == 6 or cs.mahjongg3d:
            dx = l.XOFFSET
            dy = -l.YOFFSET
            d_x = cs.SHADOW_XOFFSET
            d_y = cs.SHADOW_YOFFSET
            self._delta_x, self._delta_y = dx, -dy
        else:
            dx = 3
            dy = -3
            d_x = 0
            d_y = 0
            self._delta_x, self._delta_y = 0, 0
        # TODO - This should be moved to subsample logic in the future.
        if self.preview > 1:
            d_x /= 2
            d_y /= 2

        font = self.app.getFont("canvas_default")

        # set window size
        dxx, dyy = abs(dx), abs(dy)
        cardw, cardh = l.CW - d_x, l.CH - d_y
        w = l.XM + dxx + cols * cardw + d_x + l.XM + l.XM
        h = l.YM + dyy + rows * cardh + d_y + l.YM
        self.setSize(w, h)

        #
        self.cols = [[] for i in range(cols)]
        cl = range(cols)
        for col in cl:
            for row in range(rows):
                x = l.XM + dxx + col * cardw
                y = l.YM + dyy + row * cardh
                stack = self.RowStack_Class(x, y, self)
                stack.CARD_XOFFSET = 0
                stack.CARD_YOFFSET = 0
                stack.coln, stack.rown = col, row
                s.rows.append(stack)
                self.cols[col].append(stack)

        # create other stacks
        y = l.YM + dyy
        ivx = -l.XS-self.canvas.xmargin
        if TOOLKIT == 'kivy':
            ivx = -1000
        s.foundations.append(Samegame_Foundation(ivx, y, self))
        self.texts.info = MfxCanvasText(self.canvas,
                                        self.width - l.XM, y,
                                        anchor="nw", font=font)
        # the Talon is invisible
        s.talon = InitialDealTalonStack(-l.XS-self.canvas.xmargin,
                                        self.height-dyy, self)
        # Define stack groups
        l.defaultStackGroups()

    def startGame(self):
        assert len(self.s.talon.cards) == self.NCARDS
        # self.s.talon.dealRow(rows = self.s.rows, frames = 0)
        n = 12
        self.s.talon.dealRow(rows=self.s.rows[:self.NCARDS-n], frames=0)
        self.startDealSample()
        self.s.talon.dealRow(rows=self.s.rows[self.NCARDS-n:])
        assert len(self.s.talon.cards) == 0

    def _createCard(self, id, deck, suit, rank, x, y):
        return Card(id, deck, id % self.NCOLORS, id % self.NCOLORS,
                    game=self, x=x, y=y)

    def fillStack(self, stack):
        to_stack = stack
        for from_stack in self.cols[stack.coln][stack.rown+1::-1]:
            if not from_stack.cards:
                continue
            self.moveMove(1, from_stack, to_stack, frames=0)
            to_stack = from_stack

    def slideStacks(self):
        # Slide to the left to fill empty columns.
        numrows = self.L[1]
        card = 0
        emptycols = 0
        for c in range(len(self.cols)):
            iscolempty = True
            colstart = card
            for r in range(numrows):
                if len(self.s.rows[card].cards) > 0:
                    iscolempty = False
                card += 1
            if iscolempty:
                emptycols += 1
            elif emptycols > 0:
                for r in range(colstart, card):
                    if len(self.s.rows[r].cards) > 0:
                        self.moveMove(1, self.s.rows[r],
                                      self.s.rows[r - (numrows * emptycols)],
                                      frames=0)

    def parseCard(self, card):
        return self.COLORS[card.suit]

    def parseStackInfo(self, stack):
        if stack not in self.s.rows:
            return ""
        row = (stack.id % self.L[1]) + 1
        column = (stack.id // self.L[1]) + 1
        return _("Row: %d, Column: %d") % (row, column)

    def getAutoStacks(self, event=None):
        return ((), (), ())


class Samegame3_20x10(AbstractSamegameGame):
    L = (20, 10)
    NCARDS = 200


class Samegame3_15x10(AbstractSamegameGame):
    L = (15, 10)
    NCARDS = 150


class Samegame3_25x15(AbstractSamegameGame):
    L = (25, 15)
    NCARDS = 375


class Samegame4_20x10(Samegame3_20x10):
    NCOLORS = 4


class Samegame4_15x10(Samegame3_15x10):
    NCOLORS = 4


class Samegame4_25x15(Samegame3_25x15):
    NCOLORS = 4


class Samegame5_20x10(Samegame3_20x10):
    NCOLORS = 5


class Samegame5_15x10(Samegame3_15x10):
    NCOLORS = 5


class Samegame5_25x15(Samegame3_25x15):
    NCOLORS = 5


class Samegame6_20x10(Samegame3_20x10):
    NCOLORS = 6


class Samegame6_15x10(Samegame3_15x10):
    NCOLORS = 6


class Samegame6_25x15(Samegame3_25x15):
    NCOLORS = 6


# ************************************************************************
# * register a Samegame type game
# ************************************************************************

def r(id, gameclass, name, rules_filename="samegame.html"):
    gi = GameInfo(id, gameclass, name,
                  GI.GT_SAMEGAME, 1, 0, GI.SL_MOSTLY_SKILL,
                  category=GI.GC_ISHIDO, short_name=name,
                  suits=list(range(1)), ranks=list(range(gameclass.NCARDS)),
                  si={"decks": 1, "ncards": gameclass.NCARDS})
    gi.ncards = gameclass.NCARDS
    gi.rules_filename = rules_filename
    registerGame(gi)
    return gi


r(19000, Samegame3_15x10, "Samegame 3 Colors 15x10")
r(19001, Samegame4_15x10, "Samegame 4 Colors 15x10")
r(19002, Samegame5_15x10, "Samegame 5 Colors 15x10")
r(19003, Samegame6_15x10, "Samegame 6 Colors 15x10")
r(19004, Samegame3_20x10, "Samegame 3 Colors 20x10")
r(19005, Samegame4_20x10, "Samegame 4 Colors 20x10")
r(19006, Samegame5_20x10, "Samegame 5 Colors 20x10")
r(19007, Samegame6_20x10, "Samegame 6 Colors 20x10")
r(19008, Samegame3_25x15, "Samegame 3 Colors 25x15")
r(19009, Samegame4_25x15, "Samegame 4 Colors 25x15")
r(19010, Samegame5_25x15, "Samegame 5 Colors 25x15")
r(19011, Samegame6_25x15, "Samegame 6 Colors 25x15")

del r
