# THIS IS A SOURCE CODE FILE FROM I'M NOT EVEN HUMAN THE GAME.
# IT COULD BE USED IN A DIFFERENT PIECE OF SOFTWARE ( LIKE A
# DIFFERENT GAME ), BUT IT WAS ORIGINALLY WRITTEN FOR I'M NOT
# EVEN HUMAN THE GAME.

# THE DEVELOPERS OF THE GAME ARE : (C) J.Y.AMIHUD, AYYZEE AND 
# OTHER CONTRIBUTORS. THIS AND OTHER FILES IN THIS GAME,
# UNLESS SPECIFICALLY NOTED, COULD BE USED UNDER THE TERMS OF
# GNU GENERAL PUBLIC LICENSE VERSION 3 OR ANY LATER VERSION.

import os
import json
import time
import random
import datetime # For the FPS meter mainly

# GTK module ( Graphical interface
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GLib
from gi.repository import GdkPixbuf
import cairo
import threading
from subprocess import *

from PIL import Image


from modules import ui

win = Gtk.Window()
win.set_size_request(600, 600)
win.connect("destroy", Gtk.main_quit)

# Hack
win.images = {}
win.current = {"frame":0}
win.FPS = 60
win.zoom = 2

############# TITLE BAR TOOLBAR #######

pannel = Gtk.HeaderBar()
pannel.set_show_close_button(True)
win.set_titlebar(pannel)

def do_launch_game(w):
    os.system("python3 play.py")
launch = Gtk.Button("Launch Game Test")
launch.connect("clicked", do_launch_game)
pannel.pack_start(launch)

selected_asset = {"path":"",
                  "image":"",
                  "frames":[]}

paned = Gtk.HPaned()
win.add(paned)

#############################################################################
#                                                                           #
#                                                                           #
#          THE ASSET SELECTOR THINGY!!!                                     #
#                                                                           #
#                               The tree view to select our asset           #
#                                                                           #
#                                                                           #
#############################################################################

def save_sprite(name):
 

    # First we need to find out how big is the biggest cell
    # And all kinds of other things.

    largestx = 0
    largesty = 0
    all_sprites = 0
    most_frames = 0

    empties = []
    for i in win.images[name]:
        data = win.images[name][i]
        print()
        print(i, data)
        if type(data) == list and len(data) and type(data[0]) == dict:
            if "title" not in data[0]:
                empties.append(i)
    for i in empties:
        del win.images[name][i]
    
    for i in win.images[name]:
        data = win.images[name][i]
        if type(data) == list and len(data) and type(data[0]) == dict:
            all_sprites += 1
            if len(data) > most_frames:
                most_frames = len(data)
            for frame in data:
                if frame["binary"].get_width() > largestx:
                    largestx = frame["binary"].get_width()
                if frame["binary"].get_height() > largesty:
                    largesty = frame["binary"].get_height()
    print(all_sprites)
    # Now we need to figured all of it out. Let's calculate and create
    # the canvas size for the final sprite

    canvasx = int(largestx * all_sprites)
    canvasy = int(largesty * most_frames)

    canvas = cairo.ImageSurface(cairo.FORMAT_ARGB32,
                                      canvasx,
                                      canvasy)
    brush = cairo.Context(canvas)

    metadata = {}
    metadata["title"] = win.images[name].get("title", "")
    metadata["authors"] = win.images[name].get("authors", [])
    metadata["licenses"] = win.images[name].get("licenses", [])
    metadata["menu_color"] = win.images[name].get("menu_color", "#000000")

    metadata["resolution"] = [  all_sprites, most_frames    ]
    n = -1
    for i in win.images[name]:
        
        data = win.images[name][i]
        
        if type(data) == list and len(data) and type(data[0]) == dict:

            n = n + 1
            
            for ind, frame in enumerate(data):
                brush.set_source_surface(frame["binary"],
                                          int(largestx*n),
                                          int(largesty*ind))
                brush.paint()

                metadata[str(n)+":"+str(ind)] = {}
                
                for stuff in frame:
                    if stuff != "binary":
                        metadata[str(n)+":"+str(ind)][stuff] = frame[stuff]
    
    empties = []                    
    for i in metadata:
        if not metadata[i]:
            empties.append(i)
    for i in empties:
        del metadata[i]


    
        
    canvas.write_to_png(name)
    print("Sprite image was saved:", name)
    
    with open(name.replace(".png", ".json"), 'w') as f:
        json.dump(metadata, f, indent=4)

    win.images = {}
    update_tree()
    

def do_revert(w=False):
    win.images = {}
    update_tree()

    for i in frames_box.get_children():
        i.destroy()

    selected_asset["path"] = ""
    selected_asset["image"] = ""
    
