#!/usr/bin/env python
# -ThemeShooterTk by HoKaze-
# Produces preview of HBC theme

# import everything
from Tkinter import *
from tkMessageBox import *
import tkFileDialog
import zipfile
import os
from PIL import Image, ImageTk, ImageFont, ImageDraw, ImageChops, ImageEnhance
import re, linecache
from decimal import *
import random
import ConfigParser
import shutil #needed for shutil.rmtree, to remove directories and contents


# determine location of program folder (for extracting files, fonts, etc)
programpath = sys.path[0]

# determine current/original folder (for navigation, cli purposes, etc)
originalpath = os.getcwd()

# determine dark water theme folder (for missing files)
darkwaterpath = os.path.join(programpath, "darkwater")

# determine classic theme folder (for missing files)
classicpath = os.path.join(programpath, "classic")

# setup config file
config = ConfigParser.ConfigParser()
config.read(os.path.join(programpath, "settings"))

# setup which replacement theme to use
replacement_theme = (config.get("Images", "missing images replacement theme"))
if replacement_theme == "darkwater":
    replacement_theme_path = darkwaterpath
elif replacement_theme == "classic":
    replacement_theme_path = classicpath
else:
    replacement_theme_path = os.path.join(programpath, replacement_theme)

# use extra console output?
consoleoutput = (config.get("Text", "command line output"))


if consoleoutput == "yes":
    print "Drawing root window..."

# setup root window
root = Tk()
root.title("ThemeShooterTk")


# file dialog for choosing theme.zip
def choosefile():
    global themefile
    os.chdir(originalpath) #cd to original dir for file select
    themefile = tkFileDialog.askopenfilename(title="Open theme file", filetypes=[("zip archive",".zip"),("All files",".*")])
    filepath.delete(1.0, END)
    filepath.insert(END, themefile)
    
# generate screenshot in current directory
def genscreen():
    if consoleoutput == "yes":
        print "Generating screenshot..."
    os.chdir(originalpath) #cd to original dir to create file
    snapshot = Image.new("RGBA", (640, 480)) #create new image
    snap1 = Image.open(background_file) #open background
    snapshot.paste(snap1, (0, 0)) #place background
    # bubbles have been moved to ensure they remain behind all other objects
    snap4mask = Image.open(bubble1_file)
    snap4 = snap4mask.convert("RGB")
    #snapshot.paste(snap4, (73, 303), snap4mask)
    snapshot.paste(snap4, (random.randrange(0, 640), random.randrange(0, 480)), snap4mask) #random bubble placement
    snapshot.paste(snap4, (random.randrange(0, 640), random.randrange(0, 480)), snap4mask)
    snap5mask = Image.open(bubble2_file)
    snap5 = snap5mask.convert("RGB")
    #snapshot.paste(snap5, (468, 113), snap5mask)
    snapshot.paste(snap5, (random.randrange(0, 640), random.randrange(0, 480)), snap5mask)
    snapshot.paste(snap5, (random.randrange(0, 640), random.randrange(0, 480)), snap5mask)
    snap6mask = Image.open(bubble3_file)
    snap6 = snap6mask.convert("RGB")
    #snapshot.paste(snap6, (318, 398), snap6mask)
    snapshot.paste(snap6, (random.randrange(0, 640), random.randrange(0, 480)), snap6mask)
    snapshot.paste(snap6, (random.randrange(0, 640), random.randrange(0, 480)), snap6mask)

    snap2mask = Image.open(appsprev_file) #use image as mask
    snap2 = snap2mask.convert("RGB") #create RGB version
    snapshot.paste(snap2, (23, 198), snap2mask) #place image, using RGB and mask to avoid alpha problems...
    snap3mask = Image.open(appsnext_file)
    snap3 = snap3mask.convert("RGB")
    snapshot.paste(snap3, (553, 198), snap3mask)
    snap7mask = Image.open(appslisthover_file)
    snap7 = snap7mask.convert("RGB")
    snapshot.paste(snap7, (104, 58), snap7mask)
    snap8mask = Image.open(appslist_file)
    snap8 = snap8mask.convert("RGB")
    snapshot.paste(snap8, (104, 128), snap8mask) #create multiple...
    snapshot.paste(snap8, (104, 198), snap8mask)
    snapshot.paste(snap8, (104, 268), snap8mask)
    snapshot.paste(snap8, (104, 338), snap8mask)
    snap9mask = Image.open(networkicon_file)
    snap9 = snap9mask.convert("RGB")
    snapshot.paste(snap9, (579, 414), snap9mask)
    snap10mask = Image.open(geckoicon_file)
    snap10 = snap10mask.convert("RGB")
    snapshot.paste(snap10, (544, 414), snap10mask)
    snap11mask = Image.open("transtext.png")
    snap11 = snap11mask.convert("RGB")
    snapshot.paste(snap11, (0, 0), snap11mask)
    snap12mask = Image.open(os.path.join(programpath, "hbc.png"))
    snap12 = snap12mask.convert("RGB")
    snapshot.paste(snap12, (30, 418), snap12mask)

    # appdb images
    if use_ap_images == True:
        appdb_file = (config.get("Files", "application database"))
        # open appdb file
        config.read(os.path.join(programpath, appdb_file))
        # set images to use
        app_image1 = config.get("Images", "image1")
        app_image2 = config.get("Images", "image2")
        app_image3 = config.get("Images", "image3")
        app_image4 = config.get("Images", "image4")
        app_image5 = config.get("Images", "image5")
        # start pasting images
        snap13mask = Image.open(os.path.join(programpath, app_image1))
        snap13 = snap13mask.convert("RGB")
        snapshot.paste(snap13, (115, 65), snap13mask)
        snap14mask = Image.open(os.path.join(programpath, app_image2))
        snap14 = snap14mask.convert("RGB")
        snapshot.paste(snap14, (115, 135), snap14mask)
        snap15mask = Image.open(os.path.join(programpath, app_image3))
        snap15 = snap15mask.convert("RGB")
        snapshot.paste(snap15, (115, 205), snap15mask)
        snap16mask = Image.open(os.path.join(programpath, app_image4))
        snap16 = snap16mask.convert("RGB")
        snapshot.paste(snap16, (115, 275)) #for some reason this image corrupts the screenshot if pasted with its mask...
        snap17mask = Image.open(os.path.join(programpath, app_image5))
        snap17 = snap17mask.convert("RGB")
        snapshot.paste(snap17, (115, 345), snap17mask)
        

    snapshot.save("screenshot.png", "PNG") #save the image at last!
    if consoleoutput == "yes":
        print "Screenshot saved."

