# ##### BEGIN GPL LICENSE BLOCK #####
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software Foundation,
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

# ################################################################
# Init
# ################################################################


bl_info = {
    "name": "three.js format",
    "author": "mrdoob, kikko, alteredq, remoe, pxf, n3tfr34k, crobi",
    "version": (1, 5, 0),
    "blender": (2, 7, 0),
    "location": "File > Import-Export",
    "description": "Import-Export three.js meshes",
    "warning": "",
    "wiki_url": "https://github.com/mrdoob/three.js/tree/master/utils/exporters/blender",
    "tracker_url": "https://github.com/mrdoob/three.js/issues",
    "category": "Import-Export"}

# To support reload properly, try to access a package var,
# if it's there, reload everything

import bpy

if "bpy" in locals():
    import imp
    if "export_threejs" in locals():
        imp.reload(export_threejs)
    if "import_threejs" in locals():
        imp.reload(import_threejs)

from bpy.props import *
from bpy_extras.io_utils import ExportHelper, ImportHelper

# ################################################################
# Custom properties
# ################################################################

bpy.types.Object.THREE_castShadow = bpy.props.BoolProperty()
bpy.types.Object.THREE_receiveShadow = bpy.props.BoolProperty()
bpy.types.Object.THREE_doubleSided = bpy.props.BoolProperty()
bpy.types.Object.THREE_exportGeometry = bpy.props.BoolProperty(default = True)
bpy.types.Object.THREE_visible = bpy.props.BoolProperty(default = True)

bpy.types.Material.THREE_useVertexColors = bpy.props.BoolProperty()
bpy.types.Material.THREE_depthWrite = bpy.props.BoolProperty(default = True)
bpy.types.Material.THREE_depthTest = bpy.props.BoolProperty(default = True)

THREE_material_types = [("Basic", "Basic", "Basic"), ("Phong", "Phong", "Phong"), ("Lambert", "Lambert", "Lambert")]
bpy.types.Material.THREE_materialType = EnumProperty(name = "Material type", description = "Material type", items = THREE_material_types, default = "Lambert")

THREE_blending_types = [("NoBlending", "NoBlending", "NoBlending"), ("NormalBlending", "NormalBlending", "NormalBlending"),
                        ("AdditiveBlending", "AdditiveBlending", "AdditiveBlending"), ("SubtractiveBlending", "SubtractiveBlending", "SubtractiveBlending"),
                        ("MultiplyBlending", "MultiplyBlending", "MultiplyBlending"), ("AdditiveAlphaBlending", "AdditiveAlphaBlending", "AdditiveAlphaBlending")]
bpy.types.Material.THREE_blendingType = EnumProperty(name = "Blending type", description = "Blending type", items = THREE_blending_types, default = "NormalBlending")

class OBJECT_PT_hello( bpy.types.Panel ):

    bl_label = "THREE"
    bl_space_type = "PROPERTIES"
    bl_region_type = "WINDOW"
    bl_context = "object"

    def draw(self, context):
        layout = self.layout
        obj = context.object

        row = layout.row()
        row.label(text="Selected object: " + obj.name )

        row = layout.row()
        row.prop( obj, "THREE_exportGeometry", text="Export geometry" )

        row = layout.row()
        row.prop( obj, "THREE_castShadow", text="Casts shadow" )

        row = layout.row()
        row.prop( obj, "THREE_receiveShadow", text="Receives shadow" )

        row = layout.row()
        row.prop( obj, "THREE_doubleSided", text="Double sided" )
        
        row = layout.row()
        row.prop( obj, "THREE_visible", text="Visible" )

