#-# Modified by D.C.-G. for translation purpose
import atexit
import os
import shutil
import tempfile
import albow
from albow.translate import _
from pymclevel import BoundingBox
import numpy
from albow.root import Cancel
import pymclevel
from albow import showProgress
from pymclevel.mclevelbase import exhaust

undo_folder = os.path.join(tempfile.gettempdir(), "mcedit_undo", str(os.getpid()))


def mkundotemp():
    if not os.path.exists(undo_folder):
        os.makedirs(undo_folder)

    return tempfile.mkdtemp("mceditundo", dir=undo_folder)


atexit.register(shutil.rmtree, undo_folder, True)


class Operation(object):
    changedLevel = True
    undoLevel = None
    redoLevel = 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) - (mincx << 4), level.Height, (maxcz << 4) - (mincz << 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

    @staticmethod
    def extractUndoSchematic(level, box):
        if box.volume > 131072:
            sch = showProgress("Recording undo...", level.extractZipSchematicIter(box), cancel=True)
        else:
            sch = level.extractZipSchematic(box)
        if sch == "Cancel" or sch == "Canceled":
            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:
            self.redoLevel = self.extractUndo(self.level, self.dirtyBox())

            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 redo(self):
        if self.redoLevel:
            def _redo():
                yield 0, 0, "Redoing..."
                if hasattr(self.level, 'copyChunkFrom'):
                    for i, (cx, cz) in enumerate(self.redoLevel.allChunks):
                        self.level.copyChunkFrom(self.redoLevel, cx, cz)
                        yield i, self.redoLevel.chunkCount, "Copying chunk %s..." % ((cx, cz),)
                else:
                    for i in self.level.copyBlocksFromIter(self.redoLevel, self.redoLevel.bounds,
                                                           self.redoLevel.sourcePoint, biomes=True):
                        yield i, self.undoLevel.chunkCount, "Copying..."

            if self.redoLevel.chunkCount > 25:
                showProgress("Redoing...", _redo())
            else:
                exhaust(_redo())

    def dirtyBox(self):
        """ The region modified by the operation.
        Return None to indicate no blocks were changed.
        """
        return None
