#!/usr/local/bin/python2.7
"""<title>a simple tile editor for pygame</title>
<pre>

usage: tileedit tiles.tga [tile_w] [tile_h]
windows: python tileedit tiles.tga [tile_w] [tile_h]

options:
  -h, --help            show this help message and exit
  --sw=SCREEN_W         screen width (app)
  --sh=SCREEN_H         screen height (app)
  --tw=TILE_W           tile width (image)
  --th=TILE_H           tile height (image)
  --width=WIDTH         new width (image)
  --height=HEIGHT       new height (image)
  -pPALETTE, --pal=PALETTE
                        filename of palette (image)
  -d, --defaults        set default settings (image)

example:
tileedit tiles.tga 16 16

note:
the editor can only edit tga files.

you may edit default options in tileedit.ini

interface:
- menus for common commands
- toolbox
- tile edit area
    left click to use current tool
    right click to select color
- tile select area
    left click to select a tile
    right click to select any region
- color select area
    click to select a color

keys:
s - save
d - reload

z - undo
x - cut selection
c - copy selection
v - paste clipboard
delete - delete selection
f - fill selection
e - draw ellipse in selection
a - select all

[ - rotate -90
] - rotate +90
, - flip horizontal
. - flip vertical

arrows - change tile
F10 - toggle fullscreen
</pre>
"""

import os,sys    
from optparse import OptionParser
from ConfigParser import ConfigParser
import pygame
from pygame.locals import *

# try:
#     import Image
#     have_pil=True
# except:
#     print "import Image failure; PIL not found."
#     have_pil=False

have_pil = False

# the following line is not needed if pgl is installed
import os
import sys; sys.path.insert(0, os.path.join('lib'))
import shutil

from pgu import gui, html


#whatever...
ini_fname = "tileedit.ini"
ini = ConfigParser()

cfg = {}

class _app(gui.Container):
    def __init__(self):
        gui.Container.__init__(self)
        #self.cls = "desktop"
        #self.background = gui.Box(self.style.background)
        
        self.screen_w = cfg['screen_w']
        self.screen_h = cfg['screen_h']
        self.screen = pygame.display.set_mode((self.screen_w,self.screen_h),SWSURFACE)
        
        self.fname = cfg['fname']

        if self.fname != None:
            if have_pil==True:

                im = Image.open(self.fname)
                mode = im.mode
                size = im.size
                data = im.tostring()

                assert mode in ("RGB", "RGBA")

                self.tiles = pygame.image.fromstring(data, size, mode)

            else:
                self.tiles = pygame.image.load(self.fname) # old tga-only method
        else:
            w,h = cfg['width'],cfg['height']
            s = pygame.Surface((w,h),SWSURFACE|SRCALPHA,32)
            s.fill((0,0,0,0))
            self.tiles = s

        self.tiles_w, self.tiles_h = self.tiles.get_width(),self.tiles.get_height()
            
        
        self.tile_w, self.tile_h = cfg['tile_w'],cfg['tile_h'] 
        self.tile = self.tiles.subsurface((0,0,self.tile_w,self.tile_h))
        
        self.color = (255,255,255,255)
        self.mode = 'draw'
        self.clipboard = None
        self.select = Rect(0,0,self.tile_w,self.tile_h)
        self.history = []
        self.dirty = 0
        
    def archive(self):
        h = self.history
        if len(h) >= 32:
            del h[0]
        c = pygame.Surface((self.tile_w,self.tile_h),SWSURFACE,self.tile)
        c.fill((0,0,0,0))
        c.blit(self.tile,(0,0))
        h.append((c,self.tile.get_offset()))
        self.dirty = 1
        
    def undo(self):
        if len(self.history) == 0: return
        c,off = self.history.pop()
        self.tiles.fill((0,0,0,0),(off[0],off[1],self.tile_w,self.tile_h))
        self.tiles.blit(c,off)
        self.tdraw.repaint()
        self.tpicker.repaint()
        self.dirty = 1
        
    def __setattr__(self,k,v):
        self.__dict__[k] = v
        
        if k == 'color':
            if hasattr(self,'cpreview'): self.cpreview.repaint()
        if k == 'tile':
            if hasattr(self,'tdraw'): self.tdraw.repaint()
            if hasattr(self,'tpicker'): self.tpicker.repaint()
            if hasattr(self,'tpreview'): self.tpreview.repaint()
    def event(self,e):
        if e.type is KEYDOWN:
            for key,cmd,value in keys:
                if e.key == key:
                    cmd(value)
                    return
        return gui.Container.event(self,e)
    
class cpreview(gui.Widget):
    def __init__(self,w,h):
        gui.Widget.__init__(self)
        self.style.width = w 
        self.style.height = h
        
    def paint(self,s):
        s.fill((128,128,128))
        s.fill(app.color)

        
