#!/usr/bin/env python

import random

from pygsear.Game import Game
from pygsear.Drawable import MultiImage, Multi, Image, AnimatedImage
from pygsear.Event import MOUSEBUTTONDOWN_Event, TIMEOUT_Event
from pygsear.Widget import SpriteButton, ImageButton, Dialog_OK
from pygsear.Cursor import MultiImageCursor
from pygsear import conf


class Yes(AnimatedImage):
    dirname = 'yes'


class Sorry(Image):
    filename = 'sorry.png'


class Nextbutton(ImageButton):
    filename = 'next.png'


class Againbutton(ImageButton):
    filename = 'again.png'


class Help(Dialog_OK):
    message = """pymm

The object of pymm is to guess the pattern of colors
set by the computer.

To make your guess, click on a color in the pallette
at the bottom of the screen, then click in one of the
spaces in the first empty row on the left.

When your guess is ready, click on the check.

For each ball that is the correct color in the correct
position, you will see a black ball in the row on the
right. For each ball that is the correct color but in
the wrong position, you will see a white ball.

You can also right click on the pallette to put that
color in the next empty space, or middle click on a
previous row to copy the whole row.

"""

class Helpbutton(ImageButton):
    filename = 'help.png'


class BallCursor(MultiImageCursor):
    filenames = ['ball_empty.png', 'ball_blue.png', 'ball_green.png',
                    'ball_purple.png', 'ball_red.png', 'ball_teal.png',
                    'ball_yellow.png', 'ball_white.png', 'ball_black.png']


class Ball(MultiImage):
    colors = ['blue', 'green', 'purple', 'red', 'teal', 'yellow']
    filenames = ['ball_empty.png', 'ball_blue.png', 'ball_green.png',
                    'ball_purple.png', 'ball_red.png', 'ball_teal.png',
                    'ball_yellow.png', 'ball_white.png', 'ball_black.png', 'ball_q.png']

    def __init__(self, color='empty', dx=None):
        MultiImage.__init__(self)

        if dx is not None:
            self.stretch(dx=dx)

        self.change_color(color)

    def random(self):
        self.color = random.choice(Ball.colors)
        image = 'ball_%s.png' % self.color
        self.flip(image)

    def change_color(self, color):
        self.color = color
        image = 'ball_%s.png' % color
        self.flip(image)

    def conceal(self):
        image = 'ball_q.png'
        self.flip(image)

    def reveal(self):
        self.change_color(self.color)


class BallRack(Multi):
    def __init__(self, cols, turns, row):
        Multi.__init__(self)
        self.cols = cols
        self.turns = turns
        if turns is not None:
            self.dx = int(60 * (8.0/turns)) - 64
        else:
            self.dx = 0
        self.size = 60 + self.dx
        self.row = row
        self.balls = []

    def empty(self):
        Multi.empty(self)
        for pos in range(self.cols):
            ball = Ball(dx=self.dx)
            self.addSprite(ball, xOffset=self.size*pos)
            self.balls.append(ball)

    def compare(self, other):
        a = [ball.color for ball in self.balls]
        b = [ball.color for ball in other.balls]

        exact = 0
        inexact = 0

        c = 0
        for color in a:
            if color == b[c]:
                exact += 1
                a[c] = None
                b[c] = None

            c += 1

        c = 0
        for color in a:
            if color is not None and b.count(color) > 0:
                inexact += 1
                b[b.index(color)] = None
            c += 1

        return (exact, inexact)


class TargetRack(BallRack):
    def __init__(self, cols):
        BallRack.__init__(self, cols, None, None)
        self.random()
        self.conceal()
        self.center(y=10)

    def random(self):
        for pos in range(self.cols):
            ball = Ball()
            ball.random()
            self.addSprite(ball, xOffset=self.size*pos)
            self.balls.append(ball)

    def conceal(self):
        for ball in self.balls:
            ball.conceal()

    def reveal(self):
        for ball in self.balls:
            ball.reveal()


class PalletteRack(BallRack):
    def __init__(self):
        size = len(Ball.colors)
        BallRack.__init__(self, size, None, None)
        for pos, color in enumerate(Ball.colors):
            ball = Ball(color)
            self.balls.append(ball)
            self.addSprite(ball, xOffset=60*pos)

        self.center(y=-10)

    #     def leftclick(self, ev):
    #         if self.rect.collidepoint(ev.pos):
    #             conf.game.active_color = self.color


class Row(Multi):
    def __init__(self, cols, turns, row):
        Multi.__init__(self)

        self.ballrack = BallRack(cols, turns, row)
        self.ballrack.empty()
        self.addSprite(self.ballrack)

        self.pegrack = BallRack(cols, turns, row)
        self.pegrack.empty()
        xoffset = self.ballrack.size + 20
        self.addSprite(self.pegrack, xOffset=xoffset*cols)

        y = -(80+(self.ballrack.size*row))
        self.center(y=y)


