import bpy
import json
import math
from bpy_extras.io_utils import ImportHelper
from bpy.props import StringProperty, BoolProperty, EnumProperty
from bpy.types import Operator

bl_info = {
    "name": "Import MCN Scene (.mccd)",
    "author": "Dylan (dylanpdx)",
    "version": (1, 1),
    "blender": (2, 68, 0),
    "location": "File > Import > MachineCraft Scene (.mccd)",
    "description": "Import MachineCraft Scene",
    "warning": "",
    "wiki_url": "https://dylangrinberg.com/MachineCraft/McnImport/",
    "tracker_url": "",
    "category": "Import"}
wm = bpy.context.window_manager

## Converts rgb255 -> r,g,b,a (if a==0, plastic. if a==1, wood)
def rgb255toRGB(rgb255):
    r = (rgb255 & 255) / 255.0
    g = (rgb255 >> 8 & 255) / 255.0
    b = (rgb255 >> 16 & 255) / 255.0
    a = (rgb255 >> 24 & 255) / 255.0
    return [r,g,b,a];

## Generates a cube. Do not call this a lot because it will cause lag
def genCloneCube():
    bpy.ops.mesh.primitive_cube_add()
    return bpy.context.object

## Adds a cube at x,y,z with rotation rx,ry,rz and size sx,sy,sz, color rgb255
def addCube(x,y,z,rx,ry,rz,sx,sy,sz,rgb255,self):
    color = rgb255toRGB(rgb255)
    copy = self.clonecube.copy()
    copy.data = self.clonecube.data.copy() # reduce lag
    copy.location=(x,z,y)
    copy.scale = (sx,sz,sy)
    copy.rotation_euler=(rx,rz,ry)

    #bpy.context.scene.objects.link(copy)
    # this groups blocks by the color they are in
    if rgb255 not in self.mats:
        self.mats[rgb255] = bpy.data.materials.new(name="MCNColor_"+str(rgb255));
        self.mats[rgb255].diffuse_color = (color[0], color[1], color[2]) #change color

    copy.data.materials.append(self.mats[rgb255]) #add the material to the object
    #copy.select = False

    return copy

def addPrim(prim,self):
    x = prim["px"]
    y = prim["py"]
    z = prim["pz"]

    rx = math.radians(prim["ex"])
    ry = math.radians(prim["ey"])
    rz = math.radians(prim["ez"])

    sx = (prim["sizeI"] & 255)
    sy = (prim["sizeI"] >> 8 & 255)
    sz = ((prim["sizeI"] >> 16) & 255)

    return addCube(x,y,z,-rx,-ry,-rz,sx/2,sy/2,sz/2,prim["rgb255"],self) # rotation is negative

def linkAdded(self):
    i = 0
    tot=str(len(self.added))
    for object in self.added:
        print("linking "+str(i)+"/"+tot)
        bpy.context.scene.objects.link(object)
        i+=1
    self.added=[]

class ImportMCCD(Operator, ImportHelper):
    """Import a MachineCraft Scene"""
    bl_idname = "mcnimport.scene"  # important since its how bpy.ops.import_test.some_data is constructed
    bl_label = "Import MCCD"

    # ImportHelper mixin class uses this
    filename_ext = ".mccd"

    filter_glob = StringProperty(
            default="*.mccd",
            options={'HIDDEN'},
            maxlen=255,  # Max internal buffer length, longer would be clamped.
            )
    def __init__(self):
        self.repeatingTimer=None
        self.mats = {} # for material grouping
        self.currentCube = 0
        self.primData = None
        self.primLen = 0
        self.added=[]

    def execute(self, context):
        if self.repeatingTimer is None:
            f = open(self.filepath, 'r', encoding='utf-8')
            data = json.loads(f.read())
            f.close()

            self.currentCube = 0
            self.primData = data["primData"]
            self.primLen = len(self.primData)
            self.clonecube = genCloneCube() # initial cube
            self.added=[]
            wm.progress_begin(0, self.primLen) # progress

            self.repeatingTimer = context.window_manager.event_timer_add(0.1, context.window)
            context.window_manager.modal_handler_add(self)
            return {"RUNNING_MODAL"}
        else:
            return {"CANCELLED"}

    def modal(self,context,event):
        #print(event.type)
        if event.type == "ESC":
            context.window_manager.event_timer_remove(self.repeatingTimer)
            self.repeatingTimer = None
            wm.progress_end()
            linkAdded(self)
            bpy.context.scene.update()
            return {"CANCELLED"}
        elif event.type == "TIMER":
            for i in range(1,100):
                self.added.append(addPrim(self.primData[self.currentCube],self))
                self.currentCube+=1
                wm.progress_update(self.currentCube)
                if self.currentCube>=self.primLen-1:
                    context.window_manager.event_timer_remove(self.repeatingTimer)
                    self.repeatingTimer=None
                    linkAdded(self)
                    bpy.context.scene.update()
                    wm.progress_end()
                    return {"FINISHED"}
            print("added "+str(self.currentCube)+"/"+str(self.primLen))
            return {"RUNNING_MODAL"}
        else:
            return {"PASS_THROUGH"}


# add to menu
def menu_func_import(self, context):
    self.layout.operator(ImportMCCD.bl_idname, text="MachineCraft Scene (.mccd)")


def register():
    bpy.utils.register_class(ImportMCCD)
    bpy.types.INFO_MT_file_import.append(menu_func_import)


def unregister():
    bpy.utils.unregister_class(ImportMCCD)
    bpy.types.INFO_MT_file_import.remove(menu_func_import)


if __name__ == "__main__":
    register()
    bpy.ops.mcnimport.scene('INVOKE_DEFAULT')
