class CompassOverlay(Drawable):
    _tex = None
    _yawPitch = (0., 0.)

    def __init__(self, small=False):
        super(CompassOverlay, self).__init__()
        self.small = small

    @property
    def yawPitch(self):
        return self._yawPitch

    @yawPitch.setter
    def yawPitch(self, value):
        self._yawPitch = value
        self.invalidate()

    def drawSelf(self):
        if self._tex is None:
            if self.small:
                filename = "compass_small.png"
            else:
                filename = "compass.png"

            self._tex = loadPNGTexture("toolicons/" + filename)#, minFilter=GL.GL_LINEAR, magFilter=GL.GL_LINEAR)

        self._tex.bind()
        size = 0.075

        with gl.glPushMatrix(GL.GL_MODELVIEW):
            GL.glLoadIdentity()

            yaw, pitch = self.yawPitch
            GL.glTranslatef(1.-size, size, 0.0)  # position on upper right corner
            GL.glRotatef(180-yaw, 0., 0., 1.)  # adjust to north
            GL.glColor3f(1., 1., 1.)

            with gl.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY):
                GL.glVertexPointer(2, GL.GL_FLOAT, 0, makeQuad(-size, -size, 2*size, 2*size))
                GL.glTexCoordPointer(2, GL.GL_FLOAT, 0, makeQuad(0, 0, 256, 256))

                with gl.glEnable(GL.GL_BLEND, GL.GL_TEXTURE_2D):
                    GL.glDrawArrays(GL.GL_QUADS, 0, 4) class ThumbView(GLPerspective):

    def __init__(self, sch, **kw):
        GLPerspective.__init__(self, **kw)  # self, xmin= -32, xmax=32, ymin= -32, ymax=32, near= -1000, far=1000)
        self.far = 16000
        self.schematic = sch
        self.renderer = PreviewRenderer(sch)
        self.fboSize = (128, 128)
        # self.renderer.position = (sch.Length / 2, 0, sch.Height / 2)

    def setup_modelview(self):
        GLU.gluLookAt(-self.schematic.Width * 2.8, self.schematic.Height * 2.5 + 1, -self.schematic.Length * 1.5,
                      self.schematic.Width, 0, self.schematic.Length,
                      0, 1, 0)
    fbo = None

    def gl_draw_tex(self):
        self.clear()
        self.renderer.draw()

    def clear(self):
        if self.drawBackground:
            GL.glClearColor(0.25, 0.27, 0.77, 1.0)
        else:
            GL.glClearColor(0.0, 0.0, 0.0, 0.0)
        GL.glClear(GL.GL_DEPTH_BUFFER_BIT | GL.GL_COLOR_BUFFER_BIT)

    def gl_draw(self):
        if self.schematic.chunkCount > len(self.renderer.chunkRenderers):
            self.gl_draw_thumb()
        else:
            if self.fbo is None:
                w, h = self.fboSize
                self.fbo = FramebufferTexture(w, h, self.gl_draw_tex)
            GL.glMatrixMode(GL.GL_PROJECTION)
            GL.glLoadIdentity()
            GL.glMatrixMode(GL.GL_MODELVIEW)
            GL.glLoadIdentity()
            GL.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY)
            GL.glColor(1.0, 1.0, 1.0, 1.0)
            GL.glVertexPointer(2, GL.GL_FLOAT, 0, array([-1, -1,
                         - 1, 1,
                         1, 1,
                         1, -1, ], dtype='float32'))
            GL.glTexCoordPointer(2, GL.GL_FLOAT, 0, array([0, 0, 0, 256, 256, 256, 256, 0], dtype='float32'))
            e = (GL.GL_TEXTURE_2D,)
            if not self.drawBackground:
                e += (GL.GL_ALPHA_TEST,)
            with gl.glEnable(*e):
                self.fbo.bind()
                GL.glDrawArrays(GL.GL_QUADS, 0, 4)
            GL.glDisableClientState(GL.GL_TEXTURE_COORD_ARRAY)

    drawBackground = True

    def gl_draw_thumb(self):
        GL.glPushAttrib(GL.GL_SCISSOR_BIT)
        r = self.rect
        r = r.move(*self.local_to_global_offset())
        GL.glScissor(r.x, self.get_root().height - r.y - r.height, r.width, r.height)
        with gl.glEnable(GL.GL_SCISSOR_TEST):
            self.clear()
            self.renderer.draw()
        GL.glPopAttrib() MUSIC_END_EVENT = USEREVENT + 1
class Cancel(Exception):
class RootWidget(Widget):
def set_modifier(key, value):
def add_modifiers(event):
def get_root():
def get_top_widget():
def get_focus():
    def __init__(self, surface):
    def set_timer(self, ms):
    def run(self):
    def capture_mouse(self, widget):
    def run_modal(self, modal_widget):
    def call_idle_handlers(self, event):
        def call(ref):
    def add_idle_handler(self, widget):
    def remove_idle_handler(self, widget):
    def send_key(self, widget, name, event):
    def begin_frame(self):
    def get_root(self):
    def show_tooltip(self, widget, pos):
        def TextTooltip(text):
        def showTip(tip):
    def update_tooltip(self, pos=None):
    def has_focus(self):
    def quit(self):
    def confirm_quit(self):
    def get_mouse_for(self, widget):
    def gl_clear(self):
    def music_end(self):
def make_scheduled_calls():
def schedule(delay, func): class Label(Widget):

    text = overridable_property('text')
    align = overridable_property('align')

    hover_color = ThemeProperty('hover_color')
    highlight_color = ThemeProperty('highlight_color')
    disabled_color = ThemeProperty('disabled_color')
    highlight_bg_color = ThemeProperty('highlight_bg_color')
    hover_bg_color = ThemeProperty('hover_bg_color')
    enabled_bg_color = ThemeProperty('enabled_bg_color')
    disabled_bg_color = ThemeProperty('disabled_bg_color')

    enabled = True
    highlighted = False
    _align = 'l'

    def __init__(self, text, width=None, **kwds):
        Widget.__init__(self, **kwds)
        font = self.font
        lines = text.split("\n")
        tw, th = 0, 0
        for line in lines:
            w, h = font.size(line)
            tw = max(tw, w)
            th += h
        if width is not None:
            tw = width
        else:
            tw = max(1, tw)
        d = 2 * self.margin
        self.size = (tw + d, th + d)
        self._text = text

    def __repr__(self):
        return "Label {0}, child of {1}".format(self.text, self.parent)

    def get_text(self):
        return self._text

    def set_text(self, x):
        self._text = x

    def get_align(self):
        return self._align

    def set_align(self, x):
        self._align = x

    def draw(self, surface):
        if not self.enabled:
            fg = self.disabled_color
            bg = self.disabled_bg_color
        else:
            fg = self.fg_color
            bg = self.enabled_bg_color
            if self.is_default:
                fg = self.default_choice_color or fg
                bg = self.default_choice_bg_color or bg
            if self.is_hover:
                fg = self.hover_color or fg
                bg = self.hover_bg_color or bg
            if self.highlighted:
                fg = self.highlight_color or fg
                bg = self.highlight_bg_color or bg

        self.draw_with(surface, fg, bg)

    is_default = False

    def draw_with(self, surface, fg, bg=None):
        if bg:
            r = surface.get_rect()
            b = self.border_width
            if b:
                e = -2 * b
                r.inflate_ip(e, e)
            surface.fill(bg, r)
        m = self.margin
        align = self.align
        width = surface.get_width()
        y = m
        lines = self.text.split("\n")
        font = self.font
        dy = font.get_linesize()
        for line in lines:
            if len(line):
                size = font.size(line)
                if size[0] == 0:
                    continue

                image = font.render(line, True, fg)
                r = image.get_rect()
                r.top = y
                if align == 'l':
                    r.left = m
                elif align == 'r':
                    r.right = width - m
                else:
                    r.centerx = width // 2
                surface.blit(image, r)
            y += dy class SmallValueDisplay(ValueDisplay):
    pass class ValueDisplay(Control, Label):

    format = "%s"
    align = 'l'

    def __init__(self, width=100, **kwds):
        Label.__init__(self, "", **kwds)
        self.set_size_for_text(width)

