#!BPY

"""
Name: 'Sculpteo...'
Blender: 249
Group: 'Export'
Tooltip: 'Upload to Sculpteo'
"""

from builtins import object, range

from past.builtins import cmp
from past.utils import old_div

__author__ = "Vivien Chappelier"
__url__ = ["http://www.sculpteo.com"]
__version__ = "1.0"

__bpydoc__ = """\
This script uploads a design on the Sculpteo website.

Usage:

Select the objects you wish to export and run this script from "File->Export" menu.
Selecting the default options from the popup box will be good in most cases.
All objects that can be represented as a mesh (mesh, curve, metaball, surface, text3d) will be exported.
"""


# ***** BEGIN GPL LICENSE BLOCK *****
#
# Script copyright (C) Vivien Chappelier, Sculpteo 2010
# - based heavily on export_obj by Campbell J Barton
# - V1.22- bspline import/export added (funded by PolyDimensions GmbH)
#
# 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
# ***** END GPL LICENCE BLOCK *****
# --------------------------------------------------------------------------
#
# - stripped unrelevant obj export options
# - added units

import Blender
import BPyMesh
import BPyMessages
import BPyObject
import BPySys
from Blender import Draw, Image, Mesh, Scene, Window, sys


# Returns a tuple - path,extension.
# 'hello.obj' >  ('hello', '.obj')
def splitExt(path):
    dotidx = path.rfind(".")
    if dotidx == -1:
        return path, ""
    else:
        return path[:dotidx], path[dotidx:]


def fixName(name):
    if name == None:
        return "None"
    else:
        return name.replace(" ", "_")


# A Dict of Materials
# (material.name, image.name):matname_imagename # matname_imagename has gaps removed.
MTL_DICT = {}


def write_mtl(filename):

    world = Blender.World.GetCurrent()
    if world:
        worldAmb = world.getAmb()
    else:
        worldAmb = (0, 0, 0)  # Default value

    file = open(filename, "w")
    file.write(
        "# Blender3D MTL File: %s\n"
        % Blender.Get("filename").split("\\")[-1].split("/")[-1]
    )
    file.write("# Material Count: %i\n" % len(MTL_DICT))
    # Write material/image combinations we have used.
    for key, (mtl_mat_name, mat, img) in MTL_DICT.items():

        # Get the Blender data for the material and the image.
        # Having an image named None will make a bug, dont do it :)

        file.write(
            "newmtl %s\n" % mtl_mat_name
        )  # Define a new material: matname_imgname

        if mat:
            file.write(
                "Ns %.6f\n" % ((mat.getHardness() - 1) * 1.9607843137254901)
            )  # Hardness, convert blenders 1-511 to MTL's
            file.write(
                "Ka %.6f %.6f %.6f\n" % tuple([c * mat.amb for c in worldAmb])
            )  # Ambient, uses mirror colour,
            file.write(
                "Kd %.6f %.6f %.6f\n" % tuple([c * mat.ref for c in mat.rgbCol])
            )  # Diffuse
            file.write(
                "Ks %.6f %.6f %.6f\n" % tuple([c * mat.spec for c in mat.specCol])
            )  # Specular
            file.write("Ni %.6f\n" % mat.IOR)  # Refraction index
            file.write("d %.6f\n" % mat.alpha)  # Alpha (obj uses 'd' for dissolve)

            # 0 to disable lighting, 1 for ambient & diffuse only (specular color set to black), 2 for full lighting.
            if mat.getMode() & Blender.Material.Modes["SHADELESS"]:
                file.write("illum 0\n")  # ignore lighting
            elif mat.getSpec() == 0:
                file.write("illum 1\n")  # no specular.
            else:
                file.write("illum 2\n")  # light normaly

        else:
            # write a dummy material here?
            file.write("Ns 0\n")
            file.write(
                "Ka %.6f %.6f %.6f\n" % tuple([c for c in worldAmb])
            )  # Ambient, uses mirror colour,
            file.write("Kd 0.8 0.8 0.8\n")
            file.write("Ks 0.8 0.8 0.8\n")
            file.write("d 1\n")  # No alpha
            file.write("illum 2\n")  # light normaly

        # Write images!
        if img:  # We have an image on the face!
            file.write(
                "map_Kd %s\n" % img.filename.split("\\")[-1].split("/")[-1]
            )  # Diffuse mapping image

        elif mat:  # No face image. if we havea material search for MTex image.
            for mtex in mat.getTextures():
                if mtex and mtex.tex.type == Blender.Texture.Types.IMAGE:
                    try:
                        filename = mtex.tex.image.filename.split("\\")[-1].split("/")[
                            -1
                        ]
                        file.write("map_Kd %s\n" % filename)  # Diffuse mapping image
                        break
                    except:
                        # Texture has no image though its an image type, best ignore.
                        pass

        file.write("\n\n")

    file.close()


