import posixpath
import tkinter as tk
import tkinter.ttk as ttk
from source.packages import PackageManager
from direct.showbase.ShowBase import ShowBase
from panda3d.core import VirtualFileSystem, Material, WindowProperties
from direct.stdpy.file import walk

import simplepbr
import gltf

def get_icons():
    return type('', (object,), {
        'app': tk.PhotoImage(file="icons/application.png"),
        'brick': tk.PhotoImage(file="icons/brick.png"),
        'bricks': tk.PhotoImage(file="icons/bricks.png"),
        'computer': tk.PhotoImage(file="icons/computer.png"),
        'folder': tk.PhotoImage(file="icons/folder.png"),
        'shape_group': tk.PhotoImage(file="icons/shape_group.png"),
        'pictures': tk.PhotoImage(file="icons/pictures.png"),
        'picture': tk.PhotoImage(file='icons/picture.png'),
        "page_red": tk.PhotoImage(file='icons/page_red.png'),
        'shape_square': tk.PhotoImage(file='icons/shape_square.png'),
        'color_wheel': tk.PhotoImage(file='icons/color_wheel.png'),
        'color_swatch': tk.PhotoImage(file='icons/color_swatch.png')
    })()

def add_package_contents_to_tree(tree, package, type, name, extensions, icon_folder, icon_group, icon_item):
    data_path = posixpath.join(package.mount_dir, type)
    tree.insert(package.mount_dir, 'end', data_path, text=f' {name}', image=icon_group)
    file_count = 0
    for root, paths, files in walk(data_path):
        for path in paths:
            tree.insert(root, 'end', posixpath.join(root, path), text=f' {path}', image=icon_folder)
        for file in files:
            if file.lower().endswith(extensions):
                file_count += 1
                tree.insert(root, 'end', posixpath.join(root, file), text=f' {file}', image=icon_item)
    tree.item(data_path, text=f' {name} ({file_count})')

def add_package_models_to_tree(tree, package, icons):
    add_package_contents_to_tree(tree, package, 'models/', '3D Models', ('.glb', '.gltf', '.obj', '.egg'), icons.folder, icons.shape_group, icons.shape_square)

def add_package_textures_to_tree(tree, package, icons):
    add_package_contents_to_tree(tree, package, 'textures/', 'Image Textures', ('.png', '.jpg', '.bmp'), icons.folder, icons.pictures, icons.picture)

def add_package_materials_to_tree(tree, package, icons):
    add_package_contents_to_tree(tree, package, 'materials/', 'Materials', ('.json'), icons.folder, icons.color_wheel, icons.color_swatch)

def add_package_to_tree(tree, package, icons):
    add_package_models_to_tree(tree, package, icons)
    add_package_textures_to_tree(tree, package, icons)
    add_package_materials_to_tree(tree, package, icons)

def init_packages_tree(tree, packages, icons):
    tree.insert('', 'end', '/', text=' /', open=True, image=icons.app)
    tree.insert('/', 'end', '/main/', text=f' Main Package "{packages.main_package.meta.name}" - {packages.main_package.meta.version}', image=icons.computer)
    tree.insert('/', 'end', '/include/', text=f' Included Packages ({len(packages.package_list)})', image=icons.bricks)
    add_package_to_tree(tree, packages.main_package, icons)
    for package in packages.package_list:
        tree.insert('/include/', 'end', package.mount_dir, text=f' "{package.meta.name}" - {package.meta.version}', image=icons.brick)
        add_package_to_tree(tree, package, icons)
    return tree

def init_gui(app, args):
    app.gui.geometry("700x500")
    app.gui.grid_rowconfigure(0, weight=10)
    app.gui.grid_rowconfigure(0, weight=1)
    app.gui.grid_columnconfigure(0, weight=1)
    package_frame = tk.Frame(app.gui, padx=4, pady=4)
    package_frame.grid(column=0, row=0, sticky="news")
    package_frame.grid_columnconfigure(0, weight=1)
    package_frame.grid_rowconfigure(0, weight=1)
    package_frame.grid_rowconfigure(1, weight=1000)
    packages_label = tk.Label(package_frame, text="Packages")
    packages_label.grid(column=0, row=0, sticky="w")
    packagetree_frame = tk.Frame(package_frame)
    packagetree_frame.grid(column=0, row=1, sticky="news")
    packagetree_frame.grid_columnconfigure(0, weight=1000)
    packagetree_frame.grid_columnconfigure(1, weight=1)
    packagetree_frame.grid_rowconfigure(0, weight=1)
    app.tree_packages = ttk.Treeview(packagetree_frame, show="tree")
    app.tree_packages.grid(column=0, row=0, sticky="news")
    packagetree_scrollbar = ttk.Scrollbar(packagetree_frame, orient="vertical", command=app.tree_packages.yview)
    packagetree_scrollbar.grid(column=1, row=0)
    app.tree_packages.bind('<Double-1>', app.on_packagetree_doubleclick)

    scene_frame = tk.Frame(app.gui, padx=4, pady=4)
    scene_frame.grid(column=0, row=1, sticky="news")
    scene_frame.grid_columnconfigure(0, weight=1)
    scene_frame.grid_rowconfigure(0, weight=1)
    scene_frame.grid_rowconfigure(1, weight=1000)
    scene_label = tk.Label(scene_frame, text="Scene Graph")
    scene_label.grid(column=0, row=0, sticky="w")
    scenetree_frame = tk.Frame(scene_frame)
    scenetree_frame.grid(column=0, row=1, sticky="news")
    scenetree_frame.grid_columnconfigure(0, weight=10000)
    scenetree_frame.grid_columnconfigure(1, weight=1)
    scenetree_frame.grid_rowconfigure(0, weight=1)
    app.tree_scene = ttk.Treeview(scenetree_frame, show="tree")
    app.tree_scene.grid(column=0, row=0, sticky="news")
    scenetree_scrollbar = ttk.Scrollbar(scenetree_frame, orient="vertical", command=app.tree_scene.yview)
    scenetree_scrollbar.grid(column=1, row=0)

class PackageExplorer:
    def __init__(self, args):
        self.gui = tk.Tk()
        self.panda = ShowBase()
        self.package_manager = PackageManager(VirtualFileSystem.getGlobalPtr())
        self.package_manager.set_main_package(args.main)
        if args.package:
            for include in args.package:
                self.package_manager.include_package(include)
        self.tree_packages = None
        self.tree_scene = None
        self.running = True
        simplepbr.init()
        gltf.patch_loader(self.panda.loader) 
        init_gui(self, args)
        init_packages_tree(self.tree_packages, self.package_manager, get_icons())
        self.gui.protocol("WM_DELETE_WINDOW", self.on_tk_close)
        
    def on_tk_close(self):
        self.running = False

    def on_packagetree_doubleclick(self, event):
        item = self.tree_packages.selection()[0]
        if item.lower().endswith(('.glb', '.gltf', '.obj', '.egg')):
            mat = Material()
            mat.setAmbient((1, 0, 0, 1))
            node = self.panda.loader.loadModel(item)
            node.reparentTo(self.panda.render)
            node.setMaterial(mat)        

    def loop(self):
        try:
            while self.running:
                self.panda.taskMgr.step()
                self.gui.update_idletasks()
                self.gui.update()
        except KeyboardInterrupt:
            return


def start_explorer(args):
    app = PackageExplorer(args)
    app.loop()