class cpicker(gui.Widget):
    def __init__(self,w,h,pal):
        gui.Widget.__init__(self)
        self.style.width = w
        self.style.height = h
        self.palette = pal
        self.palette_w = pal.get_width()
        self.palette_h = pal.get_height()
    
    def paint(self,s):
        s.blit(pygame.transform.scale(self.palette,(self.rect.w,self.rect.h)),(0,0))
            
    def event(self,e):
        if (e.type is MOUSEBUTTONDOWN) or (e.type is MOUSEMOTION and e.buttons[0] == 1 and self.container.myfocus == self):
            x,y = e.pos[0]*self.palette_w/self.rect.w,e.pos[1]*self.palette_h/self.rect.h
            x,y = max(0,x),max(0,y)
            x,y = min(self.palette_w-1,x),min(self.palette_h-1,y)
        
            app.color = self.palette.get_at((x,y))

class tpicker(gui.Widget):
    def __init__(self):
        gui.Widget.__init__(self)
        self.style.width = app.tiles_w
        self.style.height = app.tiles_h
        
    def paint(self,s):
        s.fill((128,128,128))
        s.blit(app.tiles,(0,0))
        off = app.tile.get_offset()
        pygame.draw.rect(s,(255,255,255),(off[0],off[1],app.tile_w,app.tile_h),2)
        
    def pick(self,pos):
        x,y = pos
        while x < 0: x += self.rect.w
        while y < 0: y += self.rect.h
        while x >= self.rect.w: x -= self.rect.w
        while y >= self.rect.h: y -= self.rect.h
        app.tile = app.tiles.subsurface((x,y,app.tile_w,app.tile_h))

    def event(self,e):
        if (e.type is MOUSEBUTTONDOWN and e.button == 1) or (e.type is MOUSEMOTION and e.buttons[0] == 1 and self.container.myfocus == self):
            x,y = e.pos[0]/app.tile_w*app.tile_w,e.pos[1]/app.tile_h*app.tile_h
            self.pick((x,y))
            
        if (e.type is MOUSEBUTTONDOWN and e.button == 3) or (e.type is MOUSEMOTION and e.buttons[2] == 1 and self.container.myfocus == self):
            x,y = e.pos[0]-app.tile_w/2,e.pos[1]-app.tile_h/2
            x = min(self.rect.w-app.tile_w-1,max(0,x))
            y = min(self.rect.h-app.tile_h-1,max(0,y))
            self.pick((x,y))

class tpreview(gui.Widget):
    def __init__(self):
        gui.Widget.__init__(self)
        self.style.width = app.tile_w*3
        self.style.height = app.tile_h*3
    def paint(self, s):
        atw = app.tile_w
        ath = app.tile_h
        s.fill((100,100,100))
        s.blit(app.tile,(0,0))
        s.blit(app.tile,(1*atw,0))
        s.blit(app.tile,(2*atw,0))
        s.blit(app.tile,(0,1*ath))
        s.blit(app.tile,(1*atw,1*ath))
        s.blit(app.tile,(2*atw,1*ath))
        s.blit(app.tile,(0,2*ath))
        s.blit(app.tile,(1*atw,2*ath))
        s.blit(app.tile,(2*atw,2*ath))        

