#!/usr/bin/env python2
# -*- coding: utf-8 -*-
"""
Sliding blocks Puzzel - just a game :)

Copyright © 2013, Ehab El-Gedawy <ehabsas@gmail.com>

        Released under terms of Waqf Public License.
        This program is free software; you can redistribute it and/or modify
        it under the terms of the latest version Waqf Public License as
        published by Ojuba.org.

        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.

        The Latest version of the license can be found on
        "http://waqf.ojuba.org/license"
"""

from gi.repository import Gtk, Gdk
import random, sys

_VERSION = "0.0.2"
_AUTHOR = "Ehab El-Gedawy <ehabsas@gmail.com>"
_LICENSE = "Waqf"

class InputBox(Gtk.Window):
    def __init__(self, title=".::Sliding blocks Puzzel::.", use_back_door=False):
        Gtk.Window.__init__(self, title=title)
        self.connect("delete-event", Gtk.main_quit)
        self.connect("destroy", Gtk.main_quit)
        self.set_border_width(20)
        self.set_resizable(False)
        self.set_position(Gtk.WindowPosition(1))
        
        vbox = Gtk.VBox(False, 6)
        self.add(vbox)
        h = Gtk.HBox(False, 6)
        vbox.pack_start(h, False, False, 0)
        h.pack_start(Gtk.Label('Level difficulty'), False, False, 0)
        s = Gtk.SpinButton()
        # value, lower, upper, step_increment, page_increment, page_size
        adj = Gtk.Adjustment(3, 1, 10, 1, 0, 0)
        s.set_adjustment(adj)
        h.pack_start(s, False, False, 0)
        h = Gtk.HBox(False, 0)
        vbox.pack_start(h, False, False, 0)
        b = Gtk.Button('   Ok   ')
        b.connect('clicked', self.run, s)
        h.pack_end(b, False, False, 0)
        self.show_all()
        
    def run(self, b, s):
        rows = int(s.get_value())
        rows += 2
        self.hide()
        a = Main_Window(row_count = rows, use_back_door=use_back_door)
        
