#!/usr/bin/python2.4
"""<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 sys

sys.path.insert(0, "..")
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()
                print(mode)

                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 == 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 == MOUSEBUTTONDOWN) or (
            e.type == 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 == MOUSEBUTTONDOWN and e.button == 1) or (
            e.type == 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 == MOUSEBUTTONDOWN and e.button == 3) or (
            e.type == 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 == MOUSEBUTTONDOWN and e.button == 3) or (
            e.type == MOUSEMOTION
            and e.buttons[2] == 1
            and self.container.myfocus == self
        ):
            self.picker_down(e)
        if e.type == MOUSEBUTTONDOWN and e.button == 1:
            a = "%s_down" % app.mode
            if hasattr(self, a):
                getattr(self, a)(e)
        if e.type == 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 == 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.

        temp_file_name = "tmp_" + app.fname
        print(temp_file_name)
        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 as 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 as 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 as 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(["default", "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 :