class tdraw(gui.Widget):
    def __init__(self,w,h):
        gui.Widget.__init__(self)
        self.rect.w = self.style.width = w
        self.rect.h = self.style.height = h
        self.overlay = pygame.Surface((app.tile_w,app.tile_h)).convert_alpha()
        self.overlay.fill((0,0,0,0))
        s = pygame.Surface((self.rect.w,self.rect.h))
        clrs = [(148,148,148),(108,108,108)]
        for y in range(0,app.tile_h*2):
            for x in range(0,app.tile_w*2):
                s.fill(clrs[(x+y)%2],(
                    self.rect.w*x/(app.tile_w*2),
                    self.rect.h*y/(app.tile_h*2),
                    self.rect.w/(app.tile_w*2)+1,
                    self.rect.h/(app.tile_h*2)+2))
        self.bg = s

        s = pygame.Surface((self.rect.w,self.rect.h)).convert_alpha()
        s.fill((0,0,0,0))
        for x in range(0,app.tile_w):
            pygame.draw.line(s,(0,0,0),(self.rect.w*x/app.tile_w,0),(self.rect.w*x/app.tile_w,self.rect.h))
        for y in range(0,app.tile_h):
            pygame.draw.line(s,(0,0,0),(0,self.rect.h*y/app.tile_h),(self.rect.w,self.rect.h*y/app.tile_h))
        self.grid = s

        
    def paint(self,s):
        s.blit(self.bg,(0,0))
        s.blit(pygame.transform.scale(app.tile,(self.rect.w,self.rect.h)),(0,0))
        s.blit(pygame.transform.scale(self.overlay,(self.rect.w,self.rect.h)),(0,0))
        #if app.mode == 'select':
        s.blit(self.grid,(0,0))
        r = app.select
        pygame.draw.rect(s,(255,255,255,128),Rect(r.x*self.rect.w/app.tile_w,r.y*self.rect.h/app.tile_h,r.w*self.rect.w/app.tile_w,r.h*self.rect.h/app.tile_h),4)
        
    def event(self,e):
        if (e.type is MOUSEBUTTONDOWN and e.button == 3) or (e.type is MOUSEMOTION and e.buttons[2]==1 and self.container.myfocus == self):
            self.picker_down(e)
        if e.type is MOUSEBUTTONDOWN and e.button == 1:
            a = '%s_down'%app.mode
            if hasattr(self,a): getattr(self,a)(e)
        if e.type is MOUSEMOTION and e.buttons[0] and self.container.myfocus == self:
            a = '%s_drag'%app.mode
            if hasattr(self,a): getattr(self,a)(e)
        if e.type is MOUSEBUTTONUP and e.button == 1:
            a = '%s_up'%app.mode
            if hasattr(self,a): getattr(self,a)(e)
            
    #picker
    def picker_down(self,e):
        pos = self.getpos(e)
        c = app.tile.get_at(pos)
        app.color = c
    
    #fill
    def fill_down(self,e):
        app.archive()
        pos = self.getpos(e)
        bg = app.tile.get_at(pos)
        if bg == app.color: return
        self.fill_pixel(pos,bg)
        self.repaint()
        
        
    def fill_pixel(self,pos,bg): #worst algorithm
        c = app.tile.get_at(pos)
        if c != bg: return
        app.tile.set_at(pos,app.color)
        x,y = pos
        if x > 0: self.fill_pixel((x-1,y),bg)
        if x < app.tile_w-1: self.fill_pixel((x+1,y),bg)
        if y > 0: self.fill_pixel((x,y-1),bg)
        if y < app.tile_h-1: self.fill_pixel((x,y+1),bg)
        
    #pixel        
    def pixel_down(self,e):
        app.archive()
        pos = self.getpos(e)
        app.tile.set_at(pos,app.color)
        self.repaint()
        
    #line
    def line_down(self,e):
        pos = self.getpos(e)
        self.pos = pos
        
    def line_drag(self,e):
        self.overlay.fill((0,0,0,0))
        pos = self.getpos(e)
        pygame.draw.line(self.overlay,app.color,self.pos,pos)
        self.repaint()
    
    def line_up(self,e):
        app.archive()
        self.overlay.fill((0,0,0,0))
        pos = self.getpos(e)
        pygame.draw.line(app.tile,app.color,self.pos,pos)
        self.repaint()
        
    #ellipse
    def ellipse_down(self,e):
        pos = self.getpos(e)
        self.pos = pos
        
    def ellipse_drag(self,e):
        self.overlay.fill((0,0,0,0))
        pos = self.getpos(e)
        r = pygame.Rect(self.pos[0],self.pos[1],pos[0]-self.pos[0],pos[1]-self.pos[1])
        r.normalize()
        r.width += 1
        r.height += 1
        r.width,r.height = max(2,r.width),max(2,r.height)
        pygame.draw.ellipse(self.overlay,app.color,r,1)
        self.repaint()
    
    def ellipse_up(self,e):
        app.archive()
        self.overlay.fill((0,0,0,0))
        pos = self.getpos(e)
        r = pygame.Rect(self.pos[0],self.pos[1],pos[0]-self.pos[0],pos[1]-self.pos[1])
        r.normalize()
        r.width += 1
        r.height += 1
        r.width,r.height = max(2,r.width),max(2,r.height)
        pygame.draw.ellipse(app.tile,app.color,r,1)
        self.repaint()
        
    #draw
    def draw_down(self,e):
        app.archive()
        pos = self.getpos(e)
        app.tile.set_at(pos,app.color)
        self.pos = pos
        self.repaint()
        
    def draw_drag(self,e):
        pos = self.getpos(e)
        pygame.draw.line(app.tile,app.color,self.pos,pos)
        self.pos = pos
        self.repaint()
        
    def getpos(self,e):
        x,y = (e.pos[0])*app.tile_w/self.rect.w,(e.pos[1])*app.tile_h/self.rect.h
        x = min(max(0,x),app.tile_w-1)
        y = min(max(0,y),app.tile_h-1)
        return x,y
    
    def getpos2(self,e):
        w = self.rect.w/app.tile_w
        h = self.rect.h/app.tile_h
        x,y = (e.pos[0]+w/2)*app.tile_w/self.rect.w,(e.pos[1]+h/2)*app.tile_h/self.rect.h
        x = min(max(0,x),app.tile_w)
        y = min(max(0,y),app.tile_h)
        return x,y
    
            
    
    #select
    def select_down(self,e):
        pos = self.getpos2(e)
        app.select = Rect(pos[0],pos[1],0,0)
        self.repaint()
        
    def select_drag(self,e):
        pos = self.getpos2(e)
        #pos = (e.pos[0]+app.tile_w/2)/app.tile_w,(e.pos[1]+app.tile_h/2)/app.tile_h
        
        app.select = Rect(app.select.x,app.select.y,pos[0]-app.select.x,pos[1]-app.select.y)
        app.select.w = max(0,app.select.w)
        app.select.h = max(0,app.select.h)
        
        #print app.select
        
        self.repaint()
        

        
    #eraser
    def eraser_down(self,e):
        app.archive()
        pos = self.getpos(e)
        app.tile.set_at(pos,(0,0,0,0))
        self.pos = pos
        self.repaint()
        
    def eraser_drag(self,e):
        pos = self.getpos(e)
        pygame.draw.line(app.tile,(0,0,0,0),self.pos,pos)
        self.pos = pos
        self.repaint()
        

