#! /usr/bin/env python
# coding=utf-8
#
#
# contents:
# 1. Imports
# 2. Globals
# 3. Classes
# 4.



# # # # # # #
# 1.Imports #
# # # # # # #
import pickle
import pygtk
import game
import os
from enemies import *
from items import *
pygtk.require("2.0")
import gtk


# # # # # # #
# 2.Globals #
# # # # # # #




# # # # # # #
# 3.Classes #
# # # # # # #



class RoomEditor(object):
    """ Room Editor window class.

    Window contains a List of choosable fields on the right, the modifiable fields on the left,
    and can give certain fields other values, which go into Room.fielddata
    or it does when its ready >.<
    weird bug: Save&Quit doesnt close the window, but still returns a room object.

    :todo übergebe room, nicht fields
    """

    def __init__(self, fields=0):
        """
        :param fields: lists of lists 30x52
        """
        self.chosen = "W"
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.connect("delete_event", self.delete_event)
        self.window.connect("destroy", self.destroy)
        self.window.set_border_width(10)

        self.box2 = gtk.HBox(False, 0)
        self.menubox = gtk.VBox(False, 0)

        if fields != 0:
            self.fields = fields
        # if no fields given, initialise list of lists: fields, later argument to Room()
        else:
            for i in range(30):
                self.fields.append([])
                for j in range(52):
                    self.fields[i].append("O")
        # initialize dictionary: fielddata, later argument to Room()
        self.fielddata = {}
        self.show_fields()

        # List which contains the buttons to choose field types
        self.choose_buttons = []
        self.choose_buttons.append(self.add_button(i_wall(), self.choose_field,
                                                   box=self.menubox, arg="W"))
        self.choose_buttons.append(self.add_button(i_leer(), self.choose_field,
                                                   box=self.menubox, arg="O"))
        self.choose_buttons.append(self.add_button(i_barricada(), self.choose_field,
                                                   box=self.menubox, arg="B"))
        self.choose_buttons.append(self.add_button(i_treasure(), self.choose_field,
                                                   box=self.menubox, arg="T"))
        self.choose_buttons.append(self.add_button(i_player(), self.choose_field,
                                                   box=self.menubox, arg=0))
        self.choose_buttons.append(self.add_button(i_1one(), self.choose_field,
                                                   box=self.menubox, arg=1))
        self.choose_buttons.append(self.add_text_button("Give Option", self.choose_field,
                                                        box=self.menubox, arg="C"))
        self.choose_buttons.append(self.add_text_button("Save & Quit", self.savequit,
                                                        box=self.menubox))

        self.window.add(self.box2)
        self.box2.pack_end(self.menubox)  # Vertical Box, for choosing Buttons

        self.menubox.show()
        self.box2.show()
        self.window.unmaximize()
        self.window.show()

    def main(self):
        gtk.main()  # starting event processing loop

    def delete_event(self, widget, event, data=None):
        """
        :return False if you want to destroy window
        :return True if you want to keep window
        """
        print "[Gtk] delete event occured"
        return False

    def destroy(self, widget, data=None):
        gtk.main_quit()

    def add_button(self, image, function, box=0, arg=None):
        self.button = gtk.Button()
        # When the button is clicked it calls self.hello()
        self.button.connect("clicked", function, arg)
        self.button.add(image)
        # pack a box to the beginning.
        if box:
            box.pack_start(self.button, True, True, 0)
        # Close the window
        # uncomment this if you want to close all parent windows without other children
        # when you close a window.
        # self.button1.connect_object("clicked", gtk.Widget.destroy, self.window)
        self.button.show()
        return self.button

    def add_text_button(self, label, function, box=0, arg=None):
        self.button = gtk.Button(label)
        self.button.connect("clicked", function, arg)
        if box:
            box.pack_start(self.button, True, True, 0)
        self.button.show()
        return self.button

    def choose_field(self, unusedbutnecessary, arg):
        """ choose, which field you want to add to Room.fields

        :param arg: string/integer of field type which is chosen to be added to fields.
        """
        self.chosen = arg

    def show_fields(self):
        """ shows the current fields.

        :param fields: list of lists
        :return:
        """
        y = len(self.fields)
        x = len(self.fields[0])
        self.table = gtk.Table(x, y)
        self.buttonlist = []
        for i in range(y):
            self.buttonlist.append([])
            for j in range(x):
                a = (i, j)
                a = self.add_button(self.which_image(self.fields[i][j]),
                                    self.change_field, arg=a)
                self.buttonlist[i].append(a)
                self.table.attach(a, j, j + 1, i, i + 1)
                a.show()
        self.box2.pack_start(self.table)
        self.table.show()

    def change_field(self, unusedbutnecessary, xy):
        if self.chosen == "C":
            self.give_option()
            return
        i = xy[0]
        j = xy[1]
        self.fields[i][j] = self.chosen
        self.buttonlist[i][j].destroy()
        a = self.add_button(self.which_image(self.fields[i][j]), self.change_field, arg=xy)
        self.buttonlist[i][j] = a
        self.table.attach(a, j, j + 1, i, i + 1)
        a.show()
        self.table.show()
        self.box2.show()
        self.window.show()

    def which_image(self, arg):
        if arg == "W":
            return i_wall()
        elif arg == "O":
            return i_leer()
        elif arg == "B":
            return i_barricada()
        elif arg == "T":
            return i_treasure()
        elif arg == 0:
            return i_player()
        elif arg == 1:
            return i_1one()

    def give_option(self, unusedbutnecessary, xy):
        """ Dialog to add entries to fielddata.

        This is executed if the give_option is chosen and a field is clicked.
        The field type is then assigned a value, e.g:
        {
            1 : "Police Officer", (enemy[0])
            2 : 15413874260947 (Room.description)
        }
        for now I can't implement Doors, as there is no way to edit Rooms.
        enemies: list of lists
        Do I need this function? maybe I should do this with CLI.
        :param unusedbutnecessary: unused
        :param xy: tuple(x,y), self.fields[x][y]
        :return:
        """
        # dialog
        # Lots of Buttons to choose from:
        # for room in rooms: room.id
        # for enemy in enemies: enemy[0]
        # fielddata.remove(entry)
        # fielddata[entry] = choice
        # dialog = gtk.Dialog(title="Enter a description", parent=self.window, flags=gtk.DIALOG_DESTROY_WITH_PARENT, buttons=None)
        with open("../resources/enemies", "r+") as file:
            enemies = pickle.load(file)

    def savequit(self, unusedbutnecessary, unusedbutnecessary2):
        dialog = gtk.Dialog(title="Enter a description", parent=self.window,
                            flags=gtk.DIALOG_DESTROY_WITH_PARENT, buttons=None)
        entry = gtk.Entry()
        dialog.action_area.pack_start(entry)
        entry.show()

        button = gtk.Button("Ok")
        button.connect("clicked", self.getext, entry)
        button.connect("clicked", dialog.destroy)
        dialog.action_area.pack_start(button)
        button.show()

        dialog.action_area.show()
        dialog.show()

    def getext(self, unusedbutnecessary, widget):
        description = widget.get_text()
        widget.destroy()
        self.room = game.Room(self.fields, self.fielddata, description)
        self.window.destroy()