def copy_file(source, dest):
    file = open(source, "rb")
    data = file.read()
    file.close()

    file = open(dest, "wb")
    file.write(data)
    file.close()


def copy_images(dest_dir):
    if dest_dir[-1] != sys.sep:
        dest_dir += sys.sep

    # Get unique image names
    uniqueImages = {}
    for matname, mat, image in MTL_DICT.values():  # Only use image name
        # Get Texface images
        if image:
            uniqueImages[
                image
            ] = image  # Should use sets here. wait until Python 2.4 is default.

        # Get MTex images
        if mat:
            for mtex in mat.getTextures():
                if mtex and mtex.tex.type == Blender.Texture.Types.IMAGE:
                    image_tex = mtex.tex.image
                    if image_tex:
                        try:
                            uniqueImages[image_tex] = image_tex
                        except:
                            pass

    # Now copy images
    copyCount = 0

    for bImage in uniqueImages.values():
        image_path = sys.expandpath(bImage.filename)
        if sys.exists(image_path):
            # Make a name for the target path.
            dest_image_path = dest_dir + image_path.split("\\")[-1].split("/")[-1]
            if not sys.exists(dest_image_path):  # Image isnt alredy there
                print('\tCopying "%s" > "%s"' % (image_path, dest_image_path))
                copy_file(image_path, dest_image_path)
                copyCount += 1
    print("\tCopied %d images" % copyCount)