def cmd_quit(value):
    if app.dirty: _dirty(_cmd_quit,value)
    else: _cmd_quit(value)

def _cmd_quit(value):
    app.top.quit()

def cmd_all(value):
    app.select = Rect(0,0,app.tile_w,app.tile_h)
    app.tdraw.repaint()

def cmd_undo(value):
    app.undo()
    
def cmd_redo(value):
    pass
    
def cmd_copy(value):
    #next version of pygame?
    #app.clipboard = app.tile.subsurface(app.select).copy()
    s = app.tile.subsurface(app.select)
    app.clipboard = pygame.Surface((app.select.w,app.select.h),SWSURFACE,s)
    app.clipboard.fill((0,0,0,0))
    app.clipboard.blit(s,(0,0))
    
def cmd_paste(value):
    if app.clipboard != None:
        app.archive()
        app.tile.fill((0,0,0,0),(app.select.x,app.select.y,app.clipboard.get_width(),app.clipboard.get_height()))
        app.tile.blit(app.clipboard,app.select)
        app.tdraw.repaint()

def cmd_cut(value):
    cmd_copy(value)
    cmd_delete(value)
    
def cmd_fullscreen(value):
    pygame.display.toggle_fullscreen()
    
def cmd_delete(value):
    app.archive()
    app.tile.fill((0,0,0,0),app.select)
    app.tdraw.repaint()

def cmd_fill(value):        
    app.archive()
    app.tile.fill(app.color,app.select)
    app.tdraw.repaint()

#NOTE: this feature is a temporary HACK, to be replaced by
#an ellipse tool in the future
def cmd_ellipse(value):
    app.archive()
    pygame.draw.ellipse(app.tile,app.color,app.select,1)
    app.tdraw.repaint()
        
def cmd_rotate(value):
    a = value
    app.archive()
    s = pygame.transform.rotate(app.tile,a)
    app.tile.fill((0,0,0,0))
    app.tile.blit(s,(0,0))
    app.tdraw.repaint()
    
def cmd_flip(value):
    fh,fv = value
    app.archive()
    s = pygame.transform.flip(app.tile,fh,fv)
    app.tile.fill((0,0,0,0))
    app.tile.blit(s,(0,0))
    app.tdraw.repaint()

def cmd_tpick(value):
    dx,dy = value
    off = app.tile.get_offset()
    app.tpicker.pick((off[0]+dx*app.tile_w,off[1]+dy*app.tile_h))
    
def cmd_mode(value):
    mode = value
    app.mode = mode

def cmd_load(value):
    if app.dirty: _dirty(_cmd_load,value)
    else: _cmd_load(value)


def _cmd_load(value):
    if app.fname == None:
        ErrorDialog("Load failed","Image is untitled.").open()
        return
    raise Restart()
    

def cmd_active_save(value):
    """ we check if we want to save on screen focus...
    """

    if app.save_activeevent_switch.value:
        if app.dirty:
            #"is dirty... saving"
            return cmd_save(value)
    else:
        pass
        #"is not dirty, not saving"




def cmd_save(value):
    if app.fname == None:
        return cmd_saveas(value)
    try:
        # make a temp file... save it there, and then move it in.
        # so as to avoid race with anything reading it.

        #THIS CODE DOESN'T WORK
        #temp_file_name = "tmp_" + app.fname
        #print temp_file_name
        temp_file_name = app.fname
        if have_pil==True:
            stim = pygame.image.tostring(app.tiles, "RGB")
            im=Image.fromstring("RGB", (app.tiles.get_width(),app.tiles.get_height()), stim)
            im.save(temp_file_name)
        else:
            pygame.image.save(app.tiles,temp_file_name)
        #move temp file into place.
        #shutil.move(temp_file_name, app.fname)
        
        cfg_to_ini(['tile_w','tile_h','palette'],app.fname)
        ini_save()
        app.dirty = 0
    except Exception, v:
        ErrorDialog("Save failed.",v).open()
        return
    
def cmd_saveas(value):
    dialog = SaveAsDialog()
    
    def onchange(value):
        value.close()
        vv = value.value
        fname = vv['fname'].value
        if len(fname) == 0:
            ErrorDialog("Save As failed.","File Name too short!").open()
            return
        global cfg
        app.fname = cfg['fname'] = fname
        return cmd_save(None)
        
    dialog.connect(gui.CHANGE,onchange,dialog)
    dialog.open()

def cmd_open(value):
    if app.dirty: _dirty(_cmd_open,value)
    else: _cmd_open(value)

    
