import itertools
import os
import shutil
import unittest
import numpy

import mclevel
from infiniteworld import MCInfdevOldLevel
import nbt
from schematic import MCSchematic
from box import BoundingBox
import block_copy
from templevel import mktemp, TempLevel

__author__ = 'Rio'

class TestAnvilLevelCreate(unittest.TestCase):
    def testCreate(self):
        temppath = mktemp("AnvilCreate")
        self.anvilLevel = MCInfdevOldLevel(filename=temppath, create=True)
        self.anvilLevel.close()
        shutil.rmtree(temppath)


class TestAnvilLevel(unittest.TestCase):
    def setUp(self):
        self.indevLevel = TempLevel("hell.mclevel")
        self.anvilLevel = TempLevel("AnvilWorld")

    def testUnsetProperties(self):
        level = self.anvilLevel.level
        del level.root_tag['Data']['LastPlayed']
        import time
        assert 0 != level.LastPlayed
        level.LastPlayed = time.time() * 1000 - 1000000

    def testGetEntities(self):
        level = self.anvilLevel.level
        print len(level.getEntitiesInBox(level.bounds))

    def testCreateChunks(self):
        level = self.anvilLevel.level

        for ch in list(level.allChunks):
            level.deleteChunk(*ch)
        level.createChunksInBox(BoundingBox((0, 0, 0), (32, 0, 32)))

    def testCopyChunks(self):
        level = self.anvilLevel.level
        temppath = mktemp("AnvilCreate")
        newLevel = MCInfdevOldLevel(filename=temppath, create=True)
        for cx, cz in level.allChunks:
            newLevel.copyChunkFrom(level, cx, cz)

        newLevel.close()
        shutil.rmtree(temppath)

    def testCopyConvertBlocks(self):
        indevlevel = self.indevLevel.level
        level = self.anvilLevel.level
        x, y, z = level.bounds.origin
        x += level.bounds.size[0]/2 & ~15
        z += level.bounds.size[2]/2 & ~15
        x -= indevlevel.Width / 2
        z -= indevlevel.Height / 2

        middle = (x, y, z)

        oldEntityCount = len(level.getEntitiesInBox(BoundingBox(middle, indevlevel.bounds.size)))
        level.copyBlocksFrom(indevlevel, indevlevel.bounds, middle)

        convertedSourceBlocks, convertedSourceData = block_copy.convertBlocks(indevlevel, level, indevlevel.Blocks[0:16, 0:16, 0:indevlevel.Height], indevlevel.Data[0:16, 0:16, 0:indevlevel.Height])

        assert ((level.getChunk(x >> 4, z >> 4).Blocks[0:16, 0:16, 0:indevlevel.Height]
                == convertedSourceBlocks).all())

        assert (oldEntityCount + len(indevlevel.getEntitiesInBox(indevlevel.bounds))
                == len(level.getEntitiesInBox(BoundingBox(middle, indevlevel.bounds.size))))

    def testImportSchematic(self):
        level = self.anvilLevel.level
        cx, cz = level.allChunks.next()

        schem = mclevel.fromFile("schematics/CreativeInABox.schematic")
        box = BoundingBox((cx * 16, 64, cz * 16), schem.bounds.size)
        level.copyBlocksFrom(schem, schem.bounds, (0, 64, 0))
        schem = MCSchematic(shape=schem.bounds.size)
        schem.copyBlocksFrom(level, box, (0, 0, 0))
        convertedSourceBlocks, convertedSourceData = block_copy.convertBlocks(schem, level, schem.Blocks, schem.Data)
        assert (level.getChunk(cx, cz).Blocks[0:1, 0:3, 64:65] == convertedSourceBlocks).all()

    def testRecreateChunks(self):
        level = self.anvilLevel.level

        for x, z in itertools.product(xrange(-1, 3), xrange(-1, 2)):
            level.deleteChunk(x, z)
            assert not level.containsChunk(x, z)
            level.createChunk(x, z)

    def testFill(self):
        level = self.anvilLevel.level
        cx, cz = level.allChunks.next()
        box = BoundingBox((cx * 16, 0, cz * 16), (32, level.Height, 32))
        level.fillBlocks(box, level.materials.WoodPlanks)
        level.fillBlocks(box, level.materials.WoodPlanks, [level.materials.Stone])
        level.saveInPlace()
        c = level.getChunk(cx, cz)

        assert (c.Blocks == 5).all()

    def testReplace(self):
        level = self.anvilLevel.level

        level.fillBlocks(BoundingBox((-11, 0, -7), (38, level.Height, 25)), level.materials.WoodPlanks, [level.materials.Dirt, level.materials.Grass])

    def testSaveRelight(self):
        indevlevel = self.indevLevel.level
        level = self.anvilLevel.level

        cx, cz = -3, -1

        level.deleteChunk(cx, cz)

        level.createChunk(cx, cz)
        level.copyBlocksFrom(indevlevel, BoundingBox((0, 0, 0), (32, 64, 32,)), level.bounds.origin)

        level.generateLights()
        level.saveInPlace()

    def testRecompress(self):
        level = self.anvilLevel.level
        cx, cz = level.allChunks.next()

        ch = level.getChunk(cx, cz)
        ch.dirty = True
        ch.Blocks[:] = 6
        ch.Data[:] = 13
        d = {}
        keys = 'Blocks Data SkyLight BlockLight'.split()
        for key in keys:
            d[key] = numpy.array(getattr(ch, key))

        for i in range(5):
            level.saveInPlace()
            ch = level.getChunk(cx, cz)
            ch.dirty = True
            assert (ch.Data == 13).all()
            for key in keys:
                assert (d[key] == getattr(ch, key)).all()

    def testPlayerSpawn(self):
        level = self.anvilLevel.level

        level.setPlayerSpawnPosition((0, 64, 0), "Player")
        level.getPlayerPosition()
        assert len(level.players) != 0

    def testBigEndianIntHeightMap(self):
        """ Test modifying, saving, and loading the new TAG_Int_Array heightmap
        added with the Anvil format.
        """
        chunk = nbt.load("testfiles/AnvilChunk.dat")

        hm = chunk["Level"]["HeightMap"]
        hm.value[2] = 500
        oldhm = numpy.array(hm.value)

        filename = mktemp("ChangedChunk")
        chunk.save(filename)
        changedChunk = nbt.load(filename)
        os.unlink(filename)

        eq = (changedChunk["Level"]["HeightMap"].value == oldhm)
        assert eq.all()
