"""Copyright (c) 2010-2012 David Rio Vierra

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE."""
#-# Modified by D.C.-G. for translation purpose
import traceback
from OpenGL import GL
import numpy
from numpy import newaxis

from albow import Label, ValueDisplay, AttrRef, Button, Column, ask, Row, alert, Widget, Menu, showProgress, \
    ChoiceButton, IntInputRow, CheckBoxLabel
from albow.translate import _
from editortools.editortool import EditorTool
from glbackground import Panel
from glutils import DisplayList, gl
from mceutils import alertException, setWindowCaption
import mcplatform
import pymclevel
from pymclevel.minecraft_server import MCServerChunkGenerator
from config import config

from albow.dialogs import Dialog
import renderer


class ChunkToolPanel(Panel):
    def __init__(self, tool, *a, **kw):
        if 'name' not in kw.keys():
            kw['name'] = 'Panel.ChunkToolPanel'
        Panel.__init__(self, *a, **kw)

        self.tool = tool

        self.anchor = "whl"

        chunkToolLabel = Label("Selected Chunks:")

        self.chunksLabel = ValueDisplay(ref=AttrRef(self, 'chunkSizeText'), width=115)
        self.chunksLabel.align = "c"
        self.chunksLabel.tooltipText = "..."

        deselectButton = Button("Deselect",
                                tooltipText=None,
                                action=tool.editor.deselect,
        )

        createButton = Button("Create")
        createButton.tooltipText = "Create new chunks within the selection."
        createButton.action = tool.createChunks
        createButton.highlight_color = (0, 255, 0)

        destroyButton = Button("Delete")
        destroyButton.tooltipText = "Delete the selected chunks from disk. Minecraft will recreate them the next time you are near."
        destroyButton.action = tool.destroyChunks

        pruneButton = Button("Prune")
        pruneButton.tooltipText = "Prune the world, leaving only the selected chunks. Any chunks outside of the selection will be removed, and empty region files will be deleted from disk"
        pruneButton.action = tool.pruneChunks

        relightButton = Button("Relight")
        relightButton.tooltipText = "Recalculate light values across the selected chunks"
        relightButton.action = tool.relightChunks
        relightButton.highlight_color = (255, 255, 255)

        repopButton = Button("Repop")
        repopButton.tooltipText = "Mark the selected chunks for repopulation. The next time you play Minecraft, the chunks will have trees, ores, and other features regenerated."
        repopButton.action = tool.repopChunks
        repopButton.highlight_color = (255, 200, 155)

        dontRepopButton = Button("Don't Repop")
        dontRepopButton.tooltipText = "Unmark the selected chunks. They will not repopulate the next time you play the game."
        dontRepopButton.action = tool.dontRepopChunks
        dontRepopButton.highlight_color = (255, 255, 255)

        col = Column((
        chunkToolLabel, self.chunksLabel, deselectButton, createButton, destroyButton, pruneButton, relightButton,
        repopButton, dontRepopButton))
        # col.right = self.width - 10;
        self.width = col.width
        self.height = col.height
        #self.width = 120
        self.add(col)

    @property
    def chunkSizeText(self):
        return _("{0} chunks").format(len(self.tool.selectedChunks()))

    def updateText(self):
        pass
        # self.chunksLabel.text = self.chunksLabelText()


