# ***** 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
# ***** END GPL LICENCE BLOCK *****

#TODO:

# Set the command name so that DarkRadiant recognises this file
__commandName__ = 'aseExport'
__commandDisplayName__ = 'Export ASE...'

# The actual algorithm called by DarkRadiant is contained in the execute() function
def execute():
    script = "Dark Radiant ASCII Scene Export (*.ase)"
    author = "Richard Bartlett, some additions by greebo and tels"
    version = "0.9"

    import darkradiant as dr

    # Check if we have a valid selection

    selectionInfo = GlobalSelectionSystem.getSelectionInfo()

    # Don't allow empty selections or selected components only
    if selectionInfo.totalCount == 0 or selectionInfo.totalCount == selectionInfo.componentCount:
        errMsg = GlobalDialogManager.createMessageBox('No selection', 'Nothing selected, cannot run exporter.', dr.Dialog.ERROR)
        errMsg.run()
        return

    shaderlist = []
    geomlist = []

    # simple linear triangulation of n-sided poly

    def triangulate(pointset):
        tris = []
        for count in range(1, len(pointset) - 1):
            tris.append([pointset[0], pointset[count], pointset[count + 1]])
        return tris

    # skin patch matrix with tris

    def skinmatrix(pointset, width, height):
        tris = []
        for h in range(height-1):
            for w in range(width-1):
                tris.append([pointset[w+(h*width)], pointset[w+1+(h*width)], pointset[w+width+(h*width)]])
                tris.append([pointset[w+1+(h*width)], pointset[w+1+width+(h*width)], pointset[w+width+(h*width)]])
        return tris

    def processBrush(brushnode):
        verts = []
        faces = []

        numfaces = brushnode.getNumFaces()
        for index in range(numfaces):
            facenode = brushnode.getFace(index)
            shader = facenode.getShader()

            # Tels: skip if caulk and no caulk should be exported
            if (shader == 'textures/common/caulk') and (int(GlobalRegistry.get('user/scripts/aseExport/exportcaulk'))) == 0:
                continue

            if not shader in shaderlist:
                shaderlist.append(shader)
            winding = facenode.getWinding()
            tris = triangulate([x+len(verts) for x in range(len(winding))])
            for x in tris:
                x.append(shaderlist.index(shader))
                faces.append(x)
            for x in reversed(winding):
                verts.append([x.vertex.x(), x.vertex.y(), x.vertex.z(), x.texcoord.x(), x.texcoord.y() * -1, x.normal.x(), x.normal.y(), x.normal.z()])

        geomlist.append([verts, faces])
        return

    def processPatch(patchnode):
        verts = []
        faces = []

        shader = patchnode.getShader()

        # Tels: skip if caulk and no caulk should be exported
        if shader == 'textures/common/caulk' and int(GlobalRegistry.get('user/scripts/aseExport/exportcaulk')) == 0:
            return

        if not shader in shaderlist:
            shaderlist.append(shader)
        mesh = patchnode.getTesselatedPatchMesh()
        for x in mesh.vertices:
            verts.append([x.vertex.x(), x.vertex.y(), x.vertex.z(), x.texcoord.x(), x.texcoord.y() * -1, x.normal.x(), x.normal.y(), x.normal.z()])
        tris = skinmatrix([x for x in range(len(verts))], mesh.width, mesh.height)
        for x in tris:
            x.append(shaderlist.index(shader))
            faces.append(x)

        geomlist.append([verts, faces])
        return

    # Branch on primitive nodes
    def processPrimitive(scenenode):
        if scenenode.isBrush():
            processBrush(scenenode.getBrush())
        elif scenenode.isPatch():
            processPatch(scenenode.getPatch())
        return

    # Traversor class to visit child primitives of entities
    class nodeVisitor(dr.SceneNodeVisitor):
        def pre(self, scenenode):
            # Brush?
            if scenenode.isBrush():
                processBrush(scenenode.getBrush())
            # Patch?
            elif scenenode.isPatch():
                processPatch(scenenode.getPatch())

            # Traverse all child nodes, regardless of type
            return 1

    class dataCollector(dr.SelectionVisitor):
        def visit(self, scenenode):

            if scenenode.isBrush() or scenenode.isPatch():
                processPrimitive(scenenode)
            elif scenenode.isEntity():
                # greebo: Found an entity, this could be a func_static or similar
                # Traverse children of this entity using a new walker
                nodewalker = nodeVisitor()
                scenenode.traverse(nodewalker)
            else:
                print('WARNING: unsupported node type selected. Skipping: ' + scenenode.getNodeType())

    # Dialog
    dialog = GlobalDialogManager.createDialog(script + 'v' + version)

    # Add an entry box and remember the handle
    fileHandle = dialog.addEntryBox("Filename:")
    dialog.setElementValue(fileHandle, GlobalRegistry.get('user/scripts/aseExport/recentFilename'))

    # Add an entry box and remember the handle
    pathHandle = dialog.addPathEntry("Save path:", True)
    dialog.setElementValue(pathHandle, GlobalRegistry.get('user/scripts/aseExport/recentPath'))

    # Add a checkbox
    centerObjectsHandle = dialog.addCheckbox("Center objects at 0,0,0 origin")
    dialog.setElementValue(centerObjectsHandle, GlobalRegistry.get('user/scripts/aseExport/centerObjects'))

    # Add another checkbox
    exportCaulkHandle = dialog.addCheckbox("Export caulked faces")
    dialog.setElementValue(exportCaulkHandle, GlobalRegistry.get('user/scripts/aseExport/exportcaulk'))

    if dialog.run() == dr.Dialog.OK:
        fullpath = dialog.getElementValue(pathHandle) + '/' + dialog.getElementValue(fileHandle)
        if not fullpath.endswith('.ase'):
            fullpath = fullpath + '.ase'

        # Save the path for later use
        GlobalRegistry.set('user/scripts/aseExport/recentFilename', dialog.getElementValue(fileHandle))
        GlobalRegistry.set('user/scripts/aseExport/recentPath', dialog.getElementValue(pathHandle))
        GlobalRegistry.set('user/scripts/aseExport/centerObjects', dialog.getElementValue(centerObjectsHandle))
        GlobalRegistry.set('user/scripts/aseExport/exportcaulk', dialog.getElementValue(exportCaulkHandle))

        try:
            file = open(fullpath, 'r')
            file.close()
            prompt = GlobalDialogManager.createMessageBox('Warning', 'The file ' + fullpath + ' already exists. Do you wish to overwrite it?', dr.Dialog.ASK)
            if prompt.run() == dr.Dialog.YES:
                overwrite = True
            else:
                overwrite = False
        except IOError:
            overwrite = True

        if overwrite:

            # Tels: Only collect the data if we are going to export it
            walker = dataCollector()
            GlobalSelectionSystem.foreachSelected(walker)

            # greebo: Check if we should center objects at the 0,0,0 origin
            if int(dialog.getElementValue(centerObjectsHandle)) == 1:
                #center objects at 0,0,0
                xlist = []
                ylist = []
                zlist = []
                for item in geomlist:
                    for vert in item[0]:
                        xlist.append(vert[0])
                        ylist.append(vert[1])
                        zlist.append(vert[2])
                xcenter=(max(xlist)+min(xlist))/2
                ycenter=(max(ylist)+min(ylist))/2
                zcenter=(max(zlist)+min(zlist))/2
                for item in geomlist:
                    for vert in item[0]:
                        vert[0] = vert[0] - xcenter
                        vert[1] = vert[1] - ycenter
                        vert[2] = vert[2] - zcenter

            # split objects that do not share the same texture on all faces
            newgeomlist = []
            for x in geomlist:
                texlist = []
                for data in x[1]:
                    texlist.append(data[3])
                if len(set(texlist)) > 1:
                    temp = []
                    for texture in set(texlist):
                        vertlist = []
                        facelist = []
                        for data in x[1]:
                            if data[3] == texture:
                                facelist.append(data)
                        usedverts = []
                        for face in facelist:
                            usedverts.extend(face[0:3])
                        usedverts = list(set(usedverts))
                        for index in usedverts:
                            vertlist.append(x[0][index])
                        newfacelist = []
                        for face in facelist:
                            newfacelist.append([vertlist.index(x[0][face[0]]),vertlist.index(x[0][face[1]]),vertlist.index(x[0][face[2]]),face[3]])
                        newgeomlist.append([vertlist, newfacelist])
                    #del geomlist[geomlist.index(x)]
                    #geomlist.extend(temp)
                    #newgeomlist.append(temp)
                else:
                    newgeomlist.append(x)
            geomlist = newgeomlist

            scene = '''\t*SCENE_FILENAME "{0}"
\t*SCENE_FIRSTFRAME 0
\t*SCENE_LASTFRAME 100
\t*SCENE_FRAMESPEED 30
\t*SCENE_TICKSPERFRAME 160
\t*SCENE_BACKGROUND_STATIC 0.0000\t0.0000\t0.0000
\t*SCENE_AMBIENT_STATIC 0.0000\t0.0000\t0.0000'''.format(GlobalMap.getMapName())

            materials = str()
            for x in shaderlist:
                materials = materials + '''\t*MATERIAL {0} {{
\t\t*MATERIAL_NAME "{1}"
\t\t*MATERIAL_CLASS "Standard"
\t\t*MATERIAL_AMBIENT 0.5882\t0.5882\t0.5882
\t\t*MATERIAL_DIFFUSE 0.5882\t0.5882\t0.5882
\t\t*MATERIAL_SPECULAR 0.9000\t0.9000\t0.9000
\t\t*MATERIAL_SHINE 0.1000
\t\t*MATERIAL_SHINESTRENGTH 0.0000
\t\t*MATERIAL_TRANSPARENCY 0.0000
\t\t*MATERIAL_WIRESIZE 1.0000
\t\t*MATERIAL_SHADING Blinn
\t\t*MATERIAL_XP_FALLOFF 0.0000
\t\t*MATERIAL_SELFILLUM 0.0000
\t\t*MATERIAL_FALLOFF In
\t\t*MATERIAL_XP_TYPE Filter
\t\t*MAP_DIFFUSE {{
\t\t\t*MAP_NAME "{2}"
\t\t\t*MAP_CLASS "Bitmap"
\t\t\t*MAP_SUBNO 1
\t\t\t*MAP_AMOUNT 1.0000
\t\t\t*BITMAP "\\\\purgatory\\purgatory\\doom\\base\{2}"
\t\t\t*MAP_TYPE Screen
\t\t\t*UVW_U_OFFSET 0.0000
\t\t\t*UVW_V_OFFSET 0.0000
\t\t\t*UVW_U_TILING 1.0000
\t\t\t*UVW_V_TILING 1.0000
\t\t\t*UVW_ANGLE 0.0000
\t\t\t*UVW_BLUR 1.0000
\t\t\t*UVW_BLUR_OFFSET 0.0000
\t\t\t*UVW_NOUSE_AMT 1.0000
\t\t\t*UVW_NOISE_SIZE 1.0000
\t\t\t*UVW_NOISE_LEVEL 1
\t\t\t*UVW_NOISE_PHASE 0.0000
\t\t\t*BITMAP_FILTER Pyramidal
\t\t}}
\t}}
'''.format(shaderlist.index(x), x, x.replace('/','\\'))

            geomobjects = str()

            for x in geomlist:
                # x[0] = vertices
                # vert[0] - vert[2] = vertex coords
                # vert[3] - vert[4] = texture coords
                # vert[5] - vert[7] = normal
                # x[1] = faces
                vertlist = str()
                for count, data in enumerate(x[0]):
                    vertlist = vertlist + '''\t\t\t*MESH_VERTEX {0}\t{1: 10.4f}\t{2: 10.4f}\t{3: 10.4f}\n'''.format(count, data[0], data[1], data[2])
                facelist = str()
                for count, data in enumerate(x[1]):
                    facelist = facelist + '''\t\t\t*MESH_FACE     {0}:  A:   {1} B:   {2} C:     {3} AB:       0 BC:    0 CA:    0\t *MESH_SMOOTHING 1 \t*MESH_MTLID {4}\n'''.format(count, data[0], data[1], data[2], data[3])
                tvertlist = str()
                for count, data in enumerate(x[0]):
                    tvertlist = tvertlist + '''\t\t\t*MESH_TVERT {0}\t{1: 10.4f}\t{2: 10.4f}\t0.0000\n'''.format(count, data[3], data[4])
                tfacelist = str()
                for count, data in enumerate(x[1]):
                    tfacelist = tfacelist + '''\t\t\t*MESH_TFACE {0}\t{1}\t{2}\t{3}\n'''.format(count, data[0], data[1], data[2])
                cfacelist = str()
                for count, data in enumerate(x[1]):
                    cfacelist = cfacelist + '''\t\t\t*MESH_CFACE {0}\t0\t0\t0\n'''.format(count)

                normals = str()
                for count, data in enumerate(x[1]):
                    normals += '''\t\t\t*MESH_FACENORMAL {0}\t{1: 10.4f}\t{2: 10.4f}\t{3: 10.4f}\n'''.format(count, x[0][data[0]][5], x[0][data[0]][6], x[0][data[0]][7]) # greebo: use first vertex normal as face normal
                    normals += '''\t\t\t\t*MESH_VERTEXNORMAL {0}\t{1: 10.4f}\t{2: 10.4f}\t{3: 10.4f}\n'''.format(data[0], x[0][data[0]][5], x[0][data[0]][6], x[0][data[0]][7])
                    normals += '''\t\t\t\t*MESH_VERTEXNORMAL {0}\t{1: 10.4f}\t{2: 10.4f}\t{3: 10.4f}\n'''.format(data[1], x[0][data[1]][5], x[0][data[1]][6], x[0][data[1]][7])
                    normals += '''\t\t\t\t*MESH_VERTEXNORMAL {0}\t{1: 10.4f}\t{2: 10.4f}\t{3: 10.4f}\n'''.format(data[2], x[0][data[2]][5], x[0][data[2]][6], x[0][data[2]][7])

                if len(x[1]) == 0:
                    continue

                geomobjects = geomobjects + '''*GEOMOBJECT {{
\t*NODE_NAME "{0}"
\t*NODE_TM {{
\t\t*NODE_NAME "{0}"
\t\t*INHERIT_POS 0 0 0
\t\t*INHERIT_ROT 0 0 0
\t\t*INHERIT_SCL 0 0 0
\t\t*TM_ROW0 1.0000\t0.0000\t0.0000
\t\t*TM_ROW1 0.0000\t1.0000\t0.0000
\t\t*TM_ROW2 0.0000\t0.0000\t1.0000
\t\t*TM_ROW3 0.0000\t0.0000\t0.0000
\t\t*TM_POS 0.0000\t0.0000\t0.0000
\t\t*TM_ROTAXIS 0.0000\t0.0000\t0.0000
\t\t*TM_ROTANGLE 0.0000
\t\t*TM_SCALE 1.0000\t1.0000\t1.0000
\t\t*TM_SCALEAXIS 0.0000\t0.0000\t0.0000
\t\t*TM_SCALEAXISANG 0.0000
\t}}
\t*MESH {{
\t\t*TIMEVALUE 0
\t\t*MESH_NUMVERTEX {1}
\t\t*MESH_NUMFACES {2}
\t\t*MESH_VERTEX_LIST {{
{3}\t\t}}
\t\t*MESH_FACE_LIST {{
{4}\t\t}}
\t\t*MESH_NUMTVERTEX {5}
\t\t*MESH_TVERTLIST {{
{6}\t\t}}
\t\t*MESH_NUMTVFACES {7}
\t\t*MESH_TFACELIST {{
{8}\t\t}}
\t\t*MESH_NUMCVERTEX 1
\t\t*MESH_CVERTLIST {{
\t\t\t*MESH_VERTCOL 0\t1.0000\t1.0000\t1.0000
\t\t}}
\t\t*MESH_NUMCVFACES {9}
\t\t*MESH_CFACELIST {{
{10}\t\t}}
\t\t*MESH_NORMALS {{
{11}\t\t}}
\t}}
\t*PROP_MOTIONBLUR 0
\t*PROP_CASTSHADOW 1
\t*PROP_RECVSHADOW 1
\t*MATERIAL_REF {12}
}}\n'''.format('mesh' + str(geomlist.index(x)), \
        len(x[0]), \
        len(x[1]), \
        vertlist, \
        facelist, \
        len(x[0]), \
        tvertlist, \
        len(x[1]), \
        tfacelist, \
        len(x[1]), \
        cfacelist, \
        normals, \
        x[1][0][3]) # material reference from first face

                data = '''*3DSMAX_ASCIIEXPORT\t200
*COMMENT "{0} v{1}"
*SCENE {{
{2}
}}
*MATERIAL_LIST {{
\t*MATERIAL_COUNT {3}
{4}}}
{5}'''.format(script, version, scene, len(shaderlist), materials, geomobjects)

                # Write the compiled data to the output file
                file = open(fullpath, 'w')
                file.write(data)
                file.close()

# __executeCommand__ evaluates to true after DarkRadiant has successfully initialised
if __executeCommand__:
    execute()