def _cmd_open(value):
    dialog = OpenDialog()
    
    def onchange(value):
        value.close()
        vv = value.value
        ok = 0
        
        try:        
            fname,tile_w,tile_h = vv['fname'].value,int(vv['tile_w'].value),int(vv['tile_h'].value)
            global cfg
            cfg['fname'] = fname
            cfg['tile_w'] = tile_w
            cfg['tile_h'] = tile_h
            ok = 1
        except Exception,v:
            ErrorDialog("Open failed.",v).open()
            
        if ok: raise Restart()

    
    dialog.connect(gui.CHANGE,onchange,dialog)
    dialog.open()


class Restart(Exception):
    pass
def _dirty(fnc,v):
    dialog = DirtyDialog()
    def onchange(value):
        value.close()
        return fnc(v)
    dialog.connect(gui.CHANGE,onchange,dialog)
    dialog.open()
    
def cmd_new(value):
    if app.dirty: _dirty(_cmd_new,value)
    else: _cmd_new(value)
    
        
def _cmd_new(value):
    dialog = NewDialog()
    
    def onchange(value):
        value.close()
        vv = value.value
        ok = 0
        try:        
            width,height,tile_w,tile_h = int(vv['width'].value),int(vv['height'].value),int(vv['tile_w'].value),int(vv['tile_h'].value)
            global cfg
            cfg['fname'] = None
            cfg['width'] = width
            cfg['height'] = height
            cfg['tile_w'] = tile_w
            cfg['tile_h'] = tile_h
            ok = 1
        except Exception, v:
            ErrorDialog("New failed.",v).open()
        if ok:
            raise Restart()
    
    dialog.connect(gui.CHANGE,onchange,dialog)
    dialog.open()


menus = [
    ('File/New',cmd_new,None),
    ('File/Open',cmd_open,None),
    ('File/Save',cmd_save,None),
    ('File/Save As',cmd_saveas,None),
    ('File/Reload',cmd_load,None),
    ('File/Quit',cmd_quit,None),

    ('Edit/Undo',cmd_undo,None),
    #('Edit/Redo',None,None,None),
    ('Edit/Cut',cmd_cut,None),
    ('Edit/Copy',cmd_copy,None),
    ('Edit/Paste',cmd_paste,None),
    ('Edit/Delete',cmd_delete,None),
    ('Edit/Fill',cmd_fill,None),
        ('Edit/Ellipse',cmd_ellipse,None),
    ('Edit/Select All',cmd_all,None),
    
    ('Transform/Rotate 90 CCW',cmd_rotate,90),
    ('Transform/Rotate 90 CW',cmd_rotate,-90),
    ('Transform/Flip Horizontal',cmd_flip,(1,0)),
    ('Transform/Flip Vertical',cmd_flip,(0,1)),
    ]

keys = [
    (K_s,cmd_save,None),
    (K_d,cmd_load,None),

    (K_a,cmd_all,None),
    (K_z,cmd_undo,None),
    #('Edit/Redo',None,None,None),
    (K_c,cmd_copy,None),
    (K_v,cmd_paste,None),
    (K_x,cmd_cut,None),
    (K_DELETE,cmd_delete,None),
    (K_f,cmd_fill,None),
        (K_e,cmd_ellipse,None),
    
    (K_LEFTBRACKET,cmd_rotate,90),
    (K_RIGHTBRACKET,cmd_rotate,-90),
    (K_COMMA,cmd_flip,(1,0)),
    (K_PERIOD,cmd_flip,(0,1)),
    
    (K_UP,cmd_tpick,(0,-1)),
    (K_DOWN,cmd_tpick,(0,1)),
    (K_LEFT,cmd_tpick,(-1,0)),
    (K_RIGHT,cmd_tpick,(1,0)),
    
    (K_F10,cmd_fullscreen,None),
    ]
    

tools = [
    ('draw','draw'),
    ('pixel','pixel'),
    ('line','line'),
    #('ellipse','ellipse'),
    ('fill','fill'),
    ('select','select'),
    ('eraser','eraser'),
    ]


def init_ini():
    ini.read([ini_fname])

def ini_save():
    f = open(ini_fname,"wb")
    ini.write(f)
    f.close()
    

