# vim:ts=4:et
# ##### 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 #####

# <pep8 compliant>

import os

import bpy
from bpy_extras.object_utils import object_data_add
from mathutils import Vector,Matrix
from math import pi

from .map import parse_map, MapError
from .quakepal import palette
from .wad import WadFile
from .entity import entity_box, set_entity_props

def parse_vector(vstr):
    v = vstr.split()
    for i in range(len(v)):
        v[i] = float(v[i])
    return Vector(v)

def load_image(tx):
    if tx.name in bpy.data.images:
        return bpy.data.images[tx.name]
    img = bpy.data.images.new(tx.name, tx.miptex.width, tx.miptex.height)
    p = [0.0] * tx.miptex.width * tx.miptex.height * 4
    d = tx.miptex.texels[0]
    for j in range(tx.miptex.height):
        for k in range(tx.miptex.width):
            c = palette[d[j * tx.miptex.width + k]]
            # quake textures are top to bottom, but blender images
            # are bottom to top
            l = ((tx.miptex.height - 1 - j) * tx.miptex.width + k) * 4
            p[l + 0] = c[0] / 255.0
            p[l + 1] = c[1] / 255.0
            p[l + 2] = c[2] / 255.0
            p[l + 3] = 1.0
    img.pixels[:] = p[:]
    img.pack(True)
    return img

def load_material(tx):
    if tx.name in bpy.data.materials:
        return bpy.data.materials[tx.name]
    mat = bpy.data.materials.new(tx.name)
    mat.diffuse_color = (1, 1, 1, 1)
    mat.specular_intensity = 0
    if tx.image:
        tex = bpy.data.textures.new(tx.name, 'IMAGE')
        tex.extension = 'REPEAT'
        tex.use_preview_alpha = True
        tex.image = tx.image
        mat.texture_slots.add()
        ts = mat.texture_slots[0]
        ts.texture = tex
        ts.use_map_alpha = True
        ts.texture_coords = 'UV'
    return mat

def load_textures(texdefs, wads):
    class MT:
        def __init__(self, x, y):
            self.width = x
            self.height = y
    for tx in texdefs:
        if hasattr(tx, "miptex"):
            continue
        if not wads or not wads[0]:
            tx.miptex = MT(64,64)
            tx.image = None
        else:
            try:
                tx.miptex = wads[0].getData(tx.name)
                tx.image = load_image(tx)
            except KeyError:
                tx.miptex = MT(64,64)
                tx.image = None
        tx.material = load_material(tx)

def build_uvs(verts, faces, texdefs):
    uvs = [None] * len(faces)
    for i, f in enumerate(faces):
        tx = texdefs[i]
        fuv = []
        for vi in f:
            v = Vector(verts[vi])
            s = (v.dot(tx.vecs[0][0]) + tx.vecs[0][1]) / tx.miptex.width
            t = (v.dot(tx.vecs[1][0]) + tx.vecs[1][1]) / tx.miptex.height
            fuv.append((s, 1 - t))
        uvs[i] = fuv
    return uvs