# main function - preview theme
def previewtheme():

    if consoleoutput == "yes":
        print "Opening theme zip file..."

    # open zipfile, extract contents to original directory...error with folders?
    os.chdir(originalpath) #return to original directory
    zf = zipfile.ZipFile(themefile, "r")

    # original code replaced with extractall method to actually extract directories...some issues with extractall vs outfile.write?

    #for name in zf.namelist():
    #    outfile = open(name, "wb")
    #    outfile.write(zf.read(name))
    #    outfile.close()

    if consoleoutput == "yes":
        print "Extracting theme zip file..."

    zf.extractall(originalpath)

    if consoleoutput == "yes":
        print "Checking if all required files exist..."

    # test if files exist, if not, use dark water theme replacements
    global appsprev_file
    if os.path.exists("apps_previous.png") == True:
        appsprev_file = "apps_previous.png"
    else:
        appsprev_file = os.path.join(replacement_theme_path, "apps_previous.png")
    global appsnext_file
    if os.path.exists("apps_next.png") == True:
        appsnext_file = "apps_next.png"
    else:
        appsnext_file = os.path.join(replacement_theme_path, "apps_next.png")
    global bubble1_file
    if os.path.exists("bubble1.png") == True:
        bubble1_file = "bubble1.png"
    else:
        bubble1_file = os.path.join(replacement_theme_path, "bubble1.png")
    global bubble2_file
    if os.path.exists("bubble2.png") == True:
        bubble2_file = "bubble2.png"
    else:
        bubble2_file = os.path.join(replacement_theme_path, "bubble2.png")
    global bubble3_file
    if os.path.exists("bubble3.png") == True:
        bubble3_file = "bubble3.png"
    else:
        bubble3_file = os.path.join(replacement_theme_path, "bubble3.png")
    global appslisthover_file
    if os.path.exists("apps_list_hover.png") == True:
        appslisthover_file = "apps_list_hover.png"
    else:
        appslisthover_file = os.path.join(replacement_theme_path, "apps_list_hover.png")
    global appslist_file
    if os.path.exists("apps_list.png") == True:
        appslist_file = "apps_list.png"
    else:
        appslist_file = os.path.join(replacement_theme_path, "apps_list.png")
    global networkicon_file
    if os.path.exists("icon_network_active.png") == True:
        networkicon_file = "icon_network_active.png"
    else:
        networkicon_file = os.path.join(replacement_theme_path, "icon_network_active.png")
    global geckoicon_file
    if os.path.exists("icon_usbgecko_active.png") == True:
        geckoicon_file = "icon_usbgecko_active.png"
    else:
        geckoicon_file = os.path.join(replacement_theme_path, "icon_usbgecko_active.png")
    global background_file
    if os.path.exists("background.png") == True:
        background_file = "background.png"
    else:
        background_file = os.path.join(replacement_theme_path, "background.png")


    if consoleoutput == "yes":
        print "Drawing preview window..."

    # setup preview window
    preview = Toplevel()
    preview.title("Theme Preview")
    
    # setup canvas and background
    os.chdir(originalpath) #return to original dir
    image1 = Image.open(background_file)
    image1 = ImageTk.PhotoImage(image1)
    background = Label(preview, image=image1)
    background.image = image1
    width1 = image1.width()
    height1 = image1.height()
    canvas =  Canvas(preview, width=width1, height=height1)
    canvas.pack()
    x = (width1)/2.0
    y = (height1)/2.0
    canvas.create_image(x, y, image=image1)

    # setup other images
    # bubbles have been moved to keep them behind other objects    
    image4 = Image.open(bubble1_file)
    image4 = ImageTk.PhotoImage(image4)
    bubble1 = Label(preview, image=image4)
    bubble1.image = image4
    #canvas.create_image(105, 335, image=image4)
    canvas.create_image(random.randrange(0, 640), random.randrange(0, 480), image=image4) #random bubble placement
    canvas.create_image(random.randrange(0, 640), random.randrange(0, 480), image=image4)

    image5 = Image.open(bubble2_file)
    image5 = ImageTk.PhotoImage(image5)
    bubble2 = Label(preview, image=image5)
    bubble2.image = image5
    #canvas.create_image(500, 145, image=image5)
    canvas.create_image(random.randrange(0, 640), random.randrange(0, 480), image=image5)
    canvas.create_image(random.randrange(0, 640), random.randrange(0, 480), image=image5)

    image6 = Image.open(bubble3_file)
    image6 = ImageTk.PhotoImage(image6)
    bubble3 = Label(preview, image=image6)
    bubble3.image = image6
    #canvas.create_image(350, 430, image=image6)
    canvas.create_image(random.randrange(0, 640), random.randrange(0, 480), image=image6)
    canvas.create_image(random.randrange(0, 640), random.randrange(0, 480), image=image6)

    image2 = Image.open(appsprev_file)
    image2 = ImageTk.PhotoImage(image2)
    apps_previous = Label(preview, image=image2)
    apps_previous.image = image2
    canvas.create_image(55, 220, image=image2)
    
    image3 = Image.open(appsnext_file)
    image3 = ImageTk.PhotoImage(image3)
    apps_next = Label(preview, image=image3)
    apps_next.image = image3
    canvas.create_image(585, 220, image=image3)

    image7 = Image.open(appslisthover_file)
    image7 = ImageTk.PhotoImage(image7)
    apps_list_hover = Label(preview, image=image7)
    apps_list_hover.image = image7
    canvas.create_image(320, 90, image=image7)

    image8 = Image.open(appslist_file)
    image8 = ImageTk.PhotoImage(image8)
    apps_list = Label(preview, image=image8)
    apps_list.image = image8
    canvas.create_image(320, 160, image=image8)
    canvas.create_image(320, 230, image=image8)
    canvas.create_image(320, 300, image=image8)
    canvas.create_image(320, 370, image=image8)

    image9 = Image.open(networkicon_file)
    image9 = ImageTk.PhotoImage(image9)
    icon_network_active = Label(preview, image=image9)
    icon_network_active.image = image9
    canvas.create_image(595, 430, image=image9)

    image10 = Image.open(geckoicon_file)
    image10 = ImageTk.PhotoImage(image10)
    icon_usbgecko_active = Label(preview, image=image10)
    icon_usbgecko_active.image = image10
    canvas.create_image(560, 430, image=image10)

    # setup font colours
    redvalue = linecache.getline("theme.xml", 5)
    redvalue = int(re.sub("\D", "", redvalue))
    greenvalue = linecache.getline("theme.xml", 6)
    greenvalue = int(re.sub("\D", "", greenvalue))
    bluevalue = linecache.getline("theme.xml", 7)
    bluevalue = int(re.sub("\D", "", bluevalue))
    alphavalue = linecache.getline("theme.xml", 8) 
    alphavalue = int(re.sub("\D", "", alphavalue))
    fontcolour = (redvalue, greenvalue, bluevalue) # sets RGB colour
    hexcolour = '#%02x%02x%02x' % fontcolour # converts RGB colour to hex
    decalpha = Decimal(alphavalue) / Decimal(255) # converts alphavalue to decimal

    # chosen font
    fontfile = os.path.join(programpath, "LiberationSans-Bold.ttf")

    # create lorem ipsum list
    lorem_lines = ["Lorem ipsum", "dolor sit amet consectetuer", "adipiscing elit.", "Aenean commodo ligula eget dolor.", "Aenean massa.", "Cum sociis natoque penatibus", "et magnis dis", "parturient montes, nascetur", "ridiculus mus.", "Donec quam felis, ultricies nec"]

    # use lorem ipsum dummy text or...
    if config.get("Text", "dummy text") == "yes":
        text_lines = lorem_lines
        if consoleoutput == "yes":
            print "Using lorem ipsum dummy text..."

    # use appdb text
    elif config.get("Text", "dummy text") == "no":
        appdb_file = (config.get("Files", "application database"))
        if consoleoutput == "yes":
            print "Using appdb text..."
        # show error if appdb file can't be found and default to lorem ipsum
        if os.path.isfile(os.path.join(programpath, appdb_file)) == False:
            showerror("Invalid or missing database", "The current setting for the application database is incorrect or the database is missing. Defaulting to lorem ipsum dummy text...")
            text_lines = lorem_lines
        # if appdb file can be found, use it
        elif os.path.isfile(os.path.join(programpath, appdb_file)) == True:
            # open appdb file
            config.read(os.path.join(programpath, appdb_file))
            # sort out text
            appdb_lines=[]
            appdb_lines.append(config.get("Text", "title1"))
            appdb_lines.append(config.get("Text", "description1"))
            appdb_lines.append(config.get("Text", "title2"))
            appdb_lines.append(config.get("Text", "description2"))
            appdb_lines.append(config.get("Text", "title3"))
            appdb_lines.append(config.get("Text", "description3"))
            appdb_lines.append(config.get("Text", "title4"))
            appdb_lines.append(config.get("Text", "description4"))
            appdb_lines.append(config.get("Text", "title5"))
            appdb_lines.append(config.get("Text", "description5"))
            text_lines = appdb_lines

    # if settings are incorrect, corrupted or deleted...
    else:
        showerror("Invalid dummy text setting", "The current setting for dummy text is incorrect. Defaulting to lorem ipsum text...")
        text_lines = lorem_lines

    global use_ap_images
    use_ap_images = False

    # re-open settings file to check for appdb images settings
    config.read(os.path.join(programpath, "settings"))
    if config.get("Images", "appdb images") == "yes":
        appdb_file = (config.get("Files", "application database"))
        if consoleoutput == "yes":
            print "Using appdb images..."

        # show error if appdb file can't be found
        if os.path.isfile(os.path.join(programpath, appdb_file)) == False:
            showerror("Invalid or missing database", "The current setting for the application database is incorrect or the database is missing. Application images will not be used...")

        # if appdb file can be found, use it
        elif os.path.isfile(os.path.join(programpath, appdb_file)) == True:
            # open appdb file
            config.read(os.path.join(programpath, appdb_file))
            # set images to use
            app_image1 = config.get("Images", "image1")
            app_image2 = config.get("Images", "image2")
            app_image3 = config.get("Images", "image3")
            app_image4 = config.get("Images", "image4")
            app_image5 = config.get("Images", "image5")

            # place images on canvas
            image13 = Image.open(os.path.join(programpath, app_image1))
            image13 = ImageTk.PhotoImage(image13)
            app1 = Label(preview, image=image13)
            app1.image = image13
            canvas.create_image(180, 90, image=image13)
            image14 = Image.open(os.path.join(programpath, app_image2))
            image14 = ImageTk.PhotoImage(image14)
            app2 = Label(preview, image=image14)
            app2.image = image14
            canvas.create_image(180, 160, image=image14)
            image15 = Image.open(os.path.join(programpath, app_image3))
            image15 = ImageTk.PhotoImage(image15)
            app3 = Label(preview, image=image15)
            app3.image = image15
            canvas.create_image(180, 230, image=image15)
            image16 = Image.open(os.path.join(programpath, app_image4))
            image16 = ImageTk.PhotoImage(image16)
            app4 = Label(preview, image=image16)
            app4.image = image16
            canvas.create_image(180, 300, image=image16)
            image17 = Image.open(os.path.join(programpath, app_image5))
            image17 = ImageTk.PhotoImage(image17)
            app5 = Label(preview, image=image17)
            app5.image = image17
            canvas.create_image(180, 370, image=image17)
            use_ap_images = True

        # if settings are incorrect, corrupted or deleted...
        else:
            showerror("Invalid images setting", "The current setting for application images is incorrect. Application images will not be used...")

    if consoleoutput == "yes":
        print "Generating transtext.png..."

    # position, text, colour, size of dummy text
    dummy_data = [
        ((260, 65), text_lines[0], hexcolour, 16),
        ((260, 95), text_lines[1], hexcolour, 16),
        ((260, 135), text_lines[2], hexcolour, 16),
        ((260, 165), text_lines[3], hexcolour, 16),
        ((260, 205), text_lines[4], hexcolour, 16),
        ((260, 235), text_lines[5], hexcolour, 16),
        ((260, 275), text_lines[6], hexcolour, 16),
        ((260, 305), text_lines[7], hexcolour, 16),
        ((260, 345), text_lines[8], hexcolour, 16),
        ((260, 375), text_lines[9], hexcolour, 16),
        ]

    # create fully transparent image + alpha channel
    ttf_image = Image.new("RGB", (640, 480), (0,0,0))
    alpha = Image.new("L", ttf_image.size, "black")

    for pos, text, colour, size in dummy_data:

        # Make a grayscale image of the font, white on black.
        imagetext = Image.new("L", ttf_image.size, 0)
        drawtext = ImageDraw.Draw(imagetext)
        font = ImageFont.truetype(fontfile, size)
        drawtext.text(pos, text, font=font, fill="white")
        
        # Add the white text to our collected alpha channel. Gray pixels around
        # the edge of the text will eventually become partially transparent
        # pixels in the alpha channel.
        alpha = ImageChops.lighter(alpha, imagetext)
    
        # Make a solid color, and add it to the colour layer on every pixel
        # that has even a little bit of alpha showing.
        solidcolour = Image.new("RGBA", ttf_image.size, colour)
        imagemask = Image.eval(imagetext, lambda p: 255 * (int(p != 0)))
        ttf_image = Image.composite(solidcolour, ttf_image, imagemask)

    # Add the alpha channel to the image, set text transparency, then save
    ttf_image.putalpha(alpha)
    alpha2 = ttf_image.split()[3]
    alpha2 = ImageEnhance.Brightness(alpha2).enhance(decalpha)
    ttf_image.putalpha(alpha2)
    ttf_image.save("transtext.png", "PNG")

    if consoleoutput == "yes":
        print "transtext.png saved."

    # place transtext image on canvas
    image11 = Image.open("transtext.png")
    image11 = ImageTk.PhotoImage(image11)
    dummytext = Label(preview, image=image11)
    dummytext.image = image11
    canvas.create_image(320, 240, image=image11)

    # finally insert homebrew channel logo
    image12 = Image.open(os.path.join(programpath, "hbc.png"))
    image12 = ImageTk.PhotoImage(image12)
    hbc = Label(preview, image=image12)
    hbc.image = image12
    canvas.create_image(160, 430, image=image12)

    # create screenshot button
    screenshot = Button(root, text="Generate Screenshot", command=genscreen)
    screenshot.grid(row=2, columnspan=3, pady=5, padx=5)

    # preview window cannot be resized
    preview.resizable(0,0)

    if consoleoutput == "yes":
        print "Preview window done!"

    # start preview window
    preview.mainloop


# setup contents of root window
label = Label(root, text="Theme.zip: ")
filepath = Text(root, height=1, width=40)
browse = Button(root, text="Browse", command=choosefile)
preview = Button(root, text="Preview theme", command=previewtheme)

# position root window contents
label.grid(row=0, column=0, pady=10, padx=5)
filepath.grid(row=0, column=1, pady=10)
browse.grid(row=0, column=2, pady=10, padx=5)
preview.grid(row=1, columnspan=3, pady=5, padx=5)

# root window cannot be resized
root.resizable(0,0)

if consoleoutput == "yes":
    print "Root window done!"

# start root window
root.mainloop()


if consoleoutput == "yes":
    print "Performing file cleanup..."

# remove extracted files from current/original directory, AFTER root window has been closed
os.chdir(originalpath)
extractedfiles = zipfile.ZipFile(file(themefile))
for i in extractedfiles.infolist():
    if os.path.isfile(i.filename): #if file, delete file
        os.remove(i.filename)
    else:
        shutil.rmtree(i.filename) #if folder, delete folder AND contents

os.remove("transtext.png") #then remove transparent text file


if consoleoutput == "yes":
    print "File cleanup done!"