def init_opts():
    usage = "%prog [tiles.tga] [tile_w] [tile_h]"

    parser = OptionParser(usage)
    parser.add_option("--sw",dest="screen_w",help="screen width (app)",type='int')
    parser.add_option("--sh",dest="screen_h",help="screen height (app)",type='int')
    parser.add_option("--tw",dest="tile_w",help="tile width (image)",type='int')
    parser.add_option("--th",dest="tile_h",help="tile height (image)",type='int')
    parser.add_option("--width",dest="width",help="new width (image)",type='int')
    parser.add_option("--height",dest="height",help="new height (image)",type='int')
    parser.add_option("-p","--pal",dest="palette",help="filename of palette (image)")
    parser.add_option("-d","--defaults",dest="defaults",help="set default settings (image)",action="store_true")
    #parser.add_option("-a","--app",dest="app",help="set application level defaults",action="store_true")
    
    (opts,args) = parser.parse_args()
    
    if len(args) > 3: parser.error("incorrect number of arguments")
    
    #parse arguments
    if len(args) == 0:
        opts.fname = "None"
    elif len(args) == 1:
        opts.fname = args[0]
    elif len(args) == 2:
        opts.fname = "None"
        try: opts.tile_w,opts.tile_h = int(args[0]),int(args[1])
        except: parser.error("tile width and height must be integers")
        if opts.tile_w < 1 or opts.tile_h < 1: parser.error("width and height must be greater than 0")
    else:
        try: opts.fname,opts.tile_w,opts.tile_h = args[0],int(args[1]),int(args[2])
        except: parser.error("tile width and height must be integers")
        if opts.tile_w < 1 or opts.tile_h < 1: parser.error("width and height must be greater than 0")
        
    fname = opts.fname
    
    #create all sections
    for k in [fname,"None","app"]:
        if not ini.has_section(k):
            ini.add_section(k)
    
    #set app level defaults
    for k,v in [('screen_w',800),('screen_h',600)]:
        if not ini.has_option('app',k):
            ini.set('app',k,str(v))
            
    #set app level values
    for k in ['screen_w','screen_h']:
        if hasattr(opts,k):
            v = getattr(opts,k)
            if v != None: ini.set('app',k,str(v))
            
    #set default defaults
    for k,v in [('width',256),('height',256),('tile_w',32),('tile_h',32),('palette','palette.tga')]:
        if not ini.has_option('None',k):
            ini.set('None',k,str(v))
    
    #name of keys for normal stuff
    file_ks = ['width','height','tile_w','tile_h','palette']
        
    #set default values
    if opts.defaults:
        for k in file_ks:
            if hasattr(opts,k):
                v = getattr(opts,k)
                if v != None: ini.set('None',k,str(v))
    
    #set fname values
    for k in file_ks:
        if hasattr(opts,k):
            v = getattr(opts,k)
            if v != None: ini.set(fname,k,str(v))
    
    #save the ini
    ini_save()
            
    #convert ini to cfg stuff...
    ini_to_cfg(['app','None',fname])
    if fname == 'None': fname = None
    cfg['fname'] = fname
    
            
def ini_to_cfg(sections):
    global cfg
    ik = ['screen_w','screen_h','tile_w','tile_h','width','height']
    for s in sections:
        for k,v in ini.items(s):
            if k in ik: v = int(v)
            cfg[k] = v
def ini_to_dict(section):
    cfg = {}
    ik = ['screen_w','screen_h','tile_w','tile_h','width','height']
    for s in [section]:
        for k,v in ini.items(s):
            if k in ik: v = int(v)
            cfg[k] = v
    return cfg

def cfg_to_ini(ks,section):
    if not ini.has_section(section): ini.add_section(section)
    for k in ks:
        v = cfg[k]
        ini.set(section,k,str(v))
        
        
def init_gui():
    #themes = cfg['theme'].split(",")
    #gui.theme.load(themes)
    #gui.theme.load(['default','tools'])
    global top
    top = gui.Desktop(theme=gui.Theme([os.path.join('data','themes','default'),os.path.join('data','themes','tools')]))

    #top = gui.Desktop()
    #top.theme.load(['default','tools'])


    #pass
    