def process_entity(ent, wads):
    qfmap = bpy.context.scene.qfmap
    classname = ent.d["classname"]
    name = classname
    if "classname" in ent.d and ent.d["classname"][:5] == "light":
        light = bpy.data.lights.new("light", 'POINT')
        if "light" in ent.d:
            light.distance = float(ent.d["light"])
        elif "_light" in ent.d:
            light.distance = float(ent.d["_light"])
        else:
            light.distance = 300.0
        light.falloff_type = 'CUSTOM_CURVE'
        obj = bpy.data.objects.new(name, light)
    elif ent.b:
        verts = []
        faces = []
        texdefs = []
        for bverts, bfaces, btexdefs in ent.b:
            base = len(verts)
            verts.extend(bverts)
            texdefs.extend(btexdefs)
            for f in bfaces:
                for i in range(len(f)):
                    f[i] += base
                f.reverse()
                if not f[-1]:
                    t = f[0]
                    del f[0]
                    f.append(t)
            faces.extend(bfaces)
        load_textures(texdefs, wads)
        uvs = build_uvs(verts, faces, texdefs)
        mesh = bpy.data.meshes.new(name)
        for tx in texdefs:
            if hasattr(tx, "matindex"):
                continue
            for i, mat in enumerate(mesh.materials):
                if mat.name == tx.material.name:
                    tx.matindex = i
            if hasattr(tx, "matindex"):
                continue
            tx.matindex = len(mesh.materials)
            mesh.materials.append(tx.material)
        mesh.from_pydata(verts, [], faces)
        """uvlay = mesh.uv_textures.new(name)
        uvloop = mesh.uv_layers[0]
        for i, texpoly in enumerate(uvlay.data):
            poly = mesh.polygons[i]
            uv = uvs[i]
            tx = texdefs[i]
            texpoly.image = tx.image
            poly.material_index = tx.matindex
            for j, k in enumerate(poly.loop_indices):
                uvloop.data[k].uv = uv[j]"""#FIXME
        mesh.update()
        obj = bpy.data.objects.new(name, mesh)
    else:
        try:
            entityclass = qfmap.entity_classes[classname]
        except KeyError:
            entityclass = None
        if entityclass and entityclass.size:
            mesh = entity_box(entityclass)
            obj = bpy.data.objects.new(name, mesh)
        else:
            obj = bpy.data.objects.new(name, None)
            obj.empty_display_type = 'CUBE'
            obj.empty_display_size = 8
        obj.show_name = True
    if "origin" in ent.d:
        obj.location = parse_vector (ent.d["origin"])
    angles = Vector()
    if not ent.b:
        #brush entities doen't normally support rotation
        if "angle" in ent.d:
            angles = Vector((0, 0, float(ent.d["angle"])))
            del ent.d["angle"]
        elif "angles" in ent.d:
            angles = parse_vector(ent.d["angles"])
            angles = angles.xzy
            del ent.d["angles"]
    obj.rotation_mode = 'XZY'
    obj.rotation_euler = angles * pi / 180
    bpy.context.layer_collection.collection.objects.link(obj)
    bpy.context.view_layer.objects.active = obj
    obj.select_set(True)
    set_entity_props(obj, ent)

def import_map(operator, context, filepath):
    undo = bpy.context.preferences.edit.use_global_undo
    bpy.context.preferences.edit.use_global_undo = False

    try:
        for obj in bpy.context.scene.objects:
            obj.select_set(False)
        entities = parse_map (filepath)
    except MapError as err:
        operator.report({'ERROR'}, repr(err))
        return {'CANCELLED'}
    else:
        wads=[]
        if entities:
            if "_wad" in entities[0].d:
                wads = entities[0].d["_wad"].split(";")
            elif "wad" in entities[0].d:
                wads = entities[0].d["wad"].split(";")
            wadpath = bpy.context.scene.qfmap.wadpath
            for i in range(len(wads)):
                try:
                    wads[i] = WadFile.load(os.path.join(wadpath, wads[i]))
                except IOError:
                    try:
                        wads[i] = WadFile.load(os.path.join(wadpath,
                                               os.path.basename(wads[i])))
                    except IOError:
                        #give up
                        operator.report({'INFO'}, "Cant't find %s" % wads[i])
                        wads[i] = None
        for ent in entities:
            process_entity(ent, wads)
    finally:
        bpy.context.preferences.edit.use_global_undo = undo
    return {'FINISHED'}

def import_pts(operator, context, filepath):
    bpy.context.user_preferences.edit.use_global_undo = False

    for obj in bpy.context.scene.objects:
        obj.select_set(False)

    lines = open(filepath, "rt").readlines()
    verts = [None] * len(lines)
    edges = [None] * (len(lines) - 1)
    for i, line in enumerate(lines):
        if i:
            edges[i - 1] = (i - 1, i)
        v = line.split(" ")
        verts[i] = tuple(map(lambda x: float(x), v))
    mesh = bpy.data.meshes.new("leak points")
    mesh.from_pydata(verts, edges, [])
    mesh.update()
    obj = bpy.data.objects.new("leak points", mesh)
    bpy.context.scene.objects.link(obj)
    bpy.context.scene.objects.active=obj
    obj.select_set(True)
    bpy.context.user_preferences.edit.use_global_undo = True
    return {'FINISHED'}
