#!/usr/bin/env python
"""
Prints a map of the entire world.
"""

import os, sys
import math
from struct import pack
from color import *
# local module
try:
    import nbt
except ImportError:
    # nbt not in search path. Let's see if it can be found in the parent folder
    extrasearchpath = os.path.realpath(os.path.join(__file__,os.pardir,os.pardir))
    if not os.path.exists(os.path.join(extrasearchpath,'nbt')):
        raise
    sys.path.append(extrasearchpath)
from nbt.region import RegionFile
from nbt.chunk import AnvilChunk
from nbt.world import WorldFolder,McRegionWorldFolder
from blocks import block_ignore,block_colors
# PIL module (not build-in)
try:
    from PIL import Image
except ImportError:
    # PIL not in search path. Let's see if it can be found in the parent folder
    sys.stderr.write("Module PIL/Image not found. Pillow (a PIL fork) can be found at http://python-imaging.github.io/\n")
    # Note: it may also be possible that PIL is installed, but JPEG support is disabled or broken
    sys.exit(70) # EX_SOFTWARE

def get_heightmap_image(chunk, buffer=False, gmin=False, gmax=False):
    points = chunk.blocks.generate_heightmap(buffer, True)
    # Normalize the points
    hmin = min(points) if (gmin == False) else gmin # Allow setting the min/max explicitly, in case this is part of a bigger map
    hmax = max(points) if (gmax == False) else gmax
    hdelta = hmax-hmin+0.0
    pixels = ""
    for y in range(16):
        for x in range(16):
            # pix X => mc -Z
            # pix Y => mc X
            offset = (15-x)*16+y
            height = int((points[offset]-hmin)/hdelta*255)
            if (height < 0): height = 0
            if (height > 255): height = 255
            pixels += pack(">B", height)
    im = Image.fromstring('L', (16,16), pixels)
    return im

# Record of unknown blocks found

unknown_blocks = []

def get_ground(chunk, x, z):

    if x < 0:
        x = 0
    if x > 15:
        x = 15
    if z < 0:
        z = 0
    if z > 15:
        z = 15

    max_height = chunk.get_max_height()
    ground = max_height    

    for y in range(max_height,-1,-1):
        block_id = chunk.get_block(x, y, z)
        if block_id != None:
            if (block_id not in block_ignore or y == 0):
                # Here is ground level
                ground = y
                break
    return ground

def get_map(chunk):
    # Show an image of the chunk from above
    pixels = b""
    
    for z in range(16):
        for x in range(16):
            # Find the highest block in this column           
            ground_height = get_ground(chunk, x, z)
            block_id = chunk.get_block(x, ground_height, z)
          
            if block_id != None:
                if block_id in block_colors:
                    color = block_colors[block_id]
                else:
                    color = {'h':0, 's':0, 'l':100}
                    if not block_id in unknown_blocks:
                        unknown_blocks.append(block_id)
            else:
                color = {'h':0, 's':0, 'l':0}

            #height_shift = (ground_height-64)*0.5
            height_shift = (ground_height - get_ground(chunk, x-1, z-1)) * 5

            final_color = {'h':color['h'], 's':color['s'], 'l':color['l'] + height_shift}
            if final_color['l'] > 100: final_color['l'] = 100
            if final_color['l'] < 0: final_color['l'] = 0

            rgb = hsl2rgb(final_color['h'], final_color['s'], final_color['l'])

            pixels += pack("BBB", rgb[0], rgb[1], rgb[2])

    im = Image.frombytes('RGB', (16,16), pixels)
    return im

def generate_world_map(world_folder, show=True):
    world = WorldFolder(world_folder)
    bb = world.get_boundingbox()
    world_map = Image.new('RGB', (16*bb.lenx(),16*bb.lenz()))
    t = world.chunk_count()
    try:
        i =0.0
        for chunk in world.iter_chunks():
            if i % 50 ==0:
                sys.stdout.write("Rendering image")
            elif i % 2 == 0:
                sys.stdout.write(".")
                sys.stdout.flush()
            elif i % 50 == 49:
                sys.stdout.write("%5.1f%%\n" % (100*i/t))
            i +=1
            chunkmap = get_map(chunk)
            x,z = chunk.get_coords()
            world_map.paste(chunkmap, (16*(x-bb.minx),16*(z-bb.minz)))
        print(" done\n")
        filename = os.path.basename(world_folder)+".png"
        world_map.save(filename,"PNG")
        print("Saved map as %s" % filename)
        print("warning: unknown color for block ids:")
        for block_id in unknown_blocks:
            print("%s" % block_id)

    except KeyboardInterrupt:
        print(" aborted\n")
        filename = os.path.basename(world_folder)+".partial.png"
        world_map.save(filename,"PNG")
        print("Saved map as %s" % filename)
        return 75 # EX_TEMPFAIL
    if show:
        world_map.show()

def get_region_map(region):
    region_map = Image.new('RGB', (16*32,16*32))
    t = 32 * 32
    try:
        for i, nbtfile in enumerate(region.iter_chunks()):
            chunk = AnvilChunk(nbtfile)
            if i % 50 ==0:
                sys.stdout.write("Rendering image")
            elif i % 2 == 0:
                sys.stdout.write(".")
                sys.stdout.flush()
            elif i % 50 == 49:
                sys.stdout.write("%5.1f%%\n" % (100*float(i)/t))
            chunkmap = get_map(chunk)
            x,z = chunk.get_coords()
            region_map.paste(chunkmap, (16*(x%32),16*(z%32)))
        print(" done\n")
        rx,rz = region.get_location()
        filename = os.path.basename(str(rx) + '.' + str(rz))+".png"
        region_map.save(filename,"PNG")
        print("Saved map as %s" % filename)
        
    except KeyboardInterrupt:
        print(" aborted\n")
        exit()

def is_region_updated(region):
    
    rx,rz = region.get_location()
    imgfilename = os.path.basename(str(rx) + '.' + str(rz))+".png"
    if not os.path.exists(imgfilename):
        return False

    imgtime = os.path.getmtime(imgfilename)
    return region.timestamp < imgtime    


def generate_region_maps(world_folder):
    world = WorldFolder(world_folder)
    for region in world.iter_regions():
        if not is_region_updated(region):
            get_region_map(region)
        else:
            print("skipping region: " + str(region.get_location()))
    
    if len(unknown_blocks) > 0:
        print("warning: unknown color for block ids:")    
        for block_id in unknown_blocks:
                print("%s" % block_id)

def main(world_folder, show=True):
    #generate_world_map(world_folder, show)
    generate_region_maps(world_folder)
    return 0 # NOERR


if __name__ == '__main__':
    if (len(sys.argv) == 1):
        print("No world folder specified!")
        sys.exit(64) # EX_USAGE
    if sys.argv[1] == '--noshow' and len(sys.argv) > 2:
        show = False
        world_folder = sys.argv[2]
    else:
        show = True
        world_folder = sys.argv[1]
    # clean path name, eliminate trailing slashes. required for os.path.basename()
    world_folder = os.path.normpath(world_folder)
    if (not os.path.exists(world_folder)):
        print("No such folder as "+world_folder)
        sys.exit(72) # EX_IOERR
    
    sys.exit(main(world_folder, show))