#    def draw(self, surf):
#        value = self.value
#        text = self.format_value(value)
#        buf = self.font.render(text, True, self.fg_color)
#        frame = surf.get_rect()
#        blit_in_rect(surf, buf, frame, self.align, self.margin)

    def get_text(self):
        return self.format_value(self.value)

    def format_value(self, value):
        if value is not None:
            return self.format % value
        else:
            return "" class Dialog(Modal, Widget):

    click_outside_response = None

    def __init__(self, client=None, responses=None,
            default=0, cancel=-1, **kwds):
        Widget.__init__(self, **kwds)
        if client or responses:
            rows = []
            w1 = 0
            w2 = 0
            if client:
                rows.append(client)
                w1 = client.width
            if responses:
                buttons = Row([
                    Button(text, action=lambda t=text: self.dismiss(t))
                        for text in responses])
                rows.append(buttons)
                w2 = buttons.width
            if w1 < w2:
                a = 'l'
            else:
                a = 'r'
            contents = Column(rows, align=a)
            m = self.margin
            contents.topleft = (m, m)
            self.add(contents)
            self.shrink_wrap()
        if responses and default is not None:
            self.enter_response = responses[default]
        if responses and cancel is not None:
            self.cancel_response = responses[cancel]

    def mouse_down(self, e):
        if not e in self:
            response = self.click_outside_response
            if response is not None:
                self.dismiss(response) class QuickDialog(Dialog):
    """ Dialog that closes as soon as you click outside or press a key"""
    def mouse_down(self, evt):
        if evt not in self:
            self.dismiss(-1)
            if evt.button != 1:
                event.post(evt)

    def key_down(self, evt):
        self.dismiss()
        event.post(evt) def wrapped_label(text, wrap_width, **kwds):
    paras = text.split("\n")
    text = "\n".join([textwrap.fill(para, wrap_width) for para in paras])
    return Label(text, **kwds) class GLOrtho(GLViewport):

    def __init__(self, rect=None,
            xmin=-1, xmax=1, ymin=-1, ymax=1,
            near=-1, far=1, **kwds):
        GLViewport.__init__(self, rect, **kwds)
        self.xmin = xmin
        self.xmax = xmax
        self.ymin = ymin
        self.ymax = ymax
        self.near = near
        self.far = far

    def setup_projection(self):
        GL.glOrtho(self.xmin, self.xmax, self.ymin, self.ymax,
            self.near, self.far) class GLViewport(Widget):

    is_gl_container = True

    def gl_draw_self(self, root, offset):
        rect = self.rect.move(offset)
        # GL_CLIENT_ALL_ATTRIB_BITS is borked: defined as -1 but
        # glPushClientAttrib insists on an unsigned long.
        #GL.glPushClientAttrib(0xffffffff)
        #GL.glPushAttrib(GL.GL_ALL_ATTRIB_BITS)
        GL.glViewport(rect.left, root.height - rect.bottom, rect.width, rect.height)
        self.gl_draw_viewport()
        #GL.glPopAttrib()
        #GL.glPopClientAttrib()

    def gl_draw_viewport(self):
        self.setup_matrices()
        self.gl_draw()

    def setup_matrices(self):
        GL.glMatrixMode(GL.GL_PROJECTION)
        GL.glLoadIdentity()
        self.setup_projection()
        GL.glMatrixMode(GL.GL_MODELVIEW)
        GL.glLoadIdentity()
        self.setup_modelview()

    def setup_projection(self):
        pass

    def setup_modelview(self):
        pass

    def gl_draw(self):
        pass

    def augment_mouse_event(self, event):
        Widget.augment_mouse_event(self, event)
        w, h = self.size
        viewport = numpy.array((0, 0, w, h), dtype='int32')
        self.setup_matrices()
        gf = GL.glGetDoublev
        pr_mat = gf(GL.GL_PROJECTION_MATRIX)
        mv_mat = gf(GL.GL_MODELVIEW_MATRIX)
        x, y = event.local
        y = h - y
        up = GLU.gluUnProject
        try:
            p0 = up(x, y, 0.0, mv_mat, pr_mat, viewport)
            p1 = up(x, y, 1.0, mv_mat, pr_mat, viewport)
            event.dict['ray'] = (p0, p1)
        except ValueError:  # projection failed!
            pass class Operation(object):
    changedLevel = True
    undoLevel = None

    def __init__(self, editor, level):
        self.editor = editor
        self.level = level

    def extractUndo(self, level, box):
        if isinstance(level, pymclevel.MCInfdevOldLevel):
            return self.extractUndoChunks(level, box.chunkPositions, box.chunkCount)
        else:
            return self.extractUndoSchematic(level, box)

    def extractUndoChunks(self, level, chunks, chunkCount = None):
        if not isinstance(level, pymclevel.MCInfdevOldLevel):
            chunks = numpy.array(list(chunks))
            mincx, mincz = numpy.min(chunks, 0)
            maxcx, maxcz = numpy.max(chunks, 0)
            box = BoundingBox((mincx << 4, 0, mincz << 4), (maxcx << 4, level.Height, maxcz << 4))

            return self.extractUndoSchematic(level, box)

        undoLevel = pymclevel.MCInfdevOldLevel(mkundotemp(), create=True)
        if not chunkCount:
            try:
                chunkCount = len(chunks)
            except TypeError:
                chunkCount = -1

        def _extractUndo():
            yield 0, 0, "Recording undo..."
            for i, (cx, cz) in enumerate(chunks):
                undoLevel.copyChunkFrom(level, cx, cz)
                yield i, chunkCount, "Copying chunk %s..." % ((cx, cz),)
            undoLevel.saveInPlace()

        if chunkCount > 25 or chunkCount < 1:
            if "Canceled" == showProgress("Recording undo...", _extractUndo(), cancel=True):
                if albow.ask("Continue with undo disabled?", ["Continue", "Cancel"]) == "Cancel":
                    raise Cancel
                else:
                    return None
        else:
            exhaust(_extractUndo())

        return undoLevel

    def extractUndoSchematic(self, level, box):
        if box.volume > 131072:
            sch = showProgress("Recording undo...", level.extractZipSchematicIter(box), cancel=True)
        else:
            sch = level.extractZipSchematic(box)
        if sch == "Cancel":
            raise Cancel
        if sch:
            sch.sourcePoint = box.origin

        return sch


    # represents a single undoable operation
    def perform(self, recordUndo=True):
        " Perform the operation. Record undo information if recordUndo"

    def undo(self):
        """ Undo the operation. Ought to leave the Operation in a state where it can be performed again.
            Default implementation copies all chunks in undoLevel back into level. Non-chunk-based operations
            should override this."""

        if self.undoLevel:

            def _undo():
                yield 0, 0, "Undoing..."
                if hasattr(self.level, 'copyChunkFrom'):
                    for i, (cx, cz) in enumerate(self.undoLevel.allChunks):
                        self.level.copyChunkFrom(self.undoLevel, cx, cz)
                        yield i, self.undoLevel.chunkCount, "Copying chunk %s..." % ((cx, cz),)
                else:
                    for i in self.level.copyBlocksFromIter(self.undoLevel, self.undoLevel.bounds, self.undoLevel.sourcePoint, biomes=True):
                        yield i, self.undoLevel.chunkCount, "Copying..."

            if self.undoLevel.chunkCount > 25:
                showProgress("Undoing...", _undo())
            else:
                exhaust(_undo())

            self.editor.invalidateChunks(self.undoLevel.allChunks)


    def dirtyBox(self):
        """ The region modified by the operation.
        Return None to indicate no blocks were changed.
        """
        return None def GeneratorPanel():
    panel = Widget()
    panel.chunkHeight = 64
    panel.grass = True
    panel.simulate = False

    jarStorage = MCServerChunkGenerator.getDefaultJarStorage()
    if jarStorage:
        jarStorage.reloadVersions()

    generatorChoice = ChoiceButton(["Minecraft Server", "Flatland"])
    panel.generatorChoice = generatorChoice
    col = [Row((Label("Generator:"), generatorChoice))]
    noVersionsRow = Label("Will automatically download and use the latest version")
    versionContainer = Widget()

    heightinput = IntInputRow("Height: ", ref=AttrRef(panel, "chunkHeight"), min=0, max=128)
    grassinput = CheckBoxLabel("Grass", ref=AttrRef(panel, "grass"))

    flatPanel = Column([heightinput, grassinput], align="l")

    def generatorChoiceChanged():
        serverPanel.visible = generatorChoice.selectedChoice == "Minecraft Server"
        flatPanel.visible = not serverPanel.visible

    generatorChoice.choose = generatorChoiceChanged

    versionChoice = None

    if len(jarStorage.versions):
        def checkForUpdates():
            def _check():
                yield
                jarStorage.downloadCurrentServer()
                yield

            showProgress("Checking for server updates...", _check())
            versionChoice.choices = sorted(jarStorage.versions, reverse=True)
            versionChoice.choiceIndex = 0

        versionChoice = ChoiceButton(sorted(jarStorage.versions, reverse=True))
        versionChoiceRow = (Row((
            Label("Server version:"),
            versionChoice,
            Label("or"),
            Button("Check for Updates", action=checkForUpdates))))
        panel.versionChoice = versionChoice
        versionContainer.add(versionChoiceRow)
    else:
        versionContainer.add(noVersionsRow)

    versionContainer.shrink_wrap()

    menu = Menu("Advanced", [
        ("Open Server Storage", "revealStorage"),
        ("Reveal World Cache", "revealCache"),
        ("Delete World Cache", "clearCache")
        ])

    def presentMenu():
        i = menu.present(advancedButton.parent, advancedButton.topleft)
        if i != -1:
            (revealStorage, revealCache, clearCache)[i]()

    advancedButton = Button("Advanced...", presentMenu)

    @alertException
    def revealStorage():
        mcplatform.platform_open(jarStorage.cacheDir)

    @alertException
    def revealCache():
        mcplatform.platform_open(MCServerChunkGenerator.worldCacheDir)

    #revealCacheRow = Row((Label("Minecraft Server Storage: "), Button("Open Folder", action=revealCache, tooltipText="Click me to install your own minecraft_server.jar if you have any.")))

    @alertException
    def clearCache():
        MCServerChunkGenerator.clearWorldCache()

    simRow = CheckBoxLabel("Simulate world", ref=AttrRef(panel, "simulate"), tooltipText="Simulate the world for a few seconds after generating it. Reduces the save file size by processing all of the TileTicks.")

    simRow = Row((simRow, advancedButton), anchor="lrh")
    #deleteCacheRow = Row((Label("Delete Temporary World File Cache?"), Button("Delete Cache!", action=clearCache, tooltipText="Click me if you think your chunks are stale.")))

    serverPanel = Column([versionContainer, simRow, ], align="l")

    col.append(serverPanel)
    col = Column(col, align="l")
    col.add(flatPanel)
    flatPanel.topleft = serverPanel.topleft
    flatPanel.visible = False
    panel.add(col)

    panel.shrink_wrap()

    def generate(level, arg):
        useServer = generatorChoice.selectedChoice == "Minecraft Server"

        if useServer:
            def _createChunks():
                if versionChoice:
                    version = versionChoice.selectedChoice
                else:
                    version = None
                gen = MCServerChunkGenerator(version=version)


                if isinstance(arg, pymclevel.BoundingBox):
                    for i in gen.createLevelIter(level, arg, simulate=panel.simulate):
                        yield i
                else:
                    for i in gen.generateChunksInLevelIter(level, arg, simulate=panel.simulate):
                        yield i

        else:
            def _createChunks():
                height = panel.chunkHeight
                grass = panel.grass and pymclevel.alphaMaterials.Grass.ID or pymclevel.alphaMaterials.Dirt.ID
                if isinstance(arg, pymclevel.BoundingBox):
                    chunks = list(arg.chunkPositions)
                else:
                    chunks = arg

                if level.dimNo in (-1, 1):
                    maxskylight = 0
                else:
                    maxskylight = 15

                for i, (cx, cz) in enumerate(chunks):

                    yield i, len(chunks)
                    #surface = blockInput.blockInfo

                    #for cx, cz in :
                    try:
                        level.createChunk(cx, cz)
                    except ValueError, e:  # chunk already present
                        print e
                        continue
                    else:
                        ch = level.getChunk(cx, cz)
                        if height > 0:
                            stoneHeight = max(0, height - 5)
                            grassHeight = max(0, height - 1)

                            ch.Blocks[:, :, grassHeight] = grass
                            ch.Blocks[:, :, stoneHeight:grassHeight] = pymclevel.alphaMaterials.Dirt.ID
                            ch.Blocks[:, :, :stoneHeight] = pymclevel.alphaMaterials.Stone.ID

                            ch.Blocks[:, :, 0] = pymclevel.alphaMaterials.Bedrock.ID
                            ch.SkyLight[:, :, height:] = maxskylight
                            if maxskylight:
                                ch.HeightMap[:] = height

                        else:
                            ch.SkyLight[:] = maxskylight

                        ch.needsLighting = False
                        ch.dirty = True

        return _createChunks()

    panel.generate = generate
    return panel class GLBackground(GLOrtho):
    margin = 8
    bg_color = (0.0, 0.0, 0.0, 0.6)

    #bg_color = (30/255.0,0,255/255.0, 100/255.0)
    def gl_draw(self):
        #if hasattr(self, 'highlight_bg_color') and self in self.get_root().find_widget(mouse.get_pos()).all_parents():
        #    color = self.highlight_bg_color
        #else:
        color = tuple(self.bg_color) + (1.0,)

        glEnable(GL_BLEND)
        glColor(color[0], color[1], color[2], color[3])
        glVertexPointer(2, GL_FLOAT, 0, array([-1, -1, -1, 1, 1, 1, 1, -1], dtype='float32'))
        glDrawArrays(GL_QUADS, 0, 4)
        glDisable(GL_BLEND) class Panel(GLBackground):
    pass class gl(object):
    @classmethod
    def ResetGL(cls):
        DisplayList.invalidateAllLists()

    @classmethod
    @contextmanager
    def glPushMatrix(cls, matrixmode):
        try:
            GL.glMatrixMode(matrixmode)
            GL.glPushMatrix()
            yield
        finally:
            GL.glMatrixMode(matrixmode)
            GL.glPopMatrix()

    @classmethod
    @contextmanager
    def glPushAttrib(cls, attribs):
        try:
            GL.glPushAttrib(attribs)
            yield
        finally:
            GL.glPopAttrib()

    @classmethod
    @contextmanager
    def glBegin(cls, type):
        try:
            GL.glBegin(type)
            yield
        finally:
            GL.glEnd()

    @classmethod
    @contextmanager
    def glEnable(cls, *enables):
        try:
            GL.glPushAttrib(GL.GL_ENABLE_BIT)
            for e in enables:
                GL.glEnable(e)

            yield
        finally:
            GL.glPopAttrib()

    @classmethod
    @contextmanager
    def glEnableClientState(cls, *enables):
        try:
            GL.glPushClientAttrib(GL.GL_CLIENT_ALL_ATTRIB_BITS)
            for e in enables:
                GL.glEnableClientState(e)

            yield
        finally:
            GL.glPopClientAttrib()

    listCount = 0

    @classmethod
    def glGenLists(cls, n):
        cls.listCount += n
        return GL.glGenLists(n)

    @classmethod
    def glDeleteLists(cls, base, n):
        cls.listCount -= n
        return GL.glDeleteLists(base, n) class Texture(object):
    allTextures = []
    defaultFilter = GL.GL_NEAREST

    def __init__(self, textureFunc=None, minFilter=None, magFilter=None):
        minFilter = minFilter or self.defaultFilter
        magFilter = magFilter or self.defaultFilter
        if textureFunc is None:
            textureFunc = lambda: None

        self.textureFunc = textureFunc
        self._texID = GL.glGenTextures(1)

        def _delete(r):
            Texture.allTextures.remove(r)
        self.allTextures.append(weakref.ref(self, _delete))
        self.bind()
        GL.glTexParameter(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, minFilter)
        GL.glTexParameter(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, magFilter)

        self.textureFunc()

    def __del__(self):
        self.delete()

    def delete(self):
        if self._texID is not None:
            GL.glDeleteTextures(self._texID)

    def bind(self):
        GL.glBindTexture(GL.GL_TEXTURE_2D, self._texID)

    def invalidate(self):
        self.dirty = True def askSaveFile(initialDir, title, defaultName, filetype, suffix):
    if sys.platform == "win32":
        try:
            (filename, customfilter, flags) = win32gui.GetSaveFileNameW(
                hwndOwner=display.get_wm_info()['window'],
                InitialDir=initialDir,
                Flags=win32con.OFN_EXPLORER | win32con.OFN_NOCHANGEDIR | win32con.OFN_OVERWRITEPROMPT,
                File=defaultName,
                DefExt=suffix,
                Title=title,
                Filter=filetype,
                )
        except Exception, e:
            print "Error getting file name: ", e
            return

        try:
            filename = filename[:filename.index('\0')]
            filename = filename.decode(sys.getfilesystemencoding())
        except:
            pass

    elif sys.platform == "darwin" and AppKit is not None:
        sp = AppKit.NSSavePanel.savePanel()
        sp.setDirectory_(initialDir)
        sp.setAllowedFileTypes_([suffix])
        # sp.setFilename_(self.editor.level.displayName)

        if sp.runModal() == 0:
            return  # pressed cancel

        filename = sp.filename()
        AppKit.NSApp.mainWindow().makeKeyWindow()

    else:
        filename = request_new_filename(prompt=title,
                                        suffix=("." + suffix) if suffix else "",
                                        directory=initialDir,
                                        filename=defaultName,
                                        pathname=None)

    return filename class MCRenderer(object):
    isPreviewer = False

    def __init__(self, level=None, alpha=1.0):
        self.render = True
        self.origin = (0, 0, 0)
        self.rotation = 0

        self.bufferUsage = 0

        self.invalidChunkQueue = deque()
        self._chunkWorker = None
        self.chunkRenderers = {}
        self.loadableChunkMarkers = DisplayList()
        self.visibleLayers = set(Layer.AllLayers)

        self.masterLists = None

        alpha = alpha * 255
        self.alpha = (int(alpha) & 0xff)

        self.chunkStartTime = datetime.now()
        self.oldChunkStartTime = self.chunkStartTime

        self.oldPosition = None

        self.chunkSamples = [timedelta(0, 0, 0)] * 15

        self.chunkIterator = None

        import leveleditor
        Settings = leveleditor.Settings

        Settings.fastLeaves.addObserver(self)

        Settings.roughGraphics.addObserver(self)
        Settings.showHiddenOres.addObserver(self)
        Settings.vertexBufferLimit.addObserver(self)

        Settings.drawEntities.addObserver(self)
        Settings.drawTileEntities.addObserver(self)
        Settings.drawTileTicks.addObserver(self)
        Settings.drawUnpopulatedChunks.addObserver(self, "drawTerrainPopulated")
        Settings.drawMonsters.addObserver(self)
        Settings.drawItems.addObserver(self)

        Settings.showChunkRedraw.addObserver(self, "showRedraw")
        Settings.spaceHeight.addObserver(self)
        Settings.targetFPS.addObserver(self, "targetFPS")

        self.level = level

    chunkClass = ChunkRenderer
    calculatorClass = ChunkCalculator

    minViewDistance = 2
    maxViewDistance = 24

    _viewDistance = 8

    needsRedraw = True

    def toggleLayer(self, val, layer):
        if val:
            self.visibleLayers.add(layer)
        else:
            self.visibleLayers.discard(layer)
        for cr in self.chunkRenderers.itervalues():
            cr.invalidLayers.add(layer)

        self.loadNearbyChunks()

    def layerProperty(layer, default=True):  # @NoSelf
        attr = intern("_draw" + layer)

        def _get(self):
            return getattr(self, attr, default)

        def _set(self, val):
            if val != _get(self):
                setattr(self, attr, val)
                self.toggleLayer(val, layer)

        return property(_get, _set)

    drawEntities = layerProperty(Layer.Entities)
    drawTileEntities = layerProperty(Layer.TileEntities)
    drawTileTicks = layerProperty(Layer.TileTicks)
    drawMonsters = layerProperty(Layer.Monsters)
    drawItems = layerProperty(Layer.Items)
    drawTerrainPopulated = layerProperty(Layer.TerrainPopulated)

    def inSpace(self):
        if self.level is None:
            return True
        h = self.position[1]
        return ((h > self.level.Height + self.spaceHeight) or
                (h <= -self.spaceHeight))

    def chunkDistance(self, cpos):
        camx, camy, camz = self.position

        # if the renderer is offset into the world somewhere, adjust for that
        ox, oy, oz = self.origin
        camx -= ox
        camz -= oz

        camcx = int(numpy.floor(camx)) >> 4
        camcz = int(numpy.floor(camz)) >> 4

        cx, cz = cpos

        return max(abs(cx - camcx), abs(cz - camcz))

    overheadMode = False

    def detailLevelForChunk(self, cpos):
        if self.overheadMode:
            return 2
        if self.isPreviewer:
            w, l, h = self.level.bounds.size
            if w + l < 256:
                return 0

        distance = self.chunkDistance(cpos) - self.viewDistance
        if distance > 0 or self.inSpace():
            return 1
        return 0

    def getViewDistance(self):
        return self._viewDistance

    def setViewDistance(self, vd):
        vd = int(vd) & 0xfffe
        vd = min(max(vd, self.minViewDistance), self.maxViewDistance)
        if vd != self._viewDistance:
            self._viewDistance = vd
            self.viewDistanceChanged()
            # self.invalidateChunkMarkers()

    viewDistance = property(getViewDistance, setViewDistance, None, "View Distance")

    @property
    def effectiveViewDistance(self):
        if self.inSpace():
            return self.viewDistance * 4
        else:
            return self.viewDistance * 2

    def viewDistanceChanged(self):
        self.oldPosition = None  # xxx
        self.discardMasterList()
        self.loadNearbyChunks()
        self.discardChunksOutsideViewDistance()

    maxWorkFactor = 64
    minWorkFactor = 1
    workFactor = 2

    chunkCalculator = None

    _level = None

    @property
    def level(self):
        return self._level

    @level.setter
    def level(self, level):
        """ this probably warrants creating a new renderer """
        self.stopWork()

        self._level = level
        self.oldPosition = None
        self.position = (0, 0, 0)
        self.chunkCalculator = None

        self.invalidChunkQueue = deque()

        self.discardAllChunks()

        self.loadableChunkMarkers.invalidate()

        if level:
            self.chunkCalculator = self.calculatorClass(self.level)

            self.oldPosition = None
            level.allChunks

        self.loadNearbyChunks()

    position = (0, 0, 0)

    def loadChunksStartingFrom(self, wx, wz, distance=None):  # world position
        if None is self.level:
            return

        cx = wx >> 4
        cz = wz >> 4

        if distance is None:
            d = self.effectiveViewDistance
        else:
            d = distance

        self.chunkIterator = self.iterateChunks(wx, wz, d * 2)

    def iterateChunks(self, x, z, d):
        cx = x >> 4
        cz = z >> 4

        yield (cx, cz)

        step = dir = 1

        while True:
            for i in range(step):
                cx += dir
                yield (cx, cz)

            for i in range(step):
                cz += dir
                yield (cx, cz)

            step += 1
            if step > d and not self.overheadMode:
                raise StopIteration

            dir = -dir

    chunkIterator = None

    @property
    def chunkWorker(self):
        if self._chunkWorker is None:
            self._chunkWorker = self.makeWorkIterator()
        return self._chunkWorker

    def stopWork(self):
        self._chunkWorker = None

    def discardAllChunks(self):
        self.bufferUsage = 0
        self.forgetAllDisplayLists()
        self.chunkRenderers = {}
        self.oldPosition = None  # xxx force reload

    def discardChunksInBox(self, box):
        self.discardChunks(box.chunkPositions)

    def discardChunksOutsideViewDistance(self):
        if self.overheadMode:
            return

        # print "discardChunksOutsideViewDistance"
        d = self.effectiveViewDistance
        cx = (self.position[0] - self.origin[0]) / 16
        cz = (self.position[2] - self.origin[2]) / 16

        origin = (cx - d, cz - d)
        size = d * 2

        if not len(self.chunkRenderers):
            return
        (ox, oz) = origin
        bytes = 0
        # chunks = numpy.fromiter(self.chunkRenderers.iterkeys(), dtype='int32', count=len(self.chunkRenderers))
        chunks = numpy.fromiter(self.chunkRenderers.iterkeys(), dtype='i,i', count=len(self.chunkRenderers))
        chunks.dtype = 'int32'
        chunks.shape = len(self.chunkRenderers), 2

        if size:
            outsideChunks = chunks[:, 0] < ox - 1
            outsideChunks |= chunks[:, 0] > ox + size
            outsideChunks |= chunks[:, 1] < oz - 1
            outsideChunks |= chunks[:, 1] > oz + size
            chunks = chunks[outsideChunks]

        self.discardChunks(chunks)

    def discardChunks(self, chunks):
        for cx, cz in chunks:
            self.discardChunk(cx, cz)
        self.oldPosition = None  # xxx force reload

    def discardChunk(self, cx, cz):
        " discards the chunk renderer for this chunk and compresses the chunk "
        if (cx, cz) in self.chunkRenderers:
            self.bufferUsage -= self.chunkRenderers[cx, cz].bufferSize
            self.chunkRenderers[cx, cz].forgetDisplayLists()
            del self.chunkRenderers[cx, cz]

    _fastLeaves = False

    @property
    def fastLeaves(self):
        return self._fastLeaves

    @fastLeaves.setter
    def fastLeaves(self, val):
        if self._fastLeaves != bool(val):
            self.discardAllChunks()

        self._fastLeaves = bool(val)

    _roughGraphics = False

    @property
    def roughGraphics(self):
        return self._roughGraphics

    @roughGraphics.setter
    def roughGraphics(self, val):
        if self._roughGraphics != bool(val):
            self.discardAllChunks()

        self._roughGraphics = bool(val)

    _showHiddenOres = False

    @property
    def showHiddenOres(self):
        return self._showHiddenOres

    @showHiddenOres.setter
    def showHiddenOres(self, val):
        if self._showHiddenOres != bool(val):
            self.discardAllChunks()

        self._showHiddenOres = bool(val)

    def invalidateChunk(self, cx, cz, layers=None):
        " marks the chunk for regenerating vertex data and display lists "
        if (cx, cz) in self.chunkRenderers:
            # self.chunkRenderers[(cx,cz)].invalidate()
            # self.bufferUsage -= self.chunkRenderers[(cx, cz)].bufferSize

            self.chunkRenderers[(cx, cz)].invalidate(layers)
            # self.bufferUsage += self.chunkRenderers[(cx, cz)].bufferSize

            self.invalidChunkQueue.append((cx, cz))  # xxx encapsulate

    def invalidateChunksInBox(self, box, layers=None):
        # If the box is at the edge of any chunks, expanding by 1 makes sure the neighboring chunk gets redrawn.
        box = box.expand(1)

        self.invalidateChunks(box.chunkPositions, layers)

    def invalidateEntitiesInBox(self, box):
        self.invalidateChunks(box.chunkPositions, [Layer.Entities])

    def invalidateChunks(self, chunks, layers=None):
        for c in chunks:
            cx, cz = c
            self.invalidateChunk(cx, cz, layers)

        self.stopWork()
        self.discardMasterList()
        self.loadNearbyChunks()

    def invalidateAllChunks(self, layers=None):
        self.invalidateChunks(self.chunkRenderers.iterkeys(), layers)

    def forgetAllDisplayLists(self):
        for cr in self.chunkRenderers.itervalues():
            cr.forgetDisplayLists()

    def invalidateMasterList(self):
        self.discardMasterList()

    shouldRecreateMasterList = True

    def discardMasterList(self):
        self.shouldRecreateMasterList = True

    @property
    def shouldDrawAll(self):
        box = self.level.bounds
        return self.isPreviewer and box.width + box.length < 256

    distanceToChunkReload = 32.0

    def cameraMovedFarEnough(self):
        if self.shouldDrawAll:
            return False
        if self.oldPosition is None:
            return True

        cPos = self.position
        oldPos = self.oldPosition

        cameraDelta = self.distanceToChunkReload

        return any([abs(x - y) > cameraDelta for x, y in zip(cPos, oldPos)])

    def loadVisibleChunks(self):
        """ loads nearby chunks if the camera has moved beyond a certain distance """

        # print "loadVisibleChunks"
        if self.cameraMovedFarEnough():
            if datetime.now() - self.lastVisibleLoad > timedelta(0, 0.5):
                self.discardChunksOutsideViewDistance()
                self.loadNearbyChunks()

                self.oldPosition = self.position
                self.lastVisibleLoad = datetime.now()

    lastVisibleLoad = datetime.now()

    def loadNearbyChunks(self):
        if None is self.level:
            return
        # print "loadNearbyChunks"
        cameraPos = self.position

        if self.shouldDrawAll:
            self.loadAllChunks()
        else:
            # subtract self.origin to load nearby chunks correctly for preview renderers
            self.loadChunksStartingFrom(int(cameraPos[0]) - self.origin[0], int(cameraPos[2]) - self.origin[2])

    def loadAllChunks(self):
        box = self.level.bounds

        self.loadChunksStartingFrom(box.origin[0] + box.width / 2, box.origin[2] + box.length / 2, max(box.width, box.length))

    _floorTexture = None

    @property
    def floorTexture(self):
        if self._floorTexture is None:
            self._floorTexture = Texture(self.makeFloorTex)
        return self._floorTexture

    def makeFloorTex(self):
        color0 = (0xff, 0xff, 0xff, 0x22)
        color1 = (0xff, 0xff, 0xff, 0x44)

        img = numpy.array([color0, color1, color1, color0], dtype='uint8')

        GL.glTexParameter(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST)
        GL.glTexParameter(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST)

        GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA, 2, 2, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, img)

    def invalidateChunkMarkers(self):
        self.loadableChunkMarkers.invalidate()

    def _drawLoadableChunkMarkers(self):
        if self.level.chunkCount:
            chunkSet = set(self.level.allChunks)

            sizedChunks = chunkMarkers(chunkSet)

            GL.glPushAttrib(GL.GL_FOG_BIT)
            GL.glDisable(GL.GL_FOG)

            GL.glEnable(GL.GL_BLEND)
            GL.glEnable(GL.GL_POLYGON_OFFSET_FILL)
            GL.glPolygonOffset(DepthOffset.ChunkMarkers, DepthOffset.ChunkMarkers)
            GL.glEnable(GL.GL_DEPTH_TEST)

            GL.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY)
            GL.glEnable(GL.GL_TEXTURE_2D)
            GL.glColor(1.0, 1.0, 1.0, 1.0)

            self.floorTexture.bind()
            # chunkColor = numpy.zeros(shape=(chunks.shape[0], 4, 4), dtype='float32')