class ChunkTool(EditorTool):
    toolIconName = "chunk"
    tooltipText = "Chunk Control"

    @property
    def statusText(self):
        return _("Click and drag to select chunks. Hold {0} to deselect chunks. Hold {1} to select chunks.").format(_(config.keys.deselectChunks.get()), _(config.keys.selectChunks.get()))

    def toolEnabled(self):
        return isinstance(self.editor.level, pymclevel.ChunkedLevelMixin)

    _selectedChunks = None
    _displayList = None

    def drawToolMarkers(self):
        if self._displayList is None:
            self._displayList = DisplayList(self._drawToolMarkers)

        # print len(self._selectedChunks) if self._selectedChunks else None, "!=", len(self.editor.selectedChunks)

        if self._selectedChunks != self.editor.selectedChunks or True:  # xxx # TODO Pod
            self._selectedChunks = set(self.editor.selectedChunks)
            self._displayList.invalidate()

        self._displayList.call()

    def _drawToolMarkers(self):

        lines = (
            ((-1, 0), (0, 0, 0, 1), []),
            ((1, 0), (1, 0, 1, 1), []),
            ((0, -1), (0, 0, 1, 0), []),
            ((0, 1), (0, 1, 1, 1), []),
        )
        for ch in self._selectedChunks:
            cx, cz = ch
            for (dx, dz), points, positions in lines:
                n = (cx + dx, cz + dz)
                if n not in self._selectedChunks:
                    positions.append([ch])

        color = self.editor.selectionTool.selectionColor + (0.3, )
        GL.glColor(*color)
        with gl.glEnable(GL.GL_BLEND):

            #import renderer

            sizedChunks = renderer.chunkMarkers(self._selectedChunks)
            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[:, newaxis, :]
                chunkPosition *= 16
                chunkPosition[..., 1] = self.editor.level.Height
                GL.glVertexPointer(3, GL.GL_FLOAT, 0, chunkPosition.ravel())
                # chunkPosition *= 8
                GL.glDrawArrays(GL.GL_QUADS, 0, len(chunkPosition) * 4)

        for d, points, positions in lines:
            if 0 == len(positions):
                continue
            vertexArray = numpy.zeros((len(positions), 4, 3), dtype='float32')
            vertexArray[..., [0, 2]] = positions
            vertexArray.shape = len(positions), 2, 2, 3

            vertexArray[..., 0, 0, 0] += points[0]
            vertexArray[..., 0, 0, 2] += points[1]
            vertexArray[..., 0, 1, 0] += points[2]
            vertexArray[..., 0, 1, 2] += points[3]
            vertexArray[..., 1, 0, 0] += points[2]
            vertexArray[..., 1, 0, 2] += points[3]
            vertexArray[..., 1, 1, 0] += points[0]
            vertexArray[..., 1, 1, 2] += points[1]

            vertexArray *= 16

            vertexArray[..., 1, :, 1] = self.editor.level.Height

            GL.glVertexPointer(3, GL.GL_FLOAT, 0, vertexArray)
            GL.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_LINE)
            GL.glDrawArrays(GL.GL_QUADS, 0, len(positions) * 4)
            GL.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_FILL)
            with gl.glEnable(GL.GL_BLEND, GL.GL_DEPTH_TEST):
                GL.glDepthMask(False)
                GL.glDrawArrays(GL.GL_QUADS, 0, len(positions) * 4)
                GL.glDepthMask(True)

    @property
    def worldTooltipText(self):
        box = self.editor.selectionTool.selectionBoxInProgress()
        if box:
            box = box.chunkBox(self.editor.level)
            l, w = box.length // 16, box.width // 16
            return _("%s x %s chunks") % (l, w)
        else:
            if config.settings.viewMode.get() == "Chunk":
                point, face = self.editor.chunkViewport.blockFaceUnderCursor
            else:
                point, face = self.editor.mainViewport.blockFaceUnderCursor
            if point:
                return "Chunk ({}, {})".format(point[0] // 16, point[2] // 16)

    def toolSelected(self):

        self.editor.selectionToChunks()

        self.panel = ChunkToolPanel(self)

        self.panel.centery = self.editor.centery
        self.panel.left = 10

        self.editor.add(self.panel)
        
    def toolDeselected(self):
        self.editor.chunksToSelection()

    def cancel(self):
        self.editor.remove(self.panel)

    def selectedChunks(self):
        return self.editor.selectedChunks

    @alertException
    def destroyChunks(self, chunks=None):
        if "No" == ask("Really delete these chunks? This cannot be undone.", ("Yes", "No")):
            return
        if chunks is None:
            chunks = self.selectedChunks()
        chunks = list(chunks)

        def _destroyChunks():
            i = 0
            chunkCount = len(chunks)

            for cx, cz in chunks:
                i += 1
                yield (i, chunkCount)
                if self.editor.level.containsChunk(cx, cz):
                    try:
                        self.editor.level.deleteChunk(cx, cz)
                    except Exception as e:
                        print "Error during chunk delete: ", e

        with setWindowCaption("DELETING - "):
            showProgress("Deleting chunks...", _destroyChunks())

        self.editor.renderer.invalidateChunkMarkers()
        self.editor.renderer.discardAllChunks()
        # self.editor.addUnsavedEdit()

    @alertException
    def pruneChunks(self):
        if "No" == ask("Save these chunks and remove the rest? This cannot be undone.", ("Yes", "No")):
            return
        self.editor.saveFile()

        def _pruneChunks():
            maxChunks = self.editor.level.chunkCount
            selectedChunks = self.selectedChunks()
            for i, cPos in enumerate(list(self.editor.level.allChunks)):
                if cPos not in selectedChunks:
                    try:
                        self.editor.level.deleteChunk(*cPos)

                    except Exception as e:
                        print "Error during chunk delete: ", e

                yield i, maxChunks

        with setWindowCaption("PRUNING - "):
            showProgress("Pruning chunks...", _pruneChunks())

        self.editor.renderer.invalidateChunkMarkers()
        self.editor.discardAllChunks()

        # self.editor.addUnsavedEdit()

    @alertException
    def relightChunks(self):

        def _relightChunks():
            for i in self.editor.level.generateLightsIter(self.selectedChunks()):
                yield i

        with setWindowCaption("RELIGHTING - "):
            showProgress(_("Lighting {0} chunks...").format(len(self.selectedChunks())),
                         _relightChunks(), cancel=True)

            self.editor.invalidateChunks(self.selectedChunks())
            self.editor.addUnsavedEdit()

    @alertException
    def createChunks(self):
        panel = GeneratorPanel()
        col = [panel]
        label = Label("Create chunks using the settings above? This cannot be undone.")
        col.append(Row([Label("")]))
        col.append(label)
        col = Column(col)
        if Dialog(client=col, responses=["OK", "Cancel"]).present() == "Cancel":
            return
        chunks = self.selectedChunks()

        createChunks = panel.generate(self.editor.level, chunks)

        try:
            with setWindowCaption("CREATING - "):
                showProgress("Creating {0} chunks...".format(len(chunks)), createChunks, cancel=True)
        except Exception as e:
            traceback.print_exc()
            alert(_("Failed to start the chunk generator. {0!r}").format(e))
        finally:
            self.editor.renderer.invalidateChunkMarkers()
            self.editor.renderer.loadNearbyChunks()

    @alertException
    def repopChunks(self):
        for cpos in self.selectedChunks():
            try:
                chunk = self.editor.level.getChunk(*cpos)
                chunk.TerrainPopulated = False
            except pymclevel.ChunkNotPresent:
                continue
        self.editor.renderer.invalidateChunks(self.selectedChunks(), layers=["TerrainPopulated"])

    @alertException
    def dontRepopChunks(self):
        for cpos in self.selectedChunks():
            try:
                chunk = self.editor.level.getChunk(*cpos)
                chunk.TerrainPopulated = True
            except pymclevel.ChunkNotPresent:
                continue
        self.editor.renderer.invalidateChunks(self.selectedChunks(), layers=["TerrainPopulated"])

    def mouseDown(self, *args):
        return self.editor.selectionTool.mouseDown(*args)

    def mouseUp(self, evt, *args):
        self.editor.selectionTool.mouseUp(evt, *args)

    def keyDown(self, evt):
        self.editor.selectionTool.keyDown(evt)

    def keyUp(self, evt):
        self.editor.selectionTool.keyUp(evt)


def GeneratorPanel():
    panel = Widget()
    panel.chunkHeight = 64
    panel.grass = True
    panel.simulate = False
    panel.snapshot = 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=255)
    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(panel.snapshot)
                yield

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

        versionChoice = ChoiceButton(sorted(jarStorage.versions, reverse=True))
        versionChoice.set_size_for_text(200)
        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.")
    useSnapshotServer = CheckBoxLabel("Use snapshot versions", ref=AttrRef(panel, "snapshot"),
                                      tooltipText="Uses the Latest Snapshot Terrain Generation")

    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([useSnapshotServer, 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, useWorldType="DEFAULT"):
        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, worldType=useWorldType):
                        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 as 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
    panel.kill_process = MCServerChunkGenerator.terminateProcesses
    return panel