test_save = Gtk.Button("Re-Load")
test_save.connect("clicked", do_revert)
pannel.pack_start(test_save)
    
#############################################################################
#                                                                           #
#                                                                           #
#          THE ASSET SELECTOR THINGY!!!                                     #
#                                                                           #
#                               The tree view to select our asset           #
#                                                                           #
#                                                                           #
#############################################################################




def do_test_save(w):
    save_sprite(selected_asset["path"])
test_save = Gtk.Button("Save Current Asset")
test_save.connect("clicked", do_test_save)
pannel.pack_start(test_save)


def on_select(w):

    
    for i in frames_box.get_children():
        i.destroy()
    selected_frame["is"] = None
 

    (model, pathlist) = w.get_selection().get_selected_rows()
    for path in pathlist :
        tree_iter = model.get_iter(path)
        thepath = model.get_value(tree_iter,1)
        image = model.get_value(tree_iter,2)

        datapath = thepath+":"+image
        print(datapath)

        
        selected_asset["path"] = thepath
        selected_asset["image"] = image
        
        for n, i in enumerate(data_frames.get(datapath, [])):

            toggle = Gtk.ToggleButton("Frame "+str(n))
            toggle.connect("clicked", on_toggle_frame, n)
            toggle.set_relief(Gtk.ReliefStyle.NONE)
                                      
            frames_box.pack_start(toggle,0,0,0)

        frames_box.show_all()

 

data_frames = {}

treebox = Gtk.HBox()
paned.add(treebox)


def update_tree():

    for i in treebox.get_children():
        i.destroy()

    inds = list(data_frames.keys())
    for i in inds:
        del data_frames[i]

        
    data_tree_store = Gtk.TreeStore(str, str, str)
    tree = Gtk.TreeView(data_tree_store)
    tree.connect("cursor-changed", on_select)


    treescrl = Gtk.ScrolledWindow()
    treescrl.set_size_request(300,300)
    treescrl.add(tree)
    treebox.pack_start(treescrl, 1,1,0)
    
    key_column = Gtk.TreeViewColumn("Select Sprite")
    text_col = Gtk.CellRendererText()
    text_col.set_property("editable", True)
    key_column.pack_start(text_col, True)
    key_column.add_attribute(text_col, "text", 0)
    tree.append_column(key_column)



    def load_image_data(path):

        try:
            with open(path+".json") as f:
                data = json.load(f)

        except Exception as e:
            data = {}

        return data

    def populate(treeStore, data, parent=None):

        data = list(data)

        # Folders

        for i in data[0][1]:

            # Excluding the Worlds folder
            if i == "worlds":
                continue

            this = treeStore.append(parent, [i, data[0][0]+"/"+i, ""])
            populate(treeStore, os.walk(data[0][0]+"/"+i), this)

        # files
        for i in data[0][2]:
            if i.endswith(".png"):

                name = i[:-4]
                path = data[0][0]+"/"+i[:-4]
                metadata = load_image_data(path)

                name = metadata.get("title", name)

                this = treeStore.append(parent, [name, data[0][0]+"/"+i, ""])

                # TODO: Somehow to expland back to where it was after saving.
                
                res = metadata.get("resolution", [1,1])
                if res[0] or res[1]:
                    for x in range(res[0]):
                        for y in range(res[1]):
                            index = str(x)+":"+str(y)

                            index = metadata.get(index, {"title":index}).get("title", index)

                            if index == str(x)+":"+str(y):
                                continue

                            if data[0][0]+"/"+i+":"+index not in data_frames:
                                data_frames[data[0][0]+"/"+i+":"+index] = [str(x)+":"+str(y)]

                                treeStore.append(this, [index, data[0][0]+"/"+i, index])
                            else:
                                data_frames[data[0][0]+"/"+i+":"+index].append(str(x)+":"+str(y))


    populate(data_tree_store, os.walk("assets"), None)
    treebox.show_all()
    
update_tree()

second_paned = Gtk.HPaned()
paned.add(second_paned)

#############################################################################
#                                                                           #
#                                                                           #
#          THE FRAME SELECTOR THINGY!!!                                     #
#                                                                           #
#                               The tree view to select our frames          #
#                                                                           #
#                                                                           #
#############################################################################

frames_box = Gtk.VBox()
frames_box_box = Gtk.VBox()
frames_box_box.pack_start(Gtk.Label("Sprite Frames"), 0,0,5)
frames_scrl = Gtk.ScrolledWindow()
frames_scrl.add(frames_box)
frames_box_box.pack_end(frames_scrl, 1,1,0)
frames_scrl.set_size_request(300,300)
second_paned.add(frames_box_box)

selected_frame = {"is":None}