def write(
    filename,
    objects,
    EXPORT_TRI=False,
    EXPORT_UV=True,
    EXPORT_MTL=True,
    EXPORT_APPLY_MODIFIERS=True,
    EXPORT_UNIT=1.0,
):
    """
    Basic write function. The context and options must be alredy set
    This can be accessed externaly
    eg.
    write( 'c:\\test\\foobar.obj', Blender.Object.GetSelected() ) # Using default options.
    """

    def veckey3d(v):
        return round(v.x, 6), round(v.y, 6), round(v.z, 6)

    def veckey2d(v):
        return round(v.x, 6), round(v.y, 6)

    def findVertexGroupName(face, vWeightMap):
        """
        Searches the vertexDict to see what groups is assigned to a given face.
        We use a frequency system in order to sort out the name because a given vetex can
        belong to two or more groups at the same time. To find the right name for the face
        we list all the possible vertex group names with their frequency and then sort by
        frequency in descend order. The top element is the one shared by the highest number
        of vertices is the face's group
        """
        weightDict = {}
        for vert in face:
            vWeights = vWeightMap[vert.index]
            for vGroupName, weight in vWeights:
                weightDict[vGroupName] = weightDict.get(vGroupName, 0) + weight

        if weightDict:
            alist = [
                (weight, vGroupName) for vGroupName, weight in weightDict.items()
            ]  # sort least to greatest amount of weight
            alist.sort()
            return alist[-1][1]  # highest value last
        else:
            return "(null)"

    print('OBJ Export path: "%s"' % filename)
    temp_mesh_name = "~tmp-mesh"

    time1 = sys.time()
    scn = Scene.GetCurrent()

    file = open(filename, "w")

    # Write Header
    file.write(
        "# Blender3D v%s OBJ File: %s\n"
        % (
            Blender.Get("version"),
            Blender.Get("filename").split("/")[-1].split("\\")[-1],
        )
    )
    file.write("# www.blender3d.org\n")

    # Tell the obj file what material file to use.
    if EXPORT_MTL:
        mtlfilename = "%s.mtl" % ".".join(filename.split(".")[:-1])
        file.write("mtllib %s\n" % (mtlfilename.split("\\")[-1].split("/")[-1]))

    # Get the container mesh. - used for applying modifiers and non mesh objects.
    containerMesh = meshName = tempMesh = None
    for meshName in Blender.NMesh.GetNames():
        if meshName.startswith(temp_mesh_name):
            tempMesh = Mesh.Get(meshName)
            if not tempMesh.users:
                containerMesh = tempMesh
    if not containerMesh:
        containerMesh = Mesh.New(temp_mesh_name)

    del meshName
    del tempMesh

    # Initialize totals, these are updated each object
    totverts = totuvco = totno = 1

    face_vert_index = 1

    globalNormals = {}

    # Get all meshes
    for ob_main in objects:
        for ob, ob_mat in BPyObject.getDerivedObjects(ob_main):

            # Will work for non meshes now! :)
            # getMeshFromObject(ob, container_mesh=None, apply_modifiers=True, vgroups=True, scn=None)
            me = BPyMesh.getMeshFromObject(
                ob, containerMesh, EXPORT_APPLY_MODIFIERS, False, scn
            )
            if not me:
                continue

            if EXPORT_UV:
                faceuv = me.faceUV
            else:
                faceuv = False

            # We have a valid mesh
            if EXPORT_TRI and me.faces:
                # Add a dummy object to it.
                has_quads = False
                for f in me.faces:
                    if len(f) == 4:
                        has_quads = True
                        break

                if has_quads:
                    oldmode = Mesh.Mode()
                    Mesh.Mode(Mesh.SelectModes["FACE"])

                    me.sel = True
                    tempob = scn.objects.new(me)
                    me.quadToTriangle(0)  # more=0 shortest length
                    oldmode = Mesh.Mode(oldmode)
                    scn.objects.unlink(tempob)

                    Mesh.Mode(oldmode)

            # Make our own list so it can be sorted to reduce context switching
            faces = [f for f in me.faces]

            edges = []

            if not (
                len(faces) + len(edges) + len(me.verts)
            ):  # Make sure there is somthing to write
                continue  # dont bother with this mesh.

            me.transform(ob_mat)

            # # Crash Blender
            # materials = me.getMaterials(1) # 1 == will return None in the list.
            materials = me.materials

            materialNames = []
            materialItems = materials[:]
            if materials:
                for mat in materials:
                    if mat:  # !=None
                        materialNames.append(mat.name)
                    else:
                        materialNames.append(None)
                # Cant use LC because some materials are None.
                # materialNames = map(lambda mat: mat.name, materials) # Bug Blender, dosent account for null materials, still broken.

            # Possible there null materials, will mess up indicies
            # but at least it will export, wait until Blender gets fixed.
            materialNames.extend((16 - len(materialNames)) * [None])
            materialItems.extend((16 - len(materialItems)) * [None])

            # Sort by Material, then images
            # so we dont over context switch in the obj file.
            if faceuv:
                try:
                    faces.sort(key=lambda a: (a.mat, a.image, a.smooth))
                except:
                    faces.sort(
                        lambda a, b: cmp(
                            (a.mat, a.image, a.smooth), (b.mat, b.image, b.smooth)
                        )
                    )
            elif len(materials) > 1:
                try:
                    faces.sort(key=lambda a: (a.mat, a.smooth))
                except:
                    faces.sort(lambda a, b: cmp((a.mat, a.smooth), (b.mat, b.smooth)))
            else:
                # no materials
                try:
                    faces.sort(key=lambda a: a.smooth)
                except:
                    faces.sort(lambda a, b: cmp(a.smooth, b.smooth))

            # Set the default mat to no material and no image.
            contextMat = (
                0,
                0,
            )  # Can never be this, so we will label a new material teh first chance we get.
            contextSmooth = None  # Will either be true or false,  set bad to force initialization switch.

            name1 = ob.name
            name2 = ob.getData(1)
            if name1 == name2:
                obnamestring = fixName(name1)
            else:
                obnamestring = "%s_%s" % (fixName(name1), fixName(name2))

            file.write("o %s\n" % obnamestring)  # Write Object name

            # Vert
            for v in me.verts:
                vs = (
                    v.co[0] * EXPORT_UNIT,
                    v.co[1] * EXPORT_UNIT,
                    v.co[2] * EXPORT_UNIT,
                )
                file.write("v %.6f %.6f %.6f\n" % tuple(vs))

            # UV
            if faceuv:
                uv_face_mapping = [
                    [0, 0, 0, 0] for f in faces
                ]  # a bit of a waste for tri's :/

                uv_dict = {}  # could use a set() here
                for f_index, f in enumerate(faces):

                    for uv_index, uv in enumerate(f.uv):
                        uvkey = veckey2d(uv)
                        try:
                            uv_face_mapping[f_index][uv_index] = uv_dict[uvkey]
                        except:
                            uv_face_mapping[f_index][uv_index] = uv_dict[uvkey] = len(
                                uv_dict
                            )
                            file.write("vt %.6f %.6f\n" % tuple(uv))

                uv_unique_count = len(uv_dict)
                del uv, uvkey, uv_dict, f_index, uv_index
                # Only need uv_unique_count and uv_face_mapping

            if not faceuv:
                f_image = None

            for f_index, f in enumerate(faces):
                f_v = f.v
                f_smooth = f.smooth
                f_mat = min(f.mat, len(materialNames) - 1)
                if faceuv:
                    f_image = f.image
                    f_uv = f.uv

                # MAKE KEY
                if faceuv and f_image:  # Object is always true.
                    key = materialNames[f_mat], f_image.name
                else:
                    key = materialNames[f_mat], None  # No image, use None instead.

                # CHECK FOR CONTEXT SWITCH
                if key == contextMat:
                    pass  # Context alredy switched, dont do anything
                else:
                    if key[0] == None and key[1] == None:
                        # Write a null material, since we know the context has changed.
                        file.write("usemtl (null)\n")  # mat, image

                    else:
                        mat_data = MTL_DICT.get(key)
                        if not mat_data:
                            # First add to global dict so we can export to mtl
                            # Then write mtl

                            # Make a new names from the mat and image name,
                            # converting any spaces to underscores with fixName.

                            # If none image dont bother adding it to the name
                            if key[1] == None:
                                mat_data = MTL_DICT[key] = (
                                    ("%s" % fixName(key[0])),
                                    materialItems[f_mat],
                                    f_image,
                                )
                            else:
                                mat_data = MTL_DICT[key] = (
                                    ("%s_%s" % (fixName(key[0]), fixName(key[1]))),
                                    materialItems[f_mat],
                                    f_image,
                                )

                        file.write(
                            "usemtl %s\n" % mat_data[0]
                        )  # can be mat_image or (null)

                contextMat = key
                if f_smooth != contextSmooth:
                    if f_smooth:  # on now off
                        file.write("s 1\n")
                        contextSmooth = f_smooth
                    else:  # was off now on
                        file.write("s off\n")
                        contextSmooth = f_smooth

                file.write("f")
                if faceuv:
                    for vi, v in enumerate(f_v):
                        file.write(
                            " %d/%d"
                            % (
                                v.index + totverts,
                                totuvco + uv_face_mapping[f_index][vi],
                            )
                        )  # vert, uv

                    face_vert_index += len(f_v)

                else:  # No UV's
                    for v in f_v:
                        file.write(" %d" % (v.index + totverts))

                file.write("\n")

            # Make the indicies global rather then per mesh
            totverts += len(me.verts)
            if faceuv:
                totuvco += uv_unique_count
            me.verts = None
    file.close()

    # Now we have all our materials, save them
    if EXPORT_MTL:
        write_mtl(mtlfilename)
    if True:
        dest_dir = filename
        # Remove chars until we are just the path.
        while dest_dir and dest_dir[-1] not in "\\/":
            dest_dir = dest_dir[:-1]
        if dest_dir:
            copy_images(dest_dir)
        else:
            print(
                '\tError: "%s" could not be used as a base for an image path.'
                % filename
            )

    print("OBJ Export time: %.2f" % (sys.time() - time1))