class Choose_Field_Window(object):
    """ Window for choosing one field of the mission.

    weird bug: Save&Quit doesnt close the window, but still returns the coordinates.
    """
    def __init__(self, room):
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.connect("delete_event", self.delete_event)
        self.window.connect("destroy", self.destroy)
        self.window.set_border_width(10)
        self.box1 = gtk.HBox()
        self.room = room
        self.show_fields()
        self.window.add(self.box1)
        self.box1.show()
        self.window.show()

    def main(self):
        gtk.main()  # starting event processing loop
        return self.arg

    def delete_event(self, widget, event, data=None):
        """
        :return False if you want to destroy window
        :return True if you want to keep window
        """
        print "[Gtk] delete event occured"
        return False

    def destroy(self, widget, data=None):
        gtk.main_quit()

    def show_fields(self):
        """ shows the current fields.

        :param fields: list of lists
        :return:
        """
        y = len(self.room.fields)
        x = len(self.room.fields[0])
        self.table = gtk.Table(x, y)
        self.buttonlist = []
        for i in range(y):
            self.buttonlist.append([])
            for j in range(x):
                a = (i, j)
                a = self.add_button(self.which_image(self.room.fields[i][j]), self.choose_field, arg=a)
                self.buttonlist[i].append(a)
                self.table.attach(a, j, j + 1, i, i + 1)
                a.show()
        self.box1.pack_start(self.table)
        self.table.show()

    def which_image(self, arg):
        if arg == "W":
            return i_wall()
        elif arg == "O":
            return i_leer()
        elif arg == "B":
            return i_barricada()
        elif arg == "T":
            return i_treasure()
        elif arg == 0:
            return i_player()
        elif arg == 1:
            return i_1one()

    def add_button(self, image, function, box=0, arg=None):
        self.button = gtk.Button()
        self.button.add(image)
        # When the button is clicked it calls self.hello()
        self.button.connect("clicked", function, arg)
        # pack a box to the beginning.
        if box:
            box.pack_start(self.button, True, True, 0)
        # Close the window
        # uncomment this if you want to close all parent windows without other children
        # when you close a window.
        # self.button1.connect_object("clicked", gtk.Widget.destroy, self.window)
        self.button.show()
        return self.button

    def choose_field(self, unusedbutnecessary, arg):
        self.arg = arg
        self.window.destroy()