#            chunkColor[:]= (1, 1, 1, 0.15)
#
#            cc = numpy.array(chunks[:,0] + chunks[:,1], dtype='int32')
#            cc &= 1
#            coloredChunks = cc > 0
#            chunkColor[coloredChunks] = (1, 1, 1, 0.28)
#            chunkColor *= 255
#            chunkColor = numpy.array(chunkColor, dtype='uint8')
#
            # GL.glColorPointer(4, GL.GL_UNSIGNED_BYTE, 0, chunkColor)
            for size, chunks in sizedChunks.iteritems():
                if not len(chunks):
                    continue
                chunks = numpy.array(chunks, dtype='float32')

                chunkPosition = numpy.zeros(shape=(chunks.shape[0], 4, 3), dtype='float32')
                chunkPosition[:, :, (0, 2)] = numpy.array(((0, 0), (0, 1), (1, 1), (1, 0)), dtype='float32')
                chunkPosition[:, :, (0, 2)] *= size
                chunkPosition[:, :, (0, 2)] += chunks[:, numpy.newaxis, :]
                chunkPosition *= 16
                GL.glVertexPointer(3, GL.GL_FLOAT, 0, chunkPosition.ravel())
                # chunkPosition *= 8
                GL.glTexCoordPointer(2, GL.GL_FLOAT, 0, (chunkPosition[..., (0, 2)] * 8).ravel())
                GL.glDrawArrays(GL.GL_QUADS, 0, len(chunkPosition) * 4)

            GL.glDisableClientState(GL.GL_TEXTURE_COORD_ARRAY)
            GL.glDisable(GL.GL_TEXTURE_2D)
            GL.glDisable(GL.GL_BLEND)
            GL.glDisable(GL.GL_DEPTH_TEST)
            GL.glDisable(GL.GL_POLYGON_OFFSET_FILL)
            GL.glPopAttrib()

    def drawLoadableChunkMarkers(self):
        if not self.isPreviewer or isinstance(self.level, pymclevel.MCInfdevOldLevel):
            self.loadableChunkMarkers.call(self._drawLoadableChunkMarkers)

        # self.drawCompressedChunkMarkers()

    needsImmediateRedraw = False
    viewingFrustum = None
    if "-debuglists" in sys.argv:
        def createMasterLists(self):
            pass

        def callMasterLists(self):
            for cr in self.chunkRenderers.itervalues():
                cr.debugDraw()
    else:
        def createMasterLists(self):
            if self.shouldRecreateMasterList:
                lists = {}
                chunkLists = defaultdict(list)
                chunksPerFrame = 80
                shouldRecreateAgain = False

                for ch in self.chunkRenderers.itervalues():
                    if chunksPerFrame:
                        if ch.needsRedisplay:
                            chunksPerFrame -= 1
                        ch.makeDisplayLists()
                    else:
                        shouldRecreateAgain = True

                    if ch.renderstateLists:
                        for rs in ch.renderstateLists:
                            chunkLists[rs] += ch.renderstateLists[rs]

                for rs in chunkLists:
                    if len(chunkLists[rs]):
                        lists[rs] = numpy.array(chunkLists[rs], dtype='uint32').ravel()

                # lists = lists[lists.nonzero()]
                self.masterLists = lists
                self.shouldRecreateMasterList = shouldRecreateAgain
                self.needsImmediateRedraw = shouldRecreateAgain

        def callMasterLists(self):
            for renderstate in self.chunkCalculator.renderstates:
                if renderstate not in self.masterLists:
                    continue

                if self.alpha != 0xff and renderstate is not ChunkCalculator.renderstateLowDetail:
                    GL.glEnable(GL.GL_BLEND)
                renderstate.bind()

                GL.glCallLists(self.masterLists[renderstate])

                renderstate.release()
                if self.alpha != 0xff and renderstate is not ChunkCalculator.renderstateLowDetail:
                    GL.glDisable(GL.GL_BLEND)

    errorLimit = 10

    def draw(self):
        self.needsRedraw = False
        if not self.level:
            return
        if not self.chunkCalculator:
            return
        if not self.render:
            return

        chunksDrawn = 0
        if self.level.materials.name in ("Pocket", "Alpha"):
            GL.glMatrixMode(GL.GL_TEXTURE)
            GL.glScalef(1/2., 1/2., 1/2.)

        with gl.glPushMatrix(GL.GL_MODELVIEW):
            dx, dy, dz = self.origin
            GL.glTranslate(dx, dy, dz)

            GL.glEnable(GL.GL_CULL_FACE)
            GL.glEnable(GL.GL_DEPTH_TEST)

            self.level.materials.terrainTexture.bind()
            GL.glEnable(GL.GL_TEXTURE_2D)
            GL.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY)

            offset = DepthOffset.PreviewRenderer if self.isPreviewer else DepthOffset.Renderer
            GL.glPolygonOffset(offset, offset)
            GL.glEnable(GL.GL_POLYGON_OFFSET_FILL)

            self.createMasterLists()
            try:
                self.callMasterLists()

            except GL.GLError, e:
                if self.errorLimit:
                    self.errorLimit -= 1
                    traceback.print_exc()
                    print e

            GL.glDisable(GL.GL_POLYGON_OFFSET_FILL)

            GL.glDisable(GL.GL_CULL_FACE)
            GL.glDisable(GL.GL_DEPTH_TEST)

            GL.glDisable(GL.GL_TEXTURE_2D)
            GL.glDisableClientState(GL.GL_TEXTURE_COORD_ARRAY)
                # if self.drawLighting:
            self.drawLoadableChunkMarkers()

        if self.level.materials.name in ("Pocket", "Alpha"):
            GL.glMatrixMode(GL.GL_TEXTURE)
            GL.glScalef(2., 2., 2.)

    renderErrorHandled = False

    def addDebugInfo(self, addDebugString):
        addDebugString("BU: {0} MB, ".format(
            self.bufferUsage / 1000000,
             ))

        addDebugString("WQ: {0}, ".format(len(self.invalidChunkQueue)))
        if self.chunkIterator:
            addDebugString("[LR], ")

        addDebugString("CR: {0}, ".format(len(self.chunkRenderers),))

    def next(self):
        self.chunkWorker.next()

    def makeWorkIterator(self):
        ''' does chunk face and vertex calculation work. returns a generator that can be
        iterated over for smaller work units.'''

        try:
            while True:
                if self.level is None:
                    raise StopIteration

                if len(self.invalidChunkQueue) > 1024:
                    self.invalidChunkQueue.clear()

                if len(self.invalidChunkQueue):
                    c = self.invalidChunkQueue[0]
                    for i in self.workOnChunk(c):
                        yield
                    self.invalidChunkQueue.popleft()

                elif self.chunkIterator is None:
                    raise StopIteration

                else:
                    c = self.chunkIterator.next()
                    if self.vertexBufferLimit:
                        while self.bufferUsage > (0.9 * (self.vertexBufferLimit << 20)):
                            deadChunk = None
                            deadDistance = self.chunkDistance(c)
                            for cr in self.chunkRenderers.itervalues():
                                dist = self.chunkDistance(cr.chunkPosition)
                                if dist > deadDistance:
                                    deadChunk = cr
                                    deadDistance = dist

                            if deadChunk is not None:
                                self.discardChunk(*deadChunk.chunkPosition)

                            else:
                                break

                        else:
                            for i in self.workOnChunk(c):
                                yield

                    else:
                        for i in self.workOnChunk(c):
                            yield

                yield

        finally:
            self._chunkWorker = None
            if self.chunkIterator:
                self.chunkIterator = None

    vertexBufferLimit = 384

    def getChunkRenderer(self, c):
        if not (c in self.chunkRenderers):
            cr = self.chunkClass(self, c)
        else:
            cr = self.chunkRenderers[c]

        return cr

    def calcFacesForChunkRenderer(self, cr):
        self.bufferUsage -= cr.bufferSize

        calc = cr.calcFaces()
        work = 0
        for i in calc:
            yield
            work += 1

        self.chunkDone(cr, work)

    def workOnChunk(self, c):
        work = 0

        if self.level.containsChunk(*c):
            cr = self.getChunkRenderer(c)
            if self.viewingFrustum:
                # if not self.viewingFrustum.visible(numpy.array([[c[0] * 16 + 8, 64, c[1] * 16 + 8, 1.0]]), 64).any():
                if not self.viewingFrustum.visible1([c[0] * 16 + 8, self.level.Height / 2, c[1] * 16 + 8, 1.0], self.level.Height / 2):
                    raise StopIteration
                    yield

            faceInfoCalculator = self.calcFacesForChunkRenderer(cr)
            try:
                for result in faceInfoCalculator:
                    work += 1
                    if (work % MCRenderer.workFactor) == 0:
                        yield

                self.invalidateMasterList()

            except Exception, e:
                traceback.print_exc()
                fn = c

                logging.info(u"Skipped chunk {f}: {e}".format(e=e, f=fn))

    redrawChunks = 0

    def chunkDone(self, chunkRenderer, work):
        self.chunkRenderers[chunkRenderer.chunkPosition] = chunkRenderer
        self.bufferUsage += chunkRenderer.bufferSize
        # print "Chunk {0} used {1} work units".format(chunkRenderer.chunkPosition, work)
        if not self.needsRedraw:
            if self.redrawChunks:
                self.redrawChunks -= 1
                if not self.redrawChunks:
                    self.needsRedraw = True

            else:
                self.redrawChunks = 2

        if work > 0:
            self.oldChunkStartTime = self.chunkStartTime
            self.chunkStartTime = datetime.now()
            self.chunkSamples.pop(0)
            self.chunkSamples.append(self.chunkStartTime - self.oldChunkStartTime)

            cx, cz = chunkRenderer.chunkPosition