def write_ui(filename):

    if not filename.lower().endswith(".obj"):
        filename += ".obj"

    if not BPyMessages.Warning_SaveOver(filename):
        return

    global EXPORT_APPLY_MODIFIERS, EXPORT_TRI, EXPORT_UV, EXPORT_MTL, EXPORT_SEL_ONLY, EXPORT_ALL_SCENES, EXPORT_ANIMATION, EXPORT_UNIT_MM, EXPORT_UNIT_CM, EXPORT_UNIT_M, EXPORT_UNIT_IN, EXPORT_UNIT_FT, EXPORT_UNIT_YD

    EXPORT_APPLY_MODIFIERS = Draw.Create(1)
    EXPORT_TRI = Draw.Create(0)
    EXPORT_UV = Draw.Create(1)
    EXPORT_MTL = Draw.Create(1)
    EXPORT_SEL_ONLY = Draw.Create(0)
    EXPORT_ALL_SCENES = Draw.Create(1)
    EXPORT_ANIMATION = Draw.Create(0)

    EXPORT_UNIT_MM = Draw.Create(0)
    EXPORT_UNIT_CM = Draw.Create(1)
    EXPORT_UNIT_M = Draw.Create(0)
    EXPORT_UNIT_IN = Draw.Create(0)
    EXPORT_UNIT_FT = Draw.Create(0)
    EXPORT_UNIT_YD = Draw.Create(0)

    # BEGIN ALTERNATIVE UI *******************
    if True:

        EVENT_NONE = 0
        EVENT_EXIT = 1
        EVENT_REDRAW = 2
        EVENT_EXPORT = 3

        UNIT_MM = 1
        UNIT_CM = 2
        UNIT_M = 3
        UNIT_IN = 4
        UNIT_FT = 5
        UNIT_YD = 6

        GLOBALS = {}
        GLOBALS["EVENT"] = EVENT_REDRAW
        # GLOBALS['MOUSE'] = Window.GetMouseCoords()
        GLOBALS["MOUSE"] = [old_div(i, 2) for i in Window.GetScreenSize()]
        GLOBALS["UNIT"] = UNIT_CM

        def obj_ui_set_event(e, v):
            GLOBALS["EVENT"] = e

        def do_help(e, v):
            url = __url__[0]
            print("Trying to open web browser with documentation at this address...")
            print("\t" + url)

            try:
                import webbrowser

                webbrowser.open(url)
            except:
                print("...could not open a browser window.")

        def do_units(e, v):
            global EXPORT_UNIT_MM, EXPORT_UNIT_CM, EXPORT_UNIT_M, EXPORT_UNIT_FT, EXPORT_UNIT_YD
            GLOBALS["UNIT"] = e
            EXPORT_UNIT_MM.val = (
                EXPORT_UNIT_CM.val
            ) = (
                EXPORT_UNIT_M.val
            ) = EXPORT_UNIT_IN.val = EXPORT_UNIT_FT.val = EXPORT_UNIT_YD.val = 0
            if e == UNIT_MM:
                EXPORT_UNIT_MM.val = 1
            elif e == UNIT_CM:
                EXPORT_UNIT_CM.val = 1
            elif e == UNIT_M:
                EXPORT_UNIT_M.val = 1
            elif e == UNIT_IN:
                EXPORT_UNIT_IN.val = 1
            elif e == UNIT_FT:
                EXPORT_UNIT_FT.val = 1
            elif e == UNIT_YD:
                EXPORT_UNIT_YD.val = 1

        def obj_ui():
            ui_x, ui_y = GLOBALS["MOUSE"]

            # Center based on overall pup size
            ui_x -= 165
            ui_y -= 140

            global EXPORT_APPLY_MODIFIERS, EXPORT_TRI, EXPORT_UV, EXPORT_MTL, EXPORT_SEL_ONLY, EXPORT_ALL_SCENES, EXPORT_ANIMATION, EXPORT_UNIT_MM, EXPORT_UNIT_CM, EXPORT_UNIT_M, EXPORT_UNIT_FT, EXPORT_UNIT_IN, EXPORT_UNIT_YD

            Draw.Label("Context...", ui_x + 9, ui_y + 239, 220, 20)
            Draw.BeginAlign()
            EXPORT_SEL_ONLY = Draw.Toggle(
                "Selection Only",
                EVENT_NONE,
                ui_x + 9,
                ui_y + 219,
                110,
                20,
                EXPORT_SEL_ONLY.val,
                "Only export objects in visible selection. Else export whole scene.",
            )
            EXPORT_ALL_SCENES = Draw.Toggle(
                "All Scenes",
                EVENT_NONE,
                ui_x + 119,
                ui_y + 219,
                110,
                20,
                EXPORT_ALL_SCENES.val,
                "Each scene as a separate OBJ file.",
            )
            EXPORT_ANIMATION = Draw.Toggle(
                "Animation",
                EVENT_NONE,
                ui_x + 229,
                ui_y + 219,
                110,
                20,
                EXPORT_ANIMATION.val,
                "Each frame as a numbered OBJ file.",
            )
            Draw.EndAlign()

            Draw.Label("Output Options...", ui_x + 9, ui_y + 189, 220, 20)
            Draw.BeginAlign()
            EXPORT_APPLY_MODIFIERS = Draw.Toggle(
                "Apply Modifiers",
                EVENT_REDRAW,
                ui_x + 9,
                ui_y + 170,
                110,
                20,
                EXPORT_APPLY_MODIFIERS.val,
                "Use transformed mesh data from each object. May break vert order for morph targets.",
            )
            EXPORT_TRI = Draw.Toggle(
                "Triangulate",
                EVENT_NONE,
                ui_x + 119,
                ui_y + 170,
                70,
                20,
                EXPORT_TRI.val,
                "Triangulate quads.",
            )
            Draw.EndAlign()

            Draw.Label("Export...", ui_x + 9, ui_y + 139, 220, 20)
            Draw.BeginAlign()
            EXPORT_MTL = Draw.Toggle(
                "Materials",
                EVENT_NONE,
                ui_x + 9,
                ui_y + 120,
                70,
                20,
                EXPORT_MTL.val,
                "Write a separate MTL file with the OBJ.",
            )
            EXPORT_UV = Draw.Toggle(
                "UVs",
                EVENT_NONE,
                ui_x + 79,
                ui_y + 120,
                31,
                20,
                EXPORT_UV.val,
                "Export texface UV coords.",
            )
            Draw.EndAlign()

            Draw.Label("Dimensions...", ui_x + 9, ui_y + 89, 220, 20)
            Draw.BeginAlign()
            EXPORT_UNIT_MM = Draw.Toggle(
                "mm",
                UNIT_MM,
                ui_x + 9,
                ui_y + 70,
                30,
                20,
                EXPORT_UNIT_MM.val,
                "millimeter",
                do_units,
            )
            EXPORT_UNIT_CM = Draw.Toggle(
                "cm",
                UNIT_CM,
                ui_x + 39,
                ui_y + 70,
                30,
                20,
                EXPORT_UNIT_CM.val,
                "centimeter",
                do_units,
            )
            EXPORT_UNIT_M = Draw.Toggle(
                "m",
                UNIT_M,
                ui_x + 69,
                ui_y + 70,
                30,
                20,
                EXPORT_UNIT_M.val,
                "meter",
                do_units,
            )
            EXPORT_UNIT_IN = Draw.Toggle(
                "in",
                UNIT_IN,
                ui_x + 99,
                ui_y + 70,
                30,
                20,
                EXPORT_UNIT_IN.val,
                "inch",
                do_units,
            )
            EXPORT_UNIT_FT = Draw.Toggle(
                "ft",
                UNIT_FT,
                ui_x + 129,
                ui_y + 70,
                30,
                20,
                EXPORT_UNIT_FT.val,
                "foot",
                do_units,
            )
            EXPORT_UNIT_YD = Draw.Toggle(
                "yd",
                UNIT_YD,
                ui_x + 159,
                ui_y + 70,
                30,
                20,
                EXPORT_UNIT_YD.val,
                "yard",
                do_units,
            )
            Draw.EndAlign()

            Draw.BeginAlign()
            Draw.PushButton(
                "Online Help",
                EVENT_REDRAW,
                ui_x + 9,
                ui_y + 9,
                110,
                20,
                "Load the wiki page for this script",
                do_help,
            )
            Draw.PushButton(
                "Cancel",
                EVENT_EXIT,
                ui_x + 119,
                ui_y + 9,
                110,
                20,
                "",
                obj_ui_set_event,
            )
            Draw.PushButton(
                "Export",
                EVENT_EXPORT,
                ui_x + 229,
                ui_y + 9,
                110,
                20,
                "Export with these settings",
                obj_ui_set_event,
            )
            Draw.EndAlign()

        # hack so the toggle buttons redraw. this is not nice at all
        while GLOBALS["EVENT"] not in (EVENT_EXIT, EVENT_EXPORT):
            Draw.UIBlock(obj_ui, 0)

        if GLOBALS["EVENT"] != EVENT_EXPORT:
            return False

    # END ALTERNATIVE UI *********************

    Window.EditMode(0)
    Window.WaitCursor(1)

    EXPORT_APPLY_MODIFIERS = EXPORT_APPLY_MODIFIERS.val
    EXPORT_TRI = EXPORT_TRI.val
    EXPORT_UV = EXPORT_UV.val
    EXPORT_MTL = EXPORT_MTL.val
    EXPORT_SEL_ONLY = EXPORT_SEL_ONLY.val
    EXPORT_ALL_SCENES = EXPORT_ALL_SCENES.val
    EXPORT_ANIMATION = EXPORT_ANIMATION.val

    EXPORT_UNIT_MM = EXPORT_UNIT_MM.val
    EXPORT_UNIT_CM = EXPORT_UNIT_CM.val
    EXPORT_UNIT_M = EXPORT_UNIT_M.val
    EXPORT_UNIT_IN = EXPORT_UNIT_IN.val
    EXPORT_UNIT_FT = EXPORT_UNIT_FT.val
    EXPORT_UNIT_YD = EXPORT_UNIT_YD.val

    EXPORT_UNIT = 1.0
    if EXPORT_UNIT_MM:
        EXPORT_UNIT = 0.1
    if EXPORT_UNIT_M:
        EXPORT_UNIT = 100
    if EXPORT_UNIT_IN:
        EXPORT_UNIT = 2.54
    if EXPORT_UNIT_FT:
        EXPORT_UNIT = 30.48
    if EXPORT_UNIT_YD:
        EXPORT_UNIT = 91.44

    base_name, ext = splitExt(filename)
    context_name = [
        base_name,
        "",
        "",
        ext,
    ]  # basename, scene_name, framenumber, extension

    # Use the options to export the data using write()
    # def write(filename, objects, EXPORT_MTL=True, EXPORT_APPLY_MODIFIERS=True):
    orig_scene = Scene.GetCurrent()
    if EXPORT_ALL_SCENES:
        export_scenes = Scene.Get()
    else:
        export_scenes = [orig_scene]

    # Export all scenes.
    for scn in export_scenes:
        scn.makeCurrent()  # If alredy current, this is not slow.
        context = scn.getRenderingContext()
        orig_frame = Blender.Get("curframe")

        if EXPORT_ALL_SCENES:  # Add scene name into the context_name
            context_name[1] = "_%s" % BPySys.cleanName(
                scn.name
            )  # WARNING, its possible that this could cause a collision. we could fix if were feeling parranoied.

        # Export an animation?
        if EXPORT_ANIMATION:
            scene_frames = range(
                context.startFrame(), context.endFrame() + 1
            )  # up to and including the end frame.
        else:
            scene_frames = [orig_frame]  # Dont export an animation.

        # Loop through all frames in the scene and export.
        for frame in scene_frames:
            if EXPORT_ANIMATION:  # Add frame to the filename.
                context_name[2] = "_%.6d" % frame

            Blender.Set("curframe", frame)
            if EXPORT_SEL_ONLY:
                export_objects = scn.objects.context
            else:
                export_objects = scn.objects

            full_path = "".join(context_name)

            # erm... bit of a problem here, this can overwrite files when exporting frames. not too bad.
            # EXPORT THE FILE.
            write(
                full_path,
                export_objects,
                EXPORT_TRI,
                EXPORT_UV,
                EXPORT_MTL,
                EXPORT_APPLY_MODIFIERS,
                EXPORT_UNIT,
            )

        Blender.Set("curframe", orig_frame)

    # Restore old active scene.
    orig_scene.makeCurrent()
    Window.WaitCursor(0)
    return True