class Main_Window(Gtk.Window):
    def __init__(self, title=".::Sliding blocks Puzzel::.", row_count = 12, use_back_door=False):
        Gtk.Window.__init__(self, title=title)
        self.connect("delete-event", Gtk.main_quit)
        self.connect("destroy", Gtk.main_quit)
        self.set_border_width(20)
        self.set_resizable(False)
        self.set_position(Gtk.WindowPosition(1))
        
        self.button_back_door = None
        self.button_width = w = 60
        ## use self.button_cnt and self.row_cnt to change size
        ## to adjust correct 
        ## (self.button_cnt + 1) % self.row_cnt == 0
        #self.button_cnt = bcnt = 64
        ## use self.row_cnt to change size
        self.row_cnt = rcnt = row_count
        self.button_cnt = bcnt = (rcnt * rcnt) -1
        wh = (((bcnt + 1) / rcnt) * w)+40
        ww = (rcnt * w) + 40
        #self.set_size_request(215, 215)
        self.set_size_request(ww, wh)
        
        self.pos = {}
        self.buttons = {}
        self.layout = l = Gtk.Layout()
        self.add(l)
        
        row_num = 0
        col_num = 0
        y = 0 
        # TODO: create colors generator function
        #colors = hex(random.randrange(300,330)).replace('0x', '#')
        colors = ["#21b", "#d0d", "#a0a", "#1aa", "#10E", "#f00", "#b3a", "#1ad", "#3d3"]
        for i in range(0, bcnt):
            if i%rcnt == 0 : 
                x = 0
                row_num += 1
                col_num = 0
                y = (row_num * w)-w
            else:
                col_num += 1
                x = col_num * w 
            b = Gtk.Button(i+1)
            b.set_size_request(w, w)
            b.connect("clicked", self.check_moves)
            if use_back_door:
                b.connect("button-press-event", self.BackDoor)
            b.set_focus_on_click(0)
            b.set_can_focus(0)
            
            try:
                color = colors[row_num-1]
            except IndexError:
                color = colors[len(colors)-row_num]
            # TODO: Change background instead of forground
            b.modify_fg(Gtk.StateType.NORMAL, Gdk.color_parse(color))
            l.put(b, x, y)
            self.pos[i+1] = (x, y)
            self.buttons[i+1] = b
        self.pos[0] = (x + w, y)
        self.org_pos = dict(self.pos)
        self.do_rand()
        self.show_all()
    
    def BackDoor(self, b, event):
        if event.button == 3:
            l = int(b.get_label())
            if self.button_back_door:
                c1 = self.buttons[l]
                c2 = self.buttons[self.button_back_door]
                pos1 = self.pos[l]
                pos2 = self.pos[self.button_back_door]
                self.layout.move(c1, *pos2)
                self.layout.move(c2, *pos1)
                self.pos[l] = pos2
                self.pos[self.button_back_door] = pos1
                self.button_back_door = None
            else:
                self.button_back_door = l
        
    def check_moves(self, b):
        l = int(b.get_label())
        ls = self.get_buttons_ls(l)
        self.do_moves(ls)
        #if self.cmp_pos(l):
        #    self.do_moves([b])
        
    def do_rand(self):
        lst = list(self.pos.keys())
        random.shuffle(lst)
        for i in range(0, int(round((len(lst)+1)/2))):
            s1 = lst[i]
            s2 = lst[i+4]
            temp_pos = self.pos[s2]
            self.pos[s2] = self.pos[s1]
            self.pos[s1] = temp_pos
        for i in range(1, self.button_cnt+1):
            self.layout.move(self.buttons[i], *self.pos[i])

    def check_done(self):
        return self.pos == self.org_pos
        
    def do_moves(self, ls=[]):
        for l in ls:
            #l = int(c.get_label())
            c = self.buttons[l]
            temp_pos = self.pos[0]
            self.pos[0] = self.pos[l]
            self.pos[l] = temp_pos
            self.layout.move(c, *temp_pos)
        if self.check_done():
            dlg = Gtk.MessageDialog(self , 1, Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO)
            dlg.format_secondary_text("Well done!\n\nPlay again?")
            r = dlg.run()
            dlg.destroy()
            if r==Gtk.ResponseType.YES:
                self.do_rand()
            else:
                Gtk.main_quit()
                
    def cmp_pos(self, pos):
        # Note: Obsoleted
        b_pos = self.pos[pos]
        t_pos = self.pos[0]
        t = (b_pos[1] - self.button_width) == t_pos[1] and b_pos[0] == t_pos[0]
        l = (b_pos[0] - self.button_width) == t_pos[0] and b_pos[1] == t_pos[1]
        b = (b_pos[1] + self.button_width) == t_pos[1] and b_pos[0] == t_pos[0]
        r = (b_pos[0] + self.button_width) == t_pos[0] and b_pos[1] == t_pos[1]
        ret = t or l or b or r
        if not ret:
            ret = self.multi_buttons(pos)
        return ret
    
    def get_buttons_ls(self, pos):
        ## DONE: Multibutton move
        ## get list of button in same row or column
        ## move all buttons !
        #map(lambda a: a[0],sorted(s.items(), key=lambda x: x[1]))
        d = []
        b_pos = self.pos[pos]
        t_pos = self.pos[0]
        if b_pos[0] == t_pos[0]:
            d = dict(filter(lambda a: a[1][0] == t_pos[0], self.pos.items()))
            if b_pos[1] < t_pos[1]:
                d = dict(filter(lambda a: a[1][1] < t_pos[1] and a[1][1] >= b_pos[1], d.items()))
                d = map(lambda a: a[0],sorted(d.items(), key=lambda x: x[1], reverse=True))
            else:
                d = dict(filter(lambda a: a[1][1] > t_pos[1] and a[1][1] <= b_pos[1], d.items()))
                d = map(lambda a: a[0],sorted(d.items(), key=lambda x: x[1]))
        
        if b_pos[1] == t_pos[1]:
            d = dict(filter(lambda a: a[1][1] == t_pos[1], self.pos.items()))
            if b_pos[0] < t_pos[0]:
                d = dict(filter(lambda a: a[1][0] < t_pos[0] and a[1][0] >= b_pos[0], d.items()))
                d = map(lambda a: a[0],sorted(d.items(), key=lambda x: x[1], reverse=True))
            else:
                d = dict(filter(lambda a: a[1][0] > t_pos[0] and a[1][0] <= b_pos[0], d.items()))
                d = map(lambda a: a[0],sorted(d.items(), key=lambda x: x[1]))
        return d
        
if __name__ == '__main__':
    #app = Main_Window()
    use_back_door='--backdoor' in sys.argv or '-b' in sys.argv
    app = InputBox(use_back_door=use_back_door)
    Gtk.main()
exit(0)