class Board(Game):
    levels = [(3, 8), (4, 10), (4, 8), (5, 12), (6, 15), (7, 18)]

    def initialize(self):
        self.set_title('pymm')
        self.set_background(color=(102, 84, 51))

        # For cursor that looks like ball.
        # This does not work very well... needs integration with
        # Event and Widget, plus it is very slow to draw.
        #
        #         self.hideMouse()
        #         cursor = BallCursor()
        #         cursor.set_hotspot((20, 20))
        #         self.sprites.add(cursor, level=1)
        #         self.cursor = cursor
        #

        self.make_pallette()

        self.level = 0
        self.set_level()

        self.yes = Yes()
        self.yes.center()
        self.sorry = Sorry()
        self.sorry.center()

        self.nextbutton = Nextbutton(callback=self.next_level)
        self.nextbutton.center(x=20, y=20)
        self.againbutton = Againbutton(callback=self.same_level)
        self.againbutton.center(x=20, y=20)

        self.helpbutton = Helpbutton(callback=self.show_help)
        self.helpbutton.center(x=-20, y=20)
        self.sprites.add(self.helpbutton)
        self.events.add(self.helpbutton.events)

    def set_level(self):
        try:
            cols, turns = self.levels[self.level]
        except IndexError:
            cols, turns = self.levels[-1]

        self.cols = cols
        self.turns = turns

        self.target = TargetRack(cols)
        self.sprites.add(self.target)

        self.make_rows()

        self.active_row = 0
        self.active_row_events = self.addEventGroup()
        self.set_active_row()

    def make_pallette(self):
        self.pallette = PalletteRack()
        self.sprites.add(self.pallette)

        for ball in self.pallette.balls:
            ev = MOUSEBUTTONDOWN_Event(button=1, callback=self.leftclick_pallette, ball=ball)
            self.events.add(ev)

            ev = MOUSEBUTTONDOWN_Event(button=3, callback=self.rightclick_pallette, ball=ball)
            self.events.add(ev)

        self.active_color = 'blue'

    def make_rows(self):
        self.rows = []
        for r in range(self.turns):
            row = Row(self.cols, self.turns, r)
            self.rows.append(row)
            self.sprites.add(row)

        self.size = row.ballrack.size

    def set_active_row(self):
        self.active_row_events.kill()
        row = self.active_row
        for ball in self.rows[row].ballrack.balls:
            ev = MOUSEBUTTONDOWN_Event(button=1, callback=self.leftclick_row, ball=ball)
            self.events.add(ev)
            self.active_row_events.add(ev)

            ev = MOUSEBUTTONDOWN_Event(button=2, callback=self.centerclick_row, ball=ball)
            self.events.add(ev)
            self.active_row_events.add(ev)

            ev = MOUSEBUTTONDOWN_Event(button=3, callback=self.rightclick_row, ball=ball)
            self.events.add(ev)
            self.active_row_events.add(ev)

        self.make_checkbutton()

    def make_checkbutton(self):
        if hasattr(self, 'cb'):
            self.cb.events.kill()
            self.cb.kill()

        cb = ImageButton(filename='check.png', callback=self.leftclick_checkbutton)
        y = -(80+(self.size*self.active_row))
        cb.center(y=y)
        self.events.add(cb.events)
        self.sprites.add(cb)
        self.cb = cb

    def leftclick_pallette(self, ev, ball):
        if ball.rect.collidepoint(ev.pos):
            self.active_color = ball.color

    def rightclick_pallette(self, ev, ball):
        if ball.rect.collidepoint(ev.pos):
            row = self.rows[self.active_row]
            for b in row.ballrack.balls:
                if b.color == 'empty':
                    b.change_color(ball.color)
                    break

    def leftclick_row(self, ev, ball):
        if ball.rect.collidepoint(ev.pos):
            ball.change_color(self.active_color)

    def centerclick_row(self, ev, ball):
        if self.active_row > 0:
            if ball.rect.collidepoint(ev.pos):
                row = self.rows[self.active_row]
                lastrow = self.rows[self.active_row - 1]
                for i, ball in enumerate(lastrow.ballrack.balls):
                    row.ballrack.balls[i].change_color(ball.color)

            else:
                for row in self.rows:
                    if row.rect.collidepoint(ev.pos):
                        activerow = self.rows[self.active_row]
                        for i, ball in enumerate(row.ballrack.balls):
                            activerow.ballrack.balls[i].change_color(ball.color)
                        break


    def rightclick_row(self, ev, ball):
        if ball.rect.collidepoint(ev.pos):
            ball.change_color('empty')

    def leftclick_checkbutton(self, ev):
        row = self.rows[self.active_row]
        exact, inexact = row.ballrack.compare(self.target)

        pegs = row.pegrack
        i = 0
        while i < exact:
            pegs.balls[i].change_color('black')
            i += 1
        while i < exact + inexact:
            pegs.balls[i].change_color('white')
            i += 1

        if exact == self.cols:
            self.won_level()
        elif self.active_row < self.turns - 1:
            self.active_row += 1
            self.set_active_row()
        else:
            self.lost_level()

    def won_level(self):
        self.target.reveal()
        self.sprites.add(self.yes, level=2)

        next = TIMEOUT_Event(3500, callback=self.show_nextbutton)
        self.events.add(next)

    def show_nextbutton(self, ev=None):
        self.sprites.add(self.nextbutton, level=2)
        self.events.add(self.nextbutton.events)

    def next_level(self, ev=None):
        for row in self.rows:
            row.kill()

        self.target.kill()
        self.yes.kill()
        self.nextbutton.kill()
        self.events.remove(self.nextbutton.events)

        self.level += 1
        self.set_level()

    def lost_level(self):
        self.target.reveal()
        self.sprites.add(self.sorry, level=2)

        again = TIMEOUT_Event(3500, callback=self.show_againbutton)
        self.events.add(again)

    def show_againbutton(self, ev=None):
        self.sprites.add(self.againbutton, level=2)
        self.events.add(self.againbutton.events)

    def same_level(self, ev=None):
        for row in self.rows:
            row.kill()

        self.target.kill()
        self.sorry.kill()
        self.againbutton.kill()
        self.events.remove(self.againbutton.events)

        self.set_level()

    def show_help(self, ev=None):
        help = Help(size=(600,550))
        help.modal()



if __name__ == '__main__':
    g = Board()
    g.mainloop()