# workaround a runtime error message caused by uuid using ctypes on windows
# see http://groups.google.com/group/pyinstaller/browse_thread/thread/a2de39dda63c1508 for details
from sys import platform

if platform == "win32":
    try:
        import ctypes
        import ctypes.util

        ctypes.util.find_library = None
    except:
        pass
import http.cookiejar
import mimetypes
import os
import stat
import tempfile
import urllib.error
import urllib.parse
import urllib.request
import webbrowser
from shutil import rmtree
from uuid import uuid1
from zipfile import ZipFile

import mimetools


# MultipartPostHandler
####
# 02/2006 Will Holcomb <wholcomb@gmail.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
#
class Callable(object):
    def __init__(self, anycallable):
        self.__call__ = anycallable


# Controls how sequences are uncoded. If true, elements may be given
#  multiple values by assigning a sequence.
doseq = 1


class MultipartPostHandler(urllib.request.BaseHandler):
    handler_order = urllib.request.HTTPHandler.handler_order - 10  # needs to run first

    def http_request(self, request):
        data = request.get_data()
        if data is not None and type(data) != str:
            v_files = []
            v_vars = []
            try:
                for (key, value) in list(data.items()):
                    from io import IOBase

                    if isinstance(value, IOBase):
                        v_files.append((key, value))
                    else:
                        v_vars.append((key, value))
            except TypeError:
                systype, value, traceback = sys.exc_info()
                raise TypeError(
                    "not a valid non-string sequence or mapping object"
                ).with_traceback(traceback)

            if len(v_files) == 0:
                data = urllib.parse.urlencode(v_vars, doseq)
            else:
                boundary, data = self.multipart_encode(v_vars, v_files)
                contenttype = "multipart/form-data; boundary=%s" % boundary
                if (
                    request.has_header("Content-Type")
                    and request.get_header("Content-Type").find("multipart/form-data")
                    != 0
                ):
                    print(
                        "Replacing %s with %s"
                        % (request.get_header("content-type"), "multipart/form-data")
                    )
                request.add_unredirected_header("Content-Type", contenttype)

            request.add_data(data)
        return request

    def multipart_encode(vars, files, boundary=None, buffer=None):
        if boundary is None:
            boundary = mimetools.choose_boundary()
        if buffer is None:
            buffer = ""
        for (key, value) in vars:
            buffer += "--%s\r\n" % boundary
            buffer += 'Content-Disposition: form-data; name="%s"' % key
            buffer += "\r\n\r\n" + value + "\r\n"
        for (key, fd) in files:
            file_size = os.fstat(fd.fileno())[stat.ST_SIZE]
            filename = os.path.basename(fd.name)
            contenttype = (
                mimetypes.guess_type(filename)[0] or "application/octet-stream"
            )
            buffer += "--%s\r\n" % boundary
            buffer += 'Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (
                key,
                filename,
            )
            buffer += "Content-Type: %s\r\n" % contenttype
            # buffer += 'Content-Length: %s\r\n' % file_size
            fd.seek(0)
            buffer += "\r\n" + fd.read() + "\r\n"
        buffer += "--%s--\r\n\r\n" % boundary
        return boundary, buffer

    multipart_encode = Callable(multipart_encode)

    https_request = http_request