def init_app():
    global app
    app = _app()

    #
    colors_height = 64
    ss = 8
        
    #--- top
    x,y,h = 0,0,0
        
    #menus
    e = gui.Menus(menus)
    e.rect.w,e.rect.h = e.resize()
    app.add(e,x,y)
    x,h = x+e.rect.w,max(h,e.rect.h)
    menus_height = e.rect.h
    
    #--- row
    x,y,h = 0,y+h,0
    
    #--- vspace
    y += ss
    
    #--- hspace
    x += ss
    
    #tools
    e = gui.Toolbox(tools,1,0,value='draw') #,"icons48")
    e.rect.w,e.rect.h = e.resize()
    def _set_mode(value):
        cmd_mode(value.value)
    e.connect(gui.CHANGE,_set_mode,e)
    app.add(e,x,y)

    
    #--- vspace
    y += ss


    #--- switchbox for saving.
    sx, sy = x,y+(max(h,e.rect.h))

    savelabel = gui.Label("Save on")
    app.add(savelabel, sx,sy)
    savelabel2 = gui.Label("focus:")
    app.add(savelabel2, sx,sy+(ss*2))

    #--- vspace
    y += (ss *5)
    sy += (ss *5)

    save_activeevent_switch = gui.Switch(False)
    app.add(save_activeevent_switch, sx,sy)
    app.save_activeevent_switch = save_activeevent_switch

    x,h = x+e.rect.w,max(h,e.rect.h)
    toolbox_width = e.rect.w

    #--- hspace
    x += ss
    y -= ss*6 #undo what was done above to the y val
        
    #tdraw
    #tdraw-calcs
    dw = app.screen_w - (toolbox_width+app.tiles.get_width()+ss*4)
    dh = app.screen_h - (menus_height+colors_height+ss*2)
    if dw/float(app.tile_w) > dh/float(app.tile_h): dw = dh/float(app.tile_h)*app.tile_w
    else: dh = dw/float(app.tile_w)*app.tile_h
    e = app.tdraw = tdraw(dw,dh)
    app.add(e,x,y)
    x,h = x+e.rect.w,max(h,e.rect.h)
    
    #--- hspace
    x += ss
    
    #tpicker
    e = app.tpicker = tpicker()
    e.rect.w,e.rect.h = e.resize()
    #--- right
    x = app.screen_w-e.rect.w-ss
    app.add(e,x,y)
    h = max(h,e.rect.h)

    #tpreview
    y = y+e.rect.h
    e = app.tpreview = tpreview()
    e.rect.w,e.rect.h = e.resize()
    app.add(e,x,y)
    
    #--- bottom
    x,y,h = 0,app.screen_h - colors_height,0
    
    #cpreview
    colors_width = toolbox_width + ss * 2
    e = app.cpreview = cpreview(colors_width,colors_height)
    e.rect.w,e.rect.h = e.resize()
    app.add(e,x,y)
    x,h = x+e.rect.w,max(h,e.rect.h)
    
    #cpicker
    if os.path.isfile(cfg['palette']):
        pal = pygame.image.load(cfg['palette'])
    else:
        #default to EGA / NES palette
        
        pw,ph = 16,6
        pdata = [(0, 0, 0, 255), (0, 0, 170, 255), (0, 170, 0, 255), (0, 170, 170, 255), (170, 0, 0, 255), (170, 0, 170, 255), (170, 85, 0, 255), (170, 170, 170, 255), (85, 85, 85, 255), (85, 85, 255, 255), (85, 255, 85, 255), (85, 255, 255, 255), (255, 85, 85, 255), (255, 85, 255, 255), (255, 255, 85, 255), (255, 255, 255, 255), (0, 0, 0, 255), (0, 0, 0, 255), (0, 0, 0, 255), (0, 0, 0, 255), (0, 0, 0, 255), (0, 0, 0, 255), (0, 0, 0, 255), (0, 0, 0, 255), (0, 0, 0, 255), (0, 0, 0, 255), (0, 0, 0, 255), (0, 0, 0, 255), (0, 0, 0, 255), (0, 0, 0, 255), (0, 0, 0, 255), (0, 0, 0, 255), (255, 255, 255, 255), (173, 243, 255, 255), (223, 214, 255, 255), (255, 190, 255, 255), (255, 176, 255, 255), (255, 177, 237, 255), (255, 191, 185, 255), (255, 217, 145, 255), (237, 246, 128, 255), (185, 255, 138, 255), (145, 255, 173, 255), (128, 255, 223, 255), (138, 255, 255, 255), (197, 197, 197, 255), (0, 0, 0, 255), (0, 0, 0, 255), (255, 255, 255, 255), (129, 200, 255, 255), (179, 171, 255, 255), (231, 146, 255, 255), (255, 132, 244, 255), (255, 133, 194, 255), (255, 148, 141, 255), (244, 173, 101, 255), (194, 202, 84, 255), (141, 227, 94, 255), (101, 240, 129, 255), (84, 240, 179, 255), (94, 225, 231, 255), (120, 120, 120, 255), (0, 0, 0, 255), (0, 0, 0, 255), (192, 192, 192, 255), (57, 128, 200, 255), (108, 99, 217, 255), (160, 74, 207, 255), (200, 61, 172, 255), (217, 61, 122, 255), (207, 76, 70, 255), (172, 102, 30, 255), (122, 130, 13, 255), (70, 155, 23, 255), (30, 169, 57, 255), (13, 168, 108, 255), (23, 153, 160, 255), (61, 61, 61, 255), (0, 0, 0, 255), (0, 0, 0, 255), (128, 128, 128, 255), (16, 87, 159, 255), (67, 58, 176, 255), (119, 34, 166, 255), (159, 20, 131, 255), (176, 20, 81, 255), (166, 35, 29, 255), (131, 61, 0, 255), (81, 89, 0, 255), (29, 114, 0, 255), (0, 128, 16, 255), (0, 127, 67, 255), (0, 112, 119, 255), (0, 0, 0, 255), (0, 0, 0, 255), (0, 0, 0, 255)]

        pal = pygame.Surface((pw,ph),SWSURFACE,32)
        n=0
        for py in range(0,ph):
            for px in range(0,pw):
                pal.set_at((px,py),pdata[n])
                n+=1
        
        
    e = app.cpicker = cpicker(app.screen_w-colors_width,colors_height,pal)
    e.rect.w,e.rect.h = e.resize()
    app.add(e,x,y)
    x,h = x+e.rect.w,max(h,e.rect.h)
    
    pygame.key.set_repeat(500,30)
    
    app.screen.fill((255,255,255,255))
    