class CreateEnemyWin(object):
    def __init__(self):
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.connect("delete_event", self.delete_event)
        self.window.connect("destroy", self.destroy)
        self.window.set_border_width(20)
        global filename
        filename = "../images/1one.gif"  # default value

        self.box1 = gtk.VBox()
        self.table1()

        self.button = gtk.Button("Done")
        self.button.connect("clicked", self.get_values)
        self.button.connect("clicked", self.destroy)
        self.box1.pack_start(self.button)
        self.button.show()

        self.window.add(self.box1)
        self.box1.show()
        self.window.show()

    def main(self):
        gtk.main()  # starting event processing loop
        return self.output

    def delete_event(self, widget, event, data=None):
        """
        :return False if you want to destroy window
        :return True if you want to keep window
        """
        print "[Gtk] delete event occured"
        return False

    def destroy(self, widget, data=None):
        gtk.main_quit()

    def table1(self):
        self.table = gtk.Table(2, 13)
        self.tablelabel("Name:", 0)
        self.tablelabel("Race:", 1)
        self.tablelabel("Image:", 2)
        self.tablelabel("Constitution:", 3)
        self.tablelabel("Strength:", 4)
        self.tablelabel("Agility:", 5)
        self.tablelabel("Mobility:", 6)
        self.tablelabel("Intelligence:", 7)
        self.tablelabel("Gun:", 8)
        self.tablelabel("Sword:", 9)
        self.tablelabel("Min. Loot:", 10)
        self.tablelabel("Max. Loot:", 11)
        self.tablelabel("Experience", 12)
        self.inputs = []
        self.tableobject(gtk.Entry, 0)
        self.tableobject(gtk.Entry, 1)

        self.button = gtk.Button("Choose File")
        self.button.connect("clicked", self.choose_file)
        self.table.attach(self.button, 1, 2, 2, 3)
        self.button.show()

        self.tableobject(gtk.Entry, 3)
        self.tableobject(gtk.Entry, 4)
        self.tableobject(gtk.Entry, 5)
        self.tableobject(gtk.Entry, 6)
        self.tableobject(gtk.Entry, 7)
        self.tableobject(gtk.Entry, 8)
        self.tableobject(gtk.Entry, 9)
        self.tableobject(gtk.Entry, 10)
        self.tableobject(gtk.Entry, 11)
        self.tableobject(gtk.Entry, 12)

        self.box1.pack_start(self.table)
        self.table.show()

    def tablelabel(self, title, y):
        a = gtk.Label(title)
        self.table.attach(a, 0, 1, y, y + 1)
        a.show()

    def tableobject(self, Klass, y):
        a = Klass()
        self.inputs.append(a)
        self.table.attach(a, 1, 2, y, y + 1)
        a.show()

    def get_values(self, unusedbutnecessary):
        lst = []
        end = []
        for a in self.inputs:
            lst.append(a.get_text())
        for i in range(len(lst) + 1):
            if i == 2:
                end.append(self.filename)
            end.append(i)
        self.output = {lst[0]: lst}

    def choose_file(self, unusedbutnecessary):
        self.filesel = gtk.FileSelection("Select an image")
        self.filesel.ok_button.connect("clicked", lambda w: self.file_ok_sel)
        self.filesel.ok_button.connect("clicked", lambda w: self.filesel.destroy())
        self.filesel.cancel_button.connect("clicked", lambda w: self.filesel.destroy())
        self.filesel.set_filename("../images/characters/1one.gif")
        self.filesel.show()

    def file_ok_sel(self, w):
        global filename
        filename = self.filesel.get_filename()