cookies = http.cookiejar.CookieJar()
opener = urllib.request.build_opener(
    urllib.request.HTTPCookieProcessor(cookies), MultipartPostHandler
)
urllib.request.install_opener(opener)

# upload
####

SCULPTEO_SERVER = "http://www.sculpteo.com"
# SCULPTEO_SERVER = 'http://localhost:8000'
SCULPTEO_UPLOADFILE = "gallery/uploadfile"
SCULPTEO_UPLOAD = "gallery/3D/upload"


def post_design(filename, name, server, lang="en"):
    uuid = uuid1().int

    # post the file
    upload_post = {"file": open(filename, "rb")}
    upload_url = "%s/%s/%s/?X-Progress-ID=%s" % (
        server,
        lang,
        SCULPTEO_UPLOADFILE,
        uuid,
    )

    print("POST %s arg %s" % (upload_url, upload_post))
    page = urllib.request.urlopen(upload_url, upload_post)

    # redirect to upload page to add name, description, etc...
    webbrowser.open_new(
        "%s/%s/%s/?X-Progress-ID=%s&name=%s"
        % (server, lang, SCULPTEO_UPLOAD, uuid, name)
    )


if __name__ == "__main__":
    scenename = Scene.GetCurrent().getName()

    # export to obj
    filename = "upload.obj"
    subdir = tempfile.mkdtemp()
    try:
        tmpfile = os.path.join(subdir, filename)
        if write_ui(tmpfile):
            # zip the directory
            zipfd, zipfilename = tempfile.mkstemp(".zip")
            try:
                zipfile = ZipFile(zipfilename, mode="w")
                for name in os.listdir(subdir):
                    zipfile.write(os.path.join(subdir, name), arcname=name)
                zipfile.close()
                os.close(zipfd)

                # send it
                post_design(zipfilename, scenename, SCULPTEO_SERVER)
            finally:
                os.unlink(zipfilename)
    finally:
        rmtree(subdir)