class MATERIAL_PT_hello( bpy.types.Panel ):

    bl_label = "THREE"
    bl_space_type = "PROPERTIES"
    bl_region_type = "WINDOW"
    bl_context = "material"

    def draw(self, context):
        layout = self.layout
        mat = context.material

        row = layout.row()
        row.label(text="Selected material: " + mat.name )

        row = layout.row()
        row.prop( mat, "THREE_materialType", text="Material type" )

        row = layout.row()
        row.prop( mat, "THREE_blendingType", text="Blending type" )

        row = layout.row()
        row.prop( mat, "THREE_useVertexColors", text="Use vertex colors" )

        row = layout.row()
        row.prop( mat, "THREE_depthWrite", text="Enable depth writing" )

        row = layout.row()
        row.prop( mat, "THREE_depthTest", text="Enable depth testing" )


# ################################################################
# Importer
# ################################################################

class ImportTHREEJS(bpy.types.Operator, ImportHelper):
    '''Load a Three.js ASCII JSON model'''

    bl_idname = "import.threejs"
    bl_label = "Import Three.js"

    filename_ext = ".js"
    filter_glob = StringProperty(default="*.js", options={'HIDDEN'})

    option_flip_yz = BoolProperty(name="Flip YZ", description="Flip YZ", default=True)
    recalculate_normals = BoolProperty(name="Recalculate normals", description="Recalculate vertex normals", default=True)
    option_worker = BoolProperty(name="Worker", description="Old format using workers", default=False)

    def execute(self, context):
        import io_mesh_threejs.import_threejs
        return io_mesh_threejs.import_threejs.load(self, context, **self.properties)


    def draw(self, context):
        layout = self.layout

        row = layout.row()
        row.prop(self.properties, "option_flip_yz")

        row = layout.row()
        row.prop(self.properties, "recalculate_normals")

        row = layout.row()
        row.prop(self.properties, "option_worker")


# ################################################################
# Exporter - settings
# ################################################################

SETTINGS_FILE_EXPORT = "threejs_settings_export.js"

import os
import json

def file_exists(filename):
    """Return true if file exists and accessible for reading.

    Should be safer than just testing for existence due to links and
    permissions magic on Unix filesystems.

    @rtype: boolean
    """

    try:
        f = open(filename, 'r')
        f.close()
        return True
    except IOError:
        return False

def get_settings_fullpath():
    return os.path.join(bpy.app.tempdir, SETTINGS_FILE_EXPORT)

def save_settings_export(properties):

    settings = {
    "option_export_scene" : properties.option_export_scene,
    "option_embed_meshes" : properties.option_embed_meshes,
    "option_url_base_html" : properties.option_url_base_html,
    "option_copy_textures" : properties.option_copy_textures,

    "option_lights" : properties.option_lights,
    "option_cameras" : properties.option_cameras,

    "option_animation_morph" : properties.option_animation_morph,
    "option_animation_skeletal" : properties.option_animation_skeletal,
    "option_frame_index_as_time" : properties.option_frame_index_as_time,

    "option_frame_step" : properties.option_frame_step,
    "option_all_meshes" : properties.option_all_meshes,

    "option_flip_yz"      : properties.option_flip_yz,

    "option_materials"       : properties.option_materials,
    "option_normals"         : properties.option_normals,
    "option_colors"          : properties.option_colors,
    "option_uv_coords"       : properties.option_uv_coords,
    "option_faces"           : properties.option_faces,
    "option_vertices"        : properties.option_vertices,

    "option_skinning"        : properties.option_skinning,
    "option_bones"           : properties.option_bones,

    "option_vertices_truncate" : properties.option_vertices_truncate,
    "option_scale"        : properties.option_scale,

    "align_model"         : properties.align_model
    }

    fname = get_settings_fullpath()
    f = open(fname, "w")
    json.dump(settings, f)