class CreateGunWin(object):
    """ Window to create guns classes.

    To do: design gun logos and change the default values.

    def __init__(self, name, price, image, damage, grange, ap):
    super(Gun, self).__init__(name, price, image)
    self.damage = damage  # Integer
    self.grange = grange  # Integer
    self.ap = ap  # Integer, AP needed to fire the gun
    self.type = "gun"
    """

    def __init__(self):
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.connect("delete_event", self.delete_event)
        self.window.connect("destroy", self.destroy)
        self.window.set_border_width(20)
        self.filename = "../images/guns/glock.gif"  # default value

        self.box1 = gtk.VBox()
        self.table1()

        self.button = gtk.Button("Done")
        self.button.connect("clicked", self.get_values)
        self.button.connect("clicked", self.destroy)
        self.box1.pack_start(self.button)
        self.button.show()

        self.window.add(self.box1)
        self.box1.show()
        self.window.show()

    def main(self):
        gtk.main()  # starting event processing loop
        return self.output

    def delete_event(self, widget, event, data=None):
        """
        :return False if you want to destroy window
        :return True if you want to keep window
        """
        print "[Gtk] delete event occured"
        return False

    def destroy(self, widget, data=None):
        gtk.main_quit()

    def table1(self):
        self.table = gtk.Table(2, 13)
        self.tablelabel("Name:", 0)
        self.tablelabel("Price:", 1)
        self.tablelabel("Damage:", 2)
        self.tablelabel("Range:", 3)
        self.tablelabel("AP:", 4)
        self.tablelabel("Image:", 5)
        self.inputs = []
        for i in range(0, 5):
            self.tableobject(gtk.Entry, i)

        self.button = gtk.Button("Choose File")
        self.button.connect("clicked", self.choose_file)
        self.table.attach(self.button, 1, 2, 5, 6)
        self.button.show()

        self.box1.pack_start(self.table)
        self.table.show()

    def tablelabel(self, title, y):
        a = gtk.Label(title)
        self.table.attach(a, 0, 1, y, y + 1)
        a.show()

    def tableobject(self, Klass, y):
        a = Klass()
        self.inputs.append(a)
        self.table.attach(a, 1, 2, y, y + 1)
        a.show()

    def get_values(self, unusedbutnecessary):
        lst = []
        end = []
        for a in self.inputs:
            lst.append(a.get_text())
        for i in range(len(lst) + 1):
            if i == 2:
                end.append(self.filename)
            end.append(i)
        self.output = {lst[0]: lst}

    def choose_file(self, unusedbutnecessary):
        self.filesel = gtk.FileSelection("Select an image")
        self.filesel.ok_button.connect("clicked", lambda w: self.file_ok_sel)
        self.filesel.ok_button.connect("clicked", lambda w: self.filesel.destroy())
        self.filesel.cancel_button.connect("clicked", lambda w: self.filesel.destroy())
        self.filesel.set_filename("../images/guns/glock.gif")
        self.filesel.show()

    def file_ok_sel(self, w):
        global filename
        filename = self.filesel.get_filename()


# # # # # # # #
# 3.Functions #
# # # # # # # #

def i_wall():
    i_wall = gtk.Image()
    i_wall.set_from_file("../images/fields/wall.gif")
    i_wall.show()
    return i_wall


def i_leer():
    i_leer = gtk.Image()
    i_leer.set_from_file("../images/fields/0leer.gif")
    i_leer.show()
    return i_leer


def i_barricada():
    i_barricada = gtk.Image()
    i_barricada.set_from_file("../images/fields/barricada.gif")
    i_barricada.show()
    return i_barricada