import sys
import gc
import os
import math
import csv
import copy
import time
import numpy
import config
import frustum
import logging
import glutils
import release
import mceutils
import platform
import functools
import editortools
import itertools
import mcplatform
import pymclevel
import renderer
from compass import CompassOverlay
from editortools.thumbview import ThumbView
from pymclevel.infiniteworld import SessionLockLost
from math import isnan
from os.path import dirname, isdir
from datetime import datetime, timedelta
from collections import defaultdict, deque
from OpenGL import GL
from OpenGL import GLU
from albow import alert, ask, AttrRef, Button, Column, get_font, Grid, input_text, IntField, Menu, root, Row, TableColumn, TableView, TextField, TimeField, Widget, CheckBox
from albow.controls import Label, SmallValueDisplay, ValueDisplay
from albow.dialogs import Dialog, QuickDialog, wrapped_label
from albow.openglwidgets import GLOrtho, GLViewport
from pygame import display, event, key, KMOD_ALT, KMOD_CTRL, KMOD_LALT, KMOD_META, KMOD_RALT, KMOD_SHIFT, mouse, MOUSEMOTION
from depths import DepthOffset
from editortools.operation import Operation
from editortools.chunk import GeneratorPanel
from glbackground import GLBackground, Panel
from glutils import gl, Texture
from mcplatform import askSaveFile
from pymclevel.minecraft_server import alphanum_key #?????
from renderer import MCRenderer
                        from pymclevel import biome_types
    def pasteSchematic(self, schematic):
        if schematic == None:
            return
        self.currentTool.cancel()
        craneTool = self.toolbar.tools[5]  # xxx
        self.currentTool = craneTool
        craneTool.loadLevel(schematic)

    def deleteSelectedBlocks(self):
        self.selectionTool.deleteBlocks()

    @mceutils.alertException
    def undo(self):
        if len(self.undoStack) == 0:
            return
        with mceutils.setWindowCaption("UNDOING - "):
            self.freezeStatus("Undoing the previous operation...")
            op = self.undoStack.pop()
            op.undo()
            changedBox = op.dirtyBox()
            if changedBox is not None:
                self.invalidateBox(changedBox)
            if op.changedLevel:
                self.addUnsavedEdit()

    def invalidateBox(self, box):
        self.renderer.invalidateChunksInBox(box)

    def invalidateChunks(self, c):
        self.renderer.invalidateChunks(c)

    def invalidateAllChunks(self):
        self.renderer.invalidateAllChunks()

    def discardAllChunks(self):
        self.renderer.discardAllChunks()

    def addDebugString(self, string):
        if self.debug:
            self.debugString += string

    averageFPS = 0.0
    averageCPS = 0.0
    shouldLoadAndRender = True
    showWorkInfo = False

    def gl_draw(self):
        self.debugString = ""
        self.inspectionString = ""

        if not self.level:
            return

        if not self.shouldLoadAndRender:
            return

        self.renderer.loadVisibleChunks()
        self.addWorker(self.renderer)

        if self.currentTool.previewRenderer:
            self.currentTool.previewRenderer.loadVisibleChunks()
            self.addWorker(self.currentTool.previewRenderer)

        self.frames += 1
        frameDuration = self.getFrameDuration()
        while frameDuration > (datetime.now() - self.frameStartTime):  # if it's less than 0ms until the next frame, go draw.  otherwise, go work.
            self.doWorkUnit()
        if self.showWorkInfo:
            self.updateWorkInfoPanel()

        frameStartTime = datetime.now()
        timeDelta = frameStartTime - self.frameStartTime

        # self.addDebugString("FrameStart: {0}  CameraTick: {1}".format(frameStartTime, self.mainViewport.lastTick))
        # self.addDebugString("D: %d, " % () )

        self.currentFrameDelta = timeDelta
        self.frameSamples.pop(0)
        self.frameSamples.append(timeDelta)

        frameTotal = numpy.sum(self.frameSamples)

        self.averageFPS = 1000000. / ((frameTotal.microseconds + 1000000 * frameTotal.seconds) / float(len(self.frameSamples)) + 0.00001)

        r = self.renderer

        chunkTotal = numpy.sum(r.chunkSamples)
        cps = 1000000. / ((chunkTotal.microseconds + 1000000 * chunkTotal.seconds) / float(len(r.chunkSamples)) + 0.00001)
        self.averageCPS = cps

        self.oldFrameStartTime = self.frameStartTime
        self.frameStartTime = frameStartTime

        if self.debug > 0:
            self.debugString = "FPS: %0.1f/%0.1f, CPS: %0.1f, VD: %d, W: %d, WF: %d, " % (1000000. / (float(timeDelta.microseconds) + 0.000001),
                                                                           self.averageFPS,
                                                                           cps,
                                                                           self.renderer.viewDistance,
                                                                           len(self.workers),
                                                                           self.renderer.workFactor)

            if True:  # xxx
                self.debugString += "DL: {dl} ({dlcount}), Tx: {t}, gc: {g}, ".format(
                    dl=len(glutils.DisplayList.allLists), dlcount=glutils.gl.listCount,
                    t=len(glutils.Texture.allTextures), g=len(gc.garbage))

            if self.renderer:
                self.renderer.addDebugInfo(self.addDebugString)

                # if self.onscreen:

    def createWorkInfoPanel(self):
        infos = []
        for w in sorted(self.workers):
            if isinstance(w, MCRenderer):
                label = Label("Rendering chunks" + ((datetime.now().second / 3) % 3) * ".")
                progress = Label("{0} chunks ({1} pending updates)".format(len(w.chunkRenderers), len(w.invalidChunkQueue)))
                col = Column((label, progress), align="l", width=200)
                infos.append(col)
            elif isinstance(w, RunningOperation):  # **FIXME** Where is RunningOperation supposed to come from?  -David Sowder 20120311

                label = Label(w.description)