def restore_settings_export(properties):

    settings = {}

    fname = get_settings_fullpath()
    if file_exists(fname):
        f = open(fname, "r")
        settings = json.load(f)

    properties.option_vertices = settings.get("option_vertices", True)
    properties.option_vertices_truncate = settings.get("option_vertices_truncate", False)
    properties.option_faces = settings.get("option_faces", True)
    properties.option_normals = settings.get("option_normals", True)

    properties.option_colors = settings.get("option_colors", True)
    properties.option_uv_coords = settings.get("option_uv_coords", True)
    properties.option_materials = settings.get("option_materials", True)

    properties.option_skinning = settings.get("option_skinning", True)
    properties.option_bones = settings.get("option_bones", True)

    properties.align_model = settings.get("align_model", "None")

    properties.option_scale = settings.get("option_scale", 1.0)
    properties.option_flip_yz = settings.get("option_flip_yz", True)

    properties.option_export_scene = settings.get("option_export_scene", False)
    properties.option_embed_meshes = settings.get("option_embed_meshes", True)
    properties.option_url_base_html = settings.get("option_url_base_html", False)
    properties.option_copy_textures = settings.get("option_copy_textures", False)

    properties.option_lights = settings.get("option_lights", False)
    properties.option_cameras = settings.get("option_cameras", False)

    properties.option_animation_morph = settings.get("option_animation_morph", False)
    properties.option_animation_skeletal = settings.get("option_animation_skeletal", False)
    properties.option_frame_index_as_time = settings.get("option_frame_index_as_time", False)

    properties.option_frame_step = settings.get("option_frame_step", 1)
    properties.option_all_meshes = settings.get("option_all_meshes", True)

# ################################################################
# Exporter
# ################################################################