def i_treasure():
    i_treasure = gtk.Image()
    i_treasure.set_from_file("../images/fields/treasure.gif")
    i_treasure.show()
    return i_treasure


def i_player():
    i_player = gtk.Image()
    i_player.set_from_file("../images/characters/player.gif")
    i_player.show()
    return i_player


def i_1one():
    i_1one = gtk.Image()
    i_1one.set_from_file("../images/characters/1one.gif")
    i_1one.show()
    return i_1one


def i_circle():
    i_circle = gtk.Image()
    i_circle.set_from_file("../images/kreis.gif")
    i_circle.show()
    return i_circle


def create_file(filename):
    """ tests if file exists - if not it creates an empty dict in it.

    :param filename: string - file path to create
    """
    try:
        with open(filename, "rb"):
            return
    except IOError:
        print("ERROR: ", filename, " doesn't exist, is created instead.")
        with open(filename, "wb") as file:
            dct = {}
            pickle.dump(dct, file)


def create_enemy():
    """ creating enemy classes and saving them in resources/enemies """
    print("Create an enemy.")
    window = CreateEnemyWin()
    newdict = window.main()
    print("Saving enemy in resources/enemies...")
    create_file("../resources/enemies")
    with open("../resources/enemies", "rb") as file:
        allenemies = pickle.load(file)
    for i in newdict:
        allenemies[i] = newdict[i]
    with open("../resources/enemies", "wb") as file:
        pickle.dump(allenemies, file)
    print("Enemy saved.")


def create_gun():
    """ creating gun classes and saving them in resources/guns """
    print("Create a gun.")
    window = CreateGunWin()
    newdict = window.main()
    print("Saving gun in resources/guns...")
    create_file("../resources/guns")
    with open("../resources/guns", "rb") as file:
        allenemies = pickle.load(file)
    for i in newdict:
        allenemies[i] = newdict[i]
    with open("../resources/guns", "wb") as file:
        pickle.dump(allenemies, file)
    print("Gun saved.")


def create_fielddata_entry(room):
    """ Create a fielddata entry for a room


    :param room: Room object
    :return: room: Room object
    """
    choice = 0
    while choice == 0:
        print("0 : Abort")
        for entry in room.fielddata:
            print(str(entry) + " : " + room.fielddata[entry])
        try:
            choice = int(raw_input("Which entry do you want to create? Pick a number which "
                                   "is not given yet. delete it if you want to update an "
                                   "existing: "))
        except ValueError:
            print("ERROR: Enter a number.")
        if choice == 0:
            return room
        elif choice in room.fielddata:
            print("ERROR: " + str(choice) + " is already in fielddata. Try again.")
            choice = 0
        else:
            room.fielddata[choice] = raw_input("Enter the content of " + str(choice) + ": ")
            return room


def erase_fielddata_entry(room):
    """
    :param room: Room object
    :return: room: Room object
    """
    choice = 0
    while choice == 0:
        print("0 : Abort")
        for entry in room.fielddata:
            print(str(entry) + " : " + room.fielddata[entry])
        try:
            choice = int(raw_input("Which entry do you want to delete? "))
        except ValueError:
            print("ERROR: Enter a number.")
        if choice == 0:
            return room
        elif choice in room.fielddata:
            del room.fielddata[choice]
            return room
        else:
            print("ERROR: " + str(choice) + " is not in fielddata. Try again.")
            choice = 0


def choose_target_enemy(room):
    print "0: Abort"
    for i in room.fielddata:
        print i + ":", room.fielddata[i]
    choice = 0
    while choice == 0:
        try:
            choice = int(raw_input("Which enemy is to be killed? "))
            if choice == 0:
                return
        except ValueError:
            print("ERROR: Enter a number.")
            choice = 0
    room.fielddata["target"] = choice