class NewDialog(gui.Dialog):
    def __init__(self,**params):
        title = gui.Label("New...")
        
        doc = html.HTML(globals={'gui':gui,'dialog':self},data="""<form id='form'><table>
        <tr>
        <td align=center>Image Size
        <td align=center>Tile Size

        <tr><td colspan='1' align='center' style='padding-right:8px;'><table>
        <tr><td align=right>Width: <td><input type='text' size='4' value='%(width)s' name='width'>
        <tr><td align=right>Height: <td><input type='text' size='4' value='%(height)s' name='height'>
        </table>
        
        <td colspan='1' align='center'><table>
        <tr><td align=right>Width: <td><input type='text' size='4' value='%(tile_w)s' name='tile_w'>
        <tr><td align=right>Height: <td><input type='text' size='4' value='%(tile_h)s' name='tile_h'>
        </table>
        
        <tr><td colspan=2>Palette: <input type='text' size=20 name='palette' value='%(palette)s'>
        
        <tr><td>&nbsp;
        
        <tr><td colspan=2><input type='button' value='Okay' onclick='dialog.send(gui.CHANGE)'> <input type='button' value='Cancel' onclick='dialog.close()'>
        
        </table>"""%ini_to_dict('None'))
        gui.Dialog.__init__(self,title,doc)
        
        self.value = doc['form']

class SaveAsDialog(gui.Dialog):
    def __init__(self,**params):
        title = gui.Label("Save As...")
        
        doc = html.HTML(globals={'gui':gui,'dialog':self},data="""
        <form id='form'>
        
        <table>
        
        <tr><td colspan=2>File Name: <input type='file' size=20 name='fname' value=''>
        
        <tr><td>&nbsp;
        
        <tr><td colspan=2><input type='button' value='Okay' onclick='dialog.send(gui.CHANGE)'> <input type='button' value='Cancel' onclick='dialog.close()'>
        
        </table>""")
        gui.Dialog.__init__(self,title,doc)
        
        self.value = doc['form']

class OpenDialog(gui.Dialog):
    def __init__(self,**params):
        title = gui.Label("Open...")
        
        def load_vals(fname,form):
            if not ini.has_section(fname): return
            
            for k,v in ini.items(fname):
                if k in form:
                    form[k].value = v
        doc = html.HTML(globals={'load_vals':load_vals,'ini':ini,'gui':gui,'dialog':self},data="""<form id='form'><table>
        
        <tr><td align=right>File Name:&nbsp;<td  align=left><input type='file' size=20 name='fname' value='' onchange='load_vals(self.value,form)'>
        <tr><td align=right>Tile Width:&nbsp;<td align=left><input type='text' size='4' value='%(tile_w)s' name='tile_w'>
        <tr><td align=right>Tile Height:&nbsp;<td align=left><input type='text' size='4' value='%(tile_h)s' name='tile_h'>
        
        <tr><td align=right>Palette:&nbsp;<td align=left><input type='text' size=20 name='palette' value='%(palette)s'>
        
        <tr><td>&nbsp;
        
        <tr><td colspan=2><input type='button' value='Okay' onclick='dialog.send(gui.CHANGE)'> <input type='button' value='Cancel' onclick='dialog.close()'>
        
        </table>"""%ini_to_dict('None'))
        gui.Dialog.__init__(self,title,doc)
        
        self.value = doc['form']

class ErrorDialog(gui.Dialog):
    def __init__(self,tt,data,**params):
        title = gui.Label("Error: "+tt)
        data = str(data)
        
        doc = html.HTML(globals={'gui':gui,'dialog':self},data="""
        <form id='form'>
        
        <table>
        <tr><td><h1>&lt;!&gt;&nbsp;</h1>
        <td>"""+data+"""
        <tr><td>&nbsp;
        <tr><td colspan=2><input type='button' value='Okay' onclick='dialog.send(gui.CHANGE);dialog.close()'>
        </table>""")
        gui.Dialog.__init__(self,title,doc)
        
        self.value = doc['form']

class DirtyDialog(gui.Dialog):
    def __init__(self,**params):
        title = gui.Label("File not yet saved...")
        data = "Your file is not yet saved.<br>Are you sure you want to continue?"
        
        doc = html.HTML(globals={'gui':gui,'dialog':self},data="""
        <form id='form'>
        
        <table>
        <tr><td><h1>&lt;!&gt;&nbsp;</h1>
        <td>"""+data+"""
        <tr><td>&nbsp;
        <tr><td colspan=2><input type='button' value='Okay' onclick='dialog.send(gui.CHANGE)'> <input type='button' value='Cancel' onclick='dialog.close()'>
        </table>""")
        gui.Dialog.__init__(self,title,doc)
        
        self.value = doc['form']



def run():
    #top.connect(gui.QUIT,top.quit,None)
    top.connect(gui.QUIT,cmd_quit,None)
    top.connect(pygame.ACTIVEEVENT, cmd_active_save,None)
    top.init(app,app.screen)
    app.top = top
    top.run()
    

def main():
    init_ini()
    init_opts()
    init_gui()
    
    restart = 1
    while restart:
        restart = 0
        try:
            init_app()
            run()
        except Restart: restart = 1
        

        
    
main()
# vim: set filetype=python sts=4 sw=4 noet si :