class ExportTHREEJS(bpy.types.Operator, ExportHelper):
    '''Export selected object / scene for Three.js (ASCII JSON format).'''

    bl_idname = "export.threejs"
    bl_label = "Export Three.js"

    filename_ext = ".js"

    option_vertices = BoolProperty(name = "Vertices", description = "Export vertices", default = True)
    option_vertices_deltas = BoolProperty(name = "Deltas", description = "Delta vertices", default = False)
    option_vertices_truncate = BoolProperty(name = "Truncate", description = "Truncate vertices", default = False)

    option_faces = BoolProperty(name = "Faces", description = "Export faces", default = True)
    option_faces_deltas = BoolProperty(name = "Deltas", description = "Delta faces", default = False)

    option_normals = BoolProperty(name = "Normals", description = "Export normals", default = True)

    option_colors = BoolProperty(name = "Colors", description = "Export vertex colors", default = True)
    option_uv_coords = BoolProperty(name = "UVs", description = "Export texture coordinates", default = True)
    option_materials = BoolProperty(name = "Materials", description = "Export materials", default = True)

    option_skinning = BoolProperty(name = "Skinning", description = "Export skin data", default = True)
    option_bones = BoolProperty(name = "Bones", description = "Export bones", default = True)

    align_types = [("None","None","None"), ("Center","Center","Center"), ("Bottom","Bottom","Bottom"), ("Top","Top","Top")]
    align_model = EnumProperty(name = "Align model", description = "Align model", items = align_types, default = "None")

    option_scale = FloatProperty(name = "Scale", description = "Scale vertices", min = 0.01, max = 1000.0, soft_min = 0.01, soft_max = 1000.0, default = 1.0)
    option_flip_yz = BoolProperty(name = "Flip YZ", description = "Flip YZ", default = True)

    option_export_scene = BoolProperty(name = "Scene", description = "Export scene", default = False)
    option_embed_meshes = BoolProperty(name = "Embed meshes", description = "Embed meshes", default = True)
    option_copy_textures = BoolProperty(name = "Copy textures", description = "Copy textures", default = False)
    option_url_base_html = BoolProperty(name = "HTML as url base", description = "Use HTML as url base ", default = False)

    option_lights = BoolProperty(name = "Lights", description = "Export default scene lights", default = False)
    option_cameras = BoolProperty(name = "Cameras", description = "Export default scene cameras", default = False)

    option_animation_morph = BoolProperty(name = "Morph animation", description = "Export animation (morphs)", default = False)
    option_animation_skeletal = BoolProperty(name = "Skeletal animation", description = "Export animation (skeletal)", default = False)
    option_frame_index_as_time = BoolProperty(name = "Frame index as time", description = "Use (original) frame index as frame time", default = False)

    option_frame_step = IntProperty(name = "Frame step", description = "Animation frame step", min = 1, max = 1000, soft_min = 1, soft_max = 1000, default = 1)
    option_all_meshes = BoolProperty(name = "All meshes", description = "All meshes (merged)", default = True)

    def invoke(self, context, event):
        restore_settings_export(self.properties)
        return ExportHelper.invoke(self, context, event)

    @classmethod
    def poll(cls, context):
        return context.active_object != None

    def execute(self, context):
        print("Selected: " + context.active_object.name)

        if not self.properties.filepath:
            raise Exception("filename not set")

        save_settings_export(self.properties)

        filepath = self.filepath

        import io_mesh_threejs.export_threejs
        return io_mesh_threejs.export_threejs.save(self, context, **self.properties)

    def draw(self, context):
        layout = self.layout

        row = layout.row()
        row.label(text="Geometry:")

        row = layout.row()
        row.prop(self.properties, "option_vertices")
        # row = layout.row()
        # row.enabled = self.properties.option_vertices
        # row.prop(self.properties, "option_vertices_deltas")
        row.prop(self.properties, "option_vertices_truncate")
        layout.separator()

        row = layout.row()
        row.prop(self.properties, "option_faces")
        row = layout.row()
        row.enabled = self.properties.option_faces
        # row.prop(self.properties, "option_faces_deltas")
        layout.separator()

        row = layout.row()
        row.prop(self.properties, "option_normals")
        layout.separator()

        row = layout.row()
        row.prop(self.properties, "option_bones")
        row.prop(self.properties, "option_skinning")
        layout.separator()

        row = layout.row()
        row.label(text="Materials:")

        row = layout.row()
        row.prop(self.properties, "option_uv_coords")
        row.prop(self.properties, "option_colors")
        row = layout.row()
        row.prop(self.properties, "option_materials")
        layout.separator()

        row = layout.row()
        row.label(text="Settings:")

        row = layout.row()
        row.prop(self.properties, "align_model")
        row = layout.row()
        row.prop(self.properties, "option_flip_yz")
        row.prop(self.properties, "option_scale")
        layout.separator()

        row = layout.row()
        row.label(text="--------- Experimental ---------")
        layout.separator()

        row = layout.row()
        row.label(text="Scene:")

        row = layout.row()
        row.prop(self.properties, "option_export_scene")
        row.prop(self.properties, "option_embed_meshes")

        row = layout.row()
        row.prop(self.properties, "option_lights")
        row.prop(self.properties, "option_cameras")
        layout.separator()

        row = layout.row()
        row.label(text="Animation:")

        row = layout.row()
        row.prop(self.properties, "option_animation_morph")
        row = layout.row()
        row.prop(self.properties, "option_animation_skeletal")
        row = layout.row()
        row.prop(self.properties, "option_frame_index_as_time")
        row = layout.row()
        row.prop(self.properties, "option_frame_step")
        layout.separator()

        row = layout.row()
        row.label(text="Settings:")

        row = layout.row()
        row.prop(self.properties, "option_all_meshes")

        row = layout.row()
        row.prop(self.properties, "option_copy_textures")

        row = layout.row()
        row.prop(self.properties, "option_url_base_html")

        layout.separator()


# ################################################################
# Common
# ################################################################

def menu_func_export(self, context):
    default_path = bpy.data.filepath.replace(".blend", ".js")
    self.layout.operator(ExportTHREEJS.bl_idname, text="Three.js (.js)").filepath = default_path

def menu_func_import(self, context):
    self.layout.operator(ImportTHREEJS.bl_idname, text="Three.js (.js)")

def register():
    bpy.utils.register_module(__name__)
    bpy.types.INFO_MT_file_export.append(menu_func_export)
    bpy.types.INFO_MT_file_import.append(menu_func_import)

def unregister():
    bpy.utils.unregister_module(__name__)
    bpy.types.INFO_MT_file_export.remove(menu_func_export)
    bpy.types.INFO_MT_file_import.remove(menu_func_import)

if __name__ == "__main__":
    register()