def create_room():
    window = RoomEditor()
    window.main()
    choice = 0
    while choice == 0:
        print("(1) Create a fielddata entry")
        print("(2) Erase a fielddata entry")
        print("(3) Choose a target enemy")
        print("(4) Finish room creation")
        try:
            choice = int(raw_input("What do you want to do? "))
        except ValueError:
            print("ERROR: Enter a number.")
        if choice == 1:
            create_fielddata_entry(window.room)
        elif choice == 2:
            erase_fielddata_entry(window.room)
        elif choice == 3:
            choose_target_enemy(window.room)
        elif choice == 4:
            print("Back to mission creation.")
            return window.room
        else:
            print("ERROR: Enter a number between 1 and 5.")
        choice = 0


def choose_room(rooms, string):
    choice = "y"
    while choice == "y":
        for i in rooms:
            print str(id(i)), i.description
        try:
            choice = raw_input(string)
        except ValueError:
            print("ERROR: Enter the ID of the room.")
        for i in rooms:
            try:
                if id(i) == int(choice):
                    return i
            except ValueError:
                if i.description == choice:
                    return i
        print("ERROR: ID is not in room list.")
        choice = "y"


def create_mission():
    """ mission creation func

    rewardmoney         # integer
    rewarditems         # list of Item-objects
    rewardxp            # integer
    description         # string
    room                # Room-object where to start
    target_item         # string (name of item) - default: None
    target_enemy        # integer (how many enemies have target attribute) - default: 0
    target_coordinates  # list (x,y,roomID) - default: None
    """
    name = 0
    while name == 0:
        name = raw_input("What shall be the name of the mission? ")
        if name in os.listdir("../missions"):
            print("ERROR: Mission name already taken.")
            name = 0
    rooms = []
    choice = "y"
    while choice == "y":
        print("Create a room.")
        rooms.append(create_room())
        try:
            choice = raw_input("Do you want to create another room? (y/N) ")
        except ValueError:
            print("ERROR: Wrong input. Continuing mission creation.")
            choice = "N"
    room = choose_room(rooms, "Which room shall be the starting point? ")
    print("Define success conditions:")
    target_item = raw_input("Name of target item necessary to win the mission (leave "
                            "empty if not): ")
    if target_item == "":
        target_item = None
    choice = raw_input("Do you want to define target coordinates? (y/N) ")
    while choice == "y":
        targetroom = choose_room(rooms, "Which room shall the target position be in? ")
        field_choice_win = Choose_Field_Window(targetroom)
        xy = field_choice_win.main()
        target_coordinates = [xy[0], xy[1], targetroom]
        choice = raw_input("Do you want to create another target position? (y/N) ")
    else:
        target_coordinates = None
    target_enemy = 0
    for r in rooms:
        if "target" in r.fielddata:
            for i in r.fields:
                for j in i:
                    if r.fielddata["target"] == j:
                        target_enemy += 1
    print(str(target_enemy) + " target enemies in mission.")
    print("Enter additional data: ")
    money = -1
    while money == -1:
        try:
            money = int(raw_input("How much BTC shall be the reward? "))
        except ValueError:
            print("ERROR: Enter a number.")
            money = -1
    xp = -1
    while xp == -1:
        try:
            xp = int(raw_input("How much XP shall be rewarded? "))
        except ValueError:
            print("ERROR: Enter a number.")
            xp = -1
    items = []  # Add reward items as soon as there is a modular item system.
    print("Enter a mission description:")
    description = raw_input()
    mission = game.Mission(money, items, xp, description, room, rooms, target_item,
                           target_enemy, target_coordinates)
    print("Mission created. Saving mission as missions/" + name + "...")
    with open("../missions/" + name, mode="wb") as file:
        pickle.dump(mission, file)
    print("Mission saved.")


def main():
    choice = 0
    while choice == 0:
        print("(1) Create a mission\n(2) Create a room (useless)\n(3) Create a gun")
        print("(4) Create a sword\n(5) Create an enemy\n(6) Exit the editor")
        try:
            choice = int(raw_input("What do you want to do? "))
        except ValueError:
            print("ERROR: Enter a number.")
        if choice == 1:
            create_mission()
        elif choice == 2:
            create_room()
        elif choice == 3:
            create_gun()
        elif choice == 4:
            pass
            # create_sword()
        elif choice == 5:
            create_enemy()
        elif choice == 6:
            print("Good Bye.")
            exit()
        else:
            print("ERROR: Enter a number between 1 and 5.")
        choice = 0


if __name__ == "__main__":
    main()