def on_toggle_frame(w, n):

    if w.get_active():
        for m, i in enumerate(frames_box.get_children()):
            if m != n:
                i.set_active(False)
        selected_frame["is"] = n
    else:
        selected_frame["is"] = None



#############################################################################
#                                                                           #
#                                                                           #
#          THE PREVIEW THINGY!!!                                            #
#                                                                           #
#                               The part where you actually see the image.  #
#                                                                           #
#                                                                           #
#############################################################################

last_third = Gtk.VBox()
second_paned.add(last_third)

def do_preview_pad(d, main_layer):

       
    win.current["frame"] += 1
    
    w = d.get_allocated_width()
    h = d.get_allocated_height()

    mx = d.get_pointer()[0]
    my = d.get_pointer()[1]

    # THE CENTER GRID
    
    main_layer.set_source_rgba(0.7,0.7,0.7,1)
    main_layer.set_line_width(3)
    main_layer.move_to( w/2, h/2-10 )
    main_layer.line_to( w/2, h/2+10 )
    main_layer.stroke()

    main_layer.set_line_width(1)
    main_layer.set_source_rgba(0.2,0.2,0.2,1)
    main_layer.move_to( w/2, h/2-10 )
    main_layer.line_to( w/2, h/2+10 )
    main_layer.stroke()
    main_layer.set_line_width(3)

    main_layer.set_source_rgba(0.7,0.7,0.7,1)
    main_layer.move_to( w/2-10, h/2 )
    main_layer.line_to( w/2+10, h/2 )
    main_layer.stroke()

    main_layer.set_line_width(1)
    main_layer.set_source_rgba(0.2,0.2,0.2,1)
    main_layer.move_to( w/2-10, h/2 )
    main_layer.line_to( w/2+10, h/2 )
    main_layer.stroke()
    main_layer.set_line_width(3)

    # THE PREVIEW BLOCK

    bh = 34 *win.zoom
    bw = 63 *win.zoom

    points = [
        [w/2, h/2+bh/2+win.zoom],
        [w/2+bw/2, h/2+win.zoom],
        [w/2+bw, h/2+bh/2+win.zoom],
        [w/2+bw/2, h/2+bh+win.zoom],
        
    ]

    main_layer.move_to( *points[-1] )
    main_layer.set_source_rgba(0.7,0.7,0.7,1)
    for i in points:
        main_layer.line_to( *i )
    main_layer.stroke()

    main_layer.move_to( *points[-1] )

    for i in points:
        main_layer.line_to( *i )
    main_layer.set_line_width(1)
    main_layer.set_source_rgba(0.2,0.2,0.2,1)

    main_layer.stroke()
    main_layer.set_line_width(3)

    asset = selected_asset.get("path", "")
    image = selected_asset.get("image", "")
    
    try:
        if not sprite_name.is_focus():
            sprite_name.set_text(win.images[asset][image][0]["title"])
        if not asset_name.is_focus():
            asset_name.set_text(win.images[asset]["title"])
    except:
        pass
    

    #### MOVE OFFSET OF THE IMAGE ###

    posx = w/2/win.zoom
    posy = h/2/win.zoom

    offx, offy = 0 , 0
    sizex, sizey = 0,0

    
    
    try:
        offx = win.images[asset][image]
        if selected_frame["is"] == None:
            offx = offx[0]
        else:
            offx = offx[selected_frame["is"]]

        sizex = offx["binary"].get_width()
        sizey = offx["binary"].get_height()

        collision_toggle.set_active(offx.get("collision", True))
        dynamic_toggle.set_active(offx.get("dynamic", False))
        main_character_toggle.set_active(offx.get("main_character", False))
        main_character_toggle.set_visible(offx.get("dynamic", False))
        ramp_toggle.set_active(offx.get("ramp", False))
        ramp_toggle.set_visible(not offx.get("dynamic", False))
        damage_toggle.set_active(offx.get("damage", False))
        if not damage_amount_spin.is_focus():
            damage_amount_spin.set_value(offx.get("damage_amount", 0.01))
        damage_amount_box.set_visible(offx.get("damage", False))
        invisible_toggle.set_active(offx.get("invisible", False))
        
        offx, offy = offx["offset"]

        if not xentry.is_focus():
            xentry.set_sensitive(True)
            xentry.set_value(offx)
            
        if not yentry.is_focus():
            yentry.set_sensitive(True)
            yentry.set_value(offy)
        
    except Exception as e:
        xentry.set_sensitive(False)
        yentry.set_sensitive(False)


    sizex = sizex * win.zoom
    sizey = sizey * win.zoom

    if 0 < mx < w and 0 < my < h:
    
        main_layer.set_source_rgba(0.7,0.7,0.7,1)
        main_layer.rectangle((posx+offx)*win.zoom, (posy+offy)*win.zoom, sizex, sizey)
        main_layer.stroke()
        main_layer.set_line_width(1)
        main_layer.set_source_rgba(0.2,0.2,0.2,1)
        main_layer.rectangle((posx+offx)*win.zoom, (posy+offy)*win.zoom, sizex, sizey)
        main_layer.stroke()
        main_layer.set_line_width(3)

    # If dragging:
    if win.offseter["pressed"]:

        try:
            if selected_frame["is"] != None:
                offx = offx + (mx-win.offseter["previous"][0])/win.zoom
                offy = offy + (my-win.offseter["previous"][1])/win.zoom
                win.images[asset][image][selected_frame["is"]]["offset"] = [int(offx), int(offy)]
            else:
                for frame in win.images[asset][image]:
                    offx, offy = frame["offset"]
                    offx = offx + (mx-win.offseter["previous"][0])/win.zoom
                    offy = offy + (my-win.offseter["previous"][1])/win.zoom
                    frame["offset"] = [int(offx), int(offy)]

            # TODO: Find something that's not as fucked.
            zoom_minus.grab_focus()
        except Exception as e:
            print(e)


        win.offseter["previous"] = [mx, my]
        
    main_layer.scale(win.zoom,
                     win.zoom)

    main_layer.set_antialias(cairo.ANTIALIAS_NONE)
    p = main_layer.get_source()
    p.set_filter(cairo.FILTER_NEAREST)
    
    
    if asset and image:       
        try:
            ui.image(win, main_layer,
                 posx,
                 posy,
                 asset, image, color=False, offset=True, frame=selected_frame["is"])
        except Exception as e:
            pass

        # SENSITIVE OR NOT BASED ON WHETHER THERE IS IMAGE SELECTED

        
        test_save.set_sensitive(True)
        asset_name.set_sensitive(True)
        sprite_name.set_sensitive(True)
        add_sprite.set_sensitive(True)
        remove_sprite.set_sensitive(True)
        add_frame.set_sensitive(True)
        remove_frame.set_sensitive(True)
        change_frame.set_sensitive(True)
        copy_frame.set_sensitive(True)
        licenses_editor.set_sensitive(True)
        authors_editor.set_sensitive(True)
        extra_button.set_sensitive(True)
        asset_color.set_sensitive(True)


        licenses = win.images[asset]["licenses"]
        if licenses_get() != licenses:
            print("Update Licenses")
            licenses_updator(licenses)

        authors = win.images[asset]["authors"]
        if authors_get() != authors:
            authors_updator(authors)

        def hex_to_rgb(value):
            value = value.lstrip('#')
            lv = len(value)
            return list(int(value[i:i+lv//3], 16) for i in range(0, lv, lv//3))
        
        color = win.images[asset]["menu_color"]
        r,g,b = hex_to_rgb(color)
        color = []
        for i in (r,g,b):
            color.append(i*255)
        
        asset_color.set_color(Gdk.Color(*color))
        
        
        
        
    else:
        
        test_save.set_sensitive(False)
        asset_name.set_sensitive(False)
        sprite_name.set_sensitive(False)
        add_sprite.set_sensitive(False)
        remove_sprite.set_sensitive(False)
        add_frame.set_sensitive(False)
        remove_frame.set_sensitive(False)
        change_frame.set_sensitive(False)
        copy_frame.set_sensitive(False)
        licenses_editor.set_sensitive(False)
        authors_editor.set_sensitive(False)
        extra_button.set_sensitive(False)
        asset_color.set_sensitive(False)

        licenses_updator([])
        authors_updator([])
        asset_name.set_text("")
        sprite_name.set_text("")

        
    preview_pad.queue_draw()
    
event_box = Gtk.EventBox()
preview_pad = Gtk.DrawingArea()
event_box.add(preview_pad)
event_box.set_size_request(200, 200)
last_third.pack_start(event_box, 1,1,1)
preview_pad.connect("draw", do_preview_pad)

# We need some logic for the offset editor ( which is moving the image with
# the cursor in the preview pad )



win.offseter = {
    "pressed": False,
    "previous":[0,0]}

def button_press(w, e):

    win.offseter["pressed"] = True
    win.offseter["previous"] = [e.x, e.y]

def button_release(w, e):
    win.offseter["pressed"] = False

event_box.connect("button-press-event", button_press)
event_box.connect("button-release-event", button_release)
last_third.pack_start(Gtk.HSeparator(), 0,0,10)

def on_add_image(w, kind="Frame"):
    dialogWindow = Gtk.Dialog(kind,
            buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
                     Gtk.STOCK_OK, Gtk.ResponseType.OK),
        )

    box = dialogWindow.get_content_area()

    
    
    if kind == "Asset":

        is_asset_folder_skip = False
        
        folder_path = selected_asset.get("path")
        if folder_path.endswith(".png"):
            is_asset_folder_skip = True
            folder_path = folder_path[:folder_path.rfind("/")]

        assbox = Gtk.HBox()
        assbox.pack_start(Gtk.Label(folder_path+"/"), 0,0,0)

        asset_file_name = Gtk.Entry()
        asset_file_name.set_text("asset_file_name")
        assbox.pack_start(asset_file_name, 0,0,0)
        
        assbox.pack_start(Gtk.Label(".png"), 0,0,0)
        
        box.pack_start(assbox, 0,0,10)
    

    ######### WE WILL TRY TO GET IMAGE FROM CLIPBOARD #######
    clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
    im =  clipboard.wait_for_image()
    if not im:
        box.pack_start(Gtk.Label("[No IMAGE IN CLIPBOARD]"), 0,0,10)
    else:
        box.pack_start(Gtk.Image.new_from_pixbuf(im), 0,0,0)
    
    
    box.show_all()
    response = dialogWindow.run()

    if response == Gtk.ResponseType.OK and im:

        asset = selected_asset.get("path")
        image = selected_asset.get("image")

        Px = im.get_width()
        Py = im.get_height()
        Pc = cairo.ImageSurface(cairo.FORMAT_ARGB32, Px, Py)
        Pb = cairo.Context(Pc)
        Gdk.cairo_set_source_pixbuf( Pb, im, 0, 0)
        Pb.paint()
        
        if kind == "Frame":


            ref = win.images[asset][image][-1].copy()


            ref["binary"] = Pc
            win.images[asset][image].append(ref)

            n = len(win.images[asset][image])-1
            
            toggle = Gtk.ToggleButton("Frame "+str(n))
            toggle.connect("clicked", on_toggle_frame, n)
            toggle.set_relief(Gtk.ReliefStyle.NONE)
            frames_box.pack_start(toggle,0,0,0)
            frames_box.show_all()
        elif kind == "ChangeFrame":
            cf = selected_frame["is"]
            if cf == None:
                cf = 0
            win.images[asset][image][cf]["binary"] = Pc

        elif kind == "Sprite":

            good = "qwertyuiopasdfghjklzxcvbnm1234567890"
            
            sprite_id = ""
            for i in range(20):
                sprite_id = sprite_id + random.choice(good)

            
                
            win.images[asset][sprite_id] = [{"binary":Pc,
                                                     "title":"New Sprite Name",
                                                     "offset":[0,0]}]
            tree = list(treebox.get_children())[0].get_child()
            tree_store = tree.get_model()
            tree_path = str(tree.get_cursor().path)
            tree_path = tree_path[:tree_path.rfind(":")]
            tree_iter = tree_store.get_iter(tree_path)
            new = tree_store.insert(tree_iter, 0)
            tree_store.set_value(new, 0,"New Sprite Name")
            tree_store.set_value(new, 1, asset)
            tree_store.set_value(new, 2, sprite_id)
            tree.set_cursor(tree_path)

            # datapath thingy for frame selector
            fpath = asset+":"+sprite_id
            data_frames[fpath] = [""]

        elif kind == "Asset":

            good = "qwertyuiopasdfghjklzxcvbnm1234567890"
            
            sprite_id = ""
            for i in range(20):
                sprite_id = sprite_id + random.choice(good)
            
            asset = folder_path+"/"+asset_file_name.get_text()+".png"
            
            win.images[asset] = {
                sprite_id:[{"binary":Pc,
                            "title":"New Sprite Name",
                            "offset":[0,0]}],
                "title":asset_file_name.get_text(),
                "authors":[],
                "licenses":[],
                "menu_color":"#000000"
                
            }
            
            # datapath thingy for frame selector
            fpath = asset+":"+sprite_id
            data_frames[fpath] = [""]

            tree = list(treebox.get_children())[0].get_child()
            tree_store = tree.get_model()
            tree_path = str(tree.get_cursor().path)
            if is_asset_folder_skip:
                tree_path = tree_path[:tree_path.rfind(":")]
            tree_iter = tree_store.get_iter(tree_path)
            new = tree_store.insert(tree_iter, 0)
            tree_store.set_value(new, 0, asset_file_name.get_text())
            tree_store.set_value(new, 1, asset)
            tree_store.set_value(new, 2, "")

            
            tree_path = tree_path + ":0"
            tree_iter = tree_store.get_iter(tree_path)
            new = tree_store.insert(tree_iter, 0)
            tree_store.set_value(new, 0, "New Sprite Name" )
            tree_store.set_value(new, 1, asset)
            tree_store.set_value(new, 2, sprite_id )
            tree.set_cursor(tree_path)

            
            
        
    dialogWindow.destroy()

def tags_editor(win, data, return_edit_functions=False, auto_fill=[]):
    
    hack = {}
    hack["data"] = data
    
    def update(new_data):

        hack["data"] = new_data


        
        for i in tagsbox.get_children():
            i.destroy()
        for tag in new_data:

            add_tag(tag)
        
    def get():
        return hack["data"]
    
    tagscont = Gtk.HBox()
    
    
    tagscrl = Gtk.ScrolledWindow()
    tagscrl.set_size_request(40,40)
    tagscont.pack_start(tagscrl, True, True, 0)

    tagsbox = Gtk.HBox()
    tagscrl.add_with_viewport(tagsbox)

    def add_tag(tag):

        if not tag:
            return
        
        if tag not in hack["data"]:
            hack["data"].append(tag)
        tagb = Gtk.HBox()
        tagb.pack_start(Gtk.Label("  "+tag+"  "), False, False, 0)
        def kill(w):
            tagb.destroy()
            hack["data"].remove(tag)
        tagk = Gtk.Button("-")
        tagk.connect("clicked", kill)
        tagk.set_relief(Gtk.ReliefStyle.NONE)
        tagb.pack_start(tagk, False, False, 0)
        tagb.pack_start(Gtk.VSeparator(), False, False, 5)
        tagsbox.pack_start(tagb, False, False, 0)
        tagsbox.show_all()

        # Scroll to the last
        def later():
            time.sleep(0.1)
            def now():
                a = tagscrl.get_hadjustment()
                a.set_value(a.get_upper())
            GLib.idle_add(now)
        load_thread = threading.Thread(target=later)
        load_thread.start()
        # The threading is needed, since we want to wait
        # while GTK will update the UI and only then move
        # the adjustent. Becuase else, it will move to the
        # last previous, not to the last last.
            
    addt = Gtk.Button("+")
    addt.set_relief(Gtk.ReliefStyle.NONE)
    tagscont.pack_end(addt, False, False, 0)
    def on_entry(w):
        add_tag(tagentry.get_text())
        tagentry.set_text("")



    tagentry = Gtk.Entry()

    if auto_fill:
    
        liststore = Gtk.ListStore(str)
        completion = Gtk.EntryCompletion()
        completion.set_model(liststore)
        completion.set_text_column(0)

        for i in auto_fill:
            liststore.append((i,))

        tagentry.set_completion(completion)
        completion.set_minimum_key_length(0)
        completion.complete()
    
    tagentry.connect("activate", on_entry)
    addt.connect("clicked", on_entry)
    tagscont.pack_end(tagentry, False, False, False)

    
    
    for tag in data:
        add_tag(tag)

    if not return_edit_functions:
        return tagscont
    else:
        return tagscont, update, get

collapsable = Gtk.Expander(label="  Authors / Licenses:  ")
last_third.pack_end(collapsable, 0,0,5)
term_box = Gtk.VBox()
collapsable.add(term_box)
    
################### LICENSES ######################

tools = Gtk.HBox()
term_box.pack_end(tools, 0,0,5)

tools.pack_start(Gtk.Label("Licenses:"), 0,0,5)

licenses_editor, licenses_updator, licenses_get = tags_editor(win, [], True, [])
tools.pack_start(licenses_editor, 1,1,5)

################### AUTHORS ######################

tools = Gtk.HBox()
term_box.pack_end(tools, 0,0,5)

tools.pack_start(Gtk.Label("Authors:"), 0,0,5)

authors_editor, authors_updator, authors_get = tags_editor(win, [], True, [])
tools.pack_start(authors_editor, 1,1,5)

################ FORTH RAW #########################

tools = Gtk.HBox()
last_third.pack_end(tools, 0,0,5)

# Folder opener

def open_folder(w):

    path = selected_asset.get("path")

    if path.endswith(".png"):
        path = path[:path.rfind("/")+1]

    Popen(["xdg-open", path])
open_folder_b = Gtk.Button("Open Folder")
open_folder_b.connect("clicked", open_folder)
tools.pack_end(open_folder_b, 0,0,5)


# Name of the asset
tools.pack_start(Gtk.Label("Asset:"), 0,0,5)

def on_asset_name(w):
    asset = selected_asset.get("path")
    image = selected_asset.get("image")
    if asset_name.get_text() and image:
        win.images[asset]["title"] = asset_name.get_text()
        
        tree = list(treebox.get_children())[0].get_child()
        tree_store = tree.get_model()
        tree_path = str(tree.get_cursor().path)
        tree_path = tree_path[:tree_path.rfind(":")]
        tree_iter = tree_store.get_iter(tree_path)
        tree_store.set_value(tree_iter, 0, w.get_text())

asset_name = Gtk.Entry()
asset_name.connect("changed", on_asset_name)
tools.pack_start(asset_name, 0,0,5)

def on_color_set(w):
    r,g,b,a = w.get_rgba()

    r = int(r*255)
    g = int(g*255)
    b = int(b*255)
    
    def rgb_to_hex(rgb):
        return '#%02x%02x%02x' % rgb
    value = rgb_to_hex((r,g,b))

    asset = selected_asset.get("path")
    win.images[asset]["menu_color"] = value

asset_color = Gtk.ColorButton()
asset_color.connect('color-set', on_color_set)
tools.pack_start(asset_color, 0,0,5)

tools.pack_start(Gtk.VSeparator(), 0,0,10)

# ADD NEW ASSET

add_asset = Gtk.Button("+ Asset")
add_asset.connect("clicked", on_add_image, "Asset")
pannel.pack_start(add_asset)



last_third.pack_end(Gtk.HSeparator(), 0,0,10)
    
################ THIRD RAW #########################
    
tools = Gtk.HBox()
last_third.pack_end(tools, 0,0,5)

# The name of the sprite
tools.pack_start(Gtk.Label("Sprite:"), 0,0,5)

def on_sprite_name(w):
    asset = selected_asset.get("path")
    image = selected_asset.get("image")
    try:
        for cf, i in enumerate(win.images[asset][image]):
            if w.get_text() and image:

                win.images[asset][image][cf]["title"] = w.get_text()

                # Updating the treeview

                tree = list(treebox.get_children())[0].get_child()
                tree_store = tree.get_model()
                tree_iter = tree_store.get_iter(tree.get_cursor().path)
                tree_store.set_value(tree_iter, 0, w.get_text())
                

                
    except Exception as e:
        print(e)
        pass
    

sprite_name = Gtk.Entry()
sprite_name.connect("changed", on_sprite_name)
tools.pack_start(sprite_name, 0,0,5)



add_sprite = Gtk.Button(" + Sprite")
add_sprite.connect("clicked", on_add_image, "Sprite")
tools.pack_start(add_sprite, 0,0,5)

def on_remove_sprite(w):

    # First we need to see how much we have
    # if less then 2 sprites in the asset ( 1 )
    # we are going to abort.

    asset = selected_asset.get("path")
    image = selected_asset.get("image")

    
    if len(win.images[asset]) > 5: # Including metedata it's 5
        del win.images[asset][image]

        # Updating the treeview

        tree = list(treebox.get_children())[0].get_child()
        tree_store = tree.get_model()
        tree_iter = tree_store.get_iter(tree.get_cursor().path)
        tree_store.remove(tree_iter)
    
remove_sprite = Gtk.Button(" - Sprite")
remove_sprite.connect("clicked", on_remove_sprite)
tools.pack_start(remove_sprite, 0,0,5)



last_third.pack_end(Gtk.HSeparator(), 0,0,10)

################ SECOND RAW #########################

tools = Gtk.HBox()
last_third.pack_end(tools, 0,0,5)


    
add_frame = Gtk.Button(" + Frame")
add_frame.connect("clicked", on_add_image, "Frame")
tools.pack_start(add_frame, 0,0,5)


def on_remove_frame(w):
    asset = selected_asset.get("path")
    image = selected_asset.get("image")
    if selected_frame["is"] != None and len(win.images[asset][image]) > 1:
        del win.images[asset][image][selected_frame["is"]]

        for i in frames_box.get_children():
            i.destroy()
        for n, i in enumerate(win.images[asset][image]):
            toggle = Gtk.ToggleButton("Frame "+str(n))
            toggle.connect("clicked", on_toggle_frame, n)
            toggle.set_relief(Gtk.ReliefStyle.NONE)
            frames_box.pack_start(toggle,0,0,0)
        frames_box.show_all()
    selected_frame["is"] = None

remove_frame = Gtk.Button(" - Frame")
remove_frame.connect("clicked", on_remove_frame)
tools.pack_start(remove_frame, 0,0,5)


change_frame = Gtk.Button(" Replace Frame")
change_frame.connect("clicked", on_add_image, "ChangeFrame")
tools.pack_start(change_frame, 0,0,5)

def on_copy_image(w):

    asset = selected_asset.get("path")
    image = selected_asset.get("image")
    
    clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)

    f = "/tmp/clipboard_copy.png"
    cf = selected_frame["is"]
    if cf == None:
        cf = 0
    canvas = win.images[asset][image][cf]["binary"]
    canvas.write_to_png(f)

    img = Gtk.Image()
    img.set_from_file(f)
    clipboard.set_image(img.get_pixbuf())
    clipboard.store()

copy_frame = Gtk.Button(" Copy Frame")
copy_frame.connect("clicked", on_copy_image)
tools.pack_start(copy_frame, 0,0,5)

############### EXTRA MENU ##################

def on_toggle(w, key):

    asset = selected_asset.get("path")
    image = selected_asset.get("image")
    
    cf = selected_frame["is"]
    if cf == None:
        for cf, n in enumerate(win.images[asset][image]):
            win.images[asset][image][cf][key] = w.get_active()
        return

    win.images[asset][image][cf][key] = w.get_active()

extra_menu = Gtk.Popover()
extra_button = Gtk.MenuButton("Extra", popover=extra_menu)
tools.pack_start(extra_button, 0,0,5)

tools = Gtk.VBox()
extra_menu.add(tools)



collision_toggle = Gtk.CheckButton("Collision")
collision_toggle.set_active(True)
collision_toggle.connect("clicked", on_toggle, "collision")
tools.pack_start(collision_toggle, 0,0,5)

dynamic_toggle = Gtk.CheckButton("Dynamic")
dynamic_toggle.set_active(False)
dynamic_toggle.connect("clicked", on_toggle, "dynamic")
tools.pack_start(dynamic_toggle, 0,0,5)

main_character_toggle = Gtk.CheckButton("Main Character")
main_character_toggle.set_active(False)
main_character_toggle.connect("clicked", on_toggle, "main_character")
tools.pack_start(main_character_toggle, 0,0,5)

ramp_toggle = Gtk.CheckButton("A Ramp")
ramp_toggle.set_active(False)
ramp_toggle.connect("clicked", on_toggle, "ramp")
tools.pack_start(ramp_toggle, 0,0,5)

invisible_toggle = Gtk.CheckButton("Invisible")
invisible_toggle.set_active(False)
invisible_toggle.connect("clicked", on_toggle, "invisible")
tools.pack_start(invisible_toggle, 0,0,5)

damage_toggle = Gtk.CheckButton("Damage")
damage_toggle.set_active(False)
damage_toggle.connect("clicked", on_toggle, "damage")
tools.pack_start(damage_toggle, 0,0,5)

damage_amount_box = Gtk.HBox()
tools.pack_start(damage_amount_box, 0,0,5)
damage_amount_adjustment = Gtk.Adjustment(0.01,
                                        lower = 0,
                                        upper = 1,
                                        step_increment = 0.001)
damage_amount_spin = Gtk.SpinButton(adjustment = damage_amount_adjustment, digits = 3)
damage_amount_box.pack_start(Gtk.Label("Damage amount: "), 0,0,5)
damage_amount_box.pack_start(damage_amount_spin, 1,1,5)
def on_changed(w):

    asset = selected_asset.get("path")
    image = selected_asset.get("image")

    if selected_frame["is"] != None or len(win.images[asset][image]) == 1:
        cf = selected_frame["is"]
        if cf == None:
            cf = 0
        win.images[asset][image][cf]["damage_amount"] = damage_amount_spin.get_value()
    
damage_amount_spin.connect("value-changed", on_changed)
        
tools.show_all()

######################## FIRST RAW ########################

tools = Gtk.HBox()
last_third.pack_end(tools, 0,0,5)

def do_zoom_plus(w):
    win.zoom += 1
zoom_plus = Gtk.Button("+")
zoom_plus.connect("clicked", do_zoom_plus)
tools.pack_start(zoom_plus, 0,0,5)


def do_zoom_minus(w):
    win.zoom -= 1
    if win.zoom < 1:
        win.zoom = 1
zoom_minus = Gtk.Button("-")
zoom_minus.connect("clicked", do_zoom_minus)
tools.pack_start(zoom_minus, 0,0,5)

tools.pack_start(Gtk.VSeparator(), 0,0,10)

tools.pack_start(Gtk.Label("Offset x:"), 0,0,1)

xadjust = Gtk.Adjustment(0,
                         lower=-1000000000,
                         upper=1000000000,
                         step_increment=1) 
xentry  = Gtk.SpinButton(adjustment=xadjust)
tools.pack_start(xentry, 0,0,1)

def on_changed(w):

    asset = selected_asset.get("path")
    image = selected_asset.get("image")

    if selected_frame["is"] != None or len(win.images[asset][image]) == 1:
        cf = selected_frame["is"]
        if cf == None:
            cf = 0
        offx = int(xentry.get_value())
        offy = int(yentry.get_value())
        win.images[asset][image][cf]["offset"] = [int(offx), int(offy)]
    

xentry.connect("value-changed", on_changed)
    
tools.pack_start(Gtk.Label("Offset y:"), 0,0,1)

yadjust = Gtk.Adjustment(0,
                         lower=-1000000000,
                         upper=1000000000,
                         step_increment=1) 
yentry  = Gtk.SpinButton(adjustment=yadjust)
tools.pack_start(yentry, 0,0,1)
yentry.connect("value-changed", on_changed)

win.show_all()
Gtk.main()
