from collections import OrderedDict
import os
import bpy
from xml.etree import ElementTree
from .texture_files import TextureType
from .types import data, SEMaterialInfo, rgb
import re


BAD_PATH = re.compile(r"^(?:[A-Za-z]:|\.\.)?[\\/]")

def se_content_dir(settings):
    se_dir = bpy.path.abspath(bpy.context.user_preferences.addons['space_engineers'].preferences.seDir)
    if not se_dir:
        return settings.baseDir # fallback, if unset
    return os.path.join(os.path.normpath(se_dir), "Content")

def derive_texture_path(settings, filepath):
    def is_in_subpath(relpath):
        return not relpath.startswith('..') and not os.path.isabs(relpath)

    image_path = os.path.normpath(bpy.path.abspath(filepath))

    try:
        relative_to_se = os.path.relpath(image_path, se_content_dir(settings))
        if is_in_subpath(relative_to_se):
            return relative_to_se
    except ValueError:
        pass

    try:
        relative_to_basedir = os.path.relpath(image_path, settings.baseDir)
        if is_in_subpath(relative_to_basedir):
            return relative_to_basedir
    except ValueError:
        pass

    return image_path

def _floatstr(f):
    return str(round(f, 2))

# fixes the misspelled constant in types.py without the need to update the value in existing .blend files
def _material_technique(technique):
    return "ALPHA_MASKED" if "ALPHAMASK" == technique else technique

def material_xml(settings, mat, file=None, node=None):
    d = data(mat)
    e = ElementTree.Element("Material", Name=mat.name)
    m = SEMaterialInfo(mat)

    def param(name, value):
        se = ElementTree.SubElement(e, 'Parameter', Name=name)
        if value:
            se.text = value

    param("Technique", _material_technique(d.technique))
    param("SpecularIntensity", _floatstr(m.specularIntensity))
    param("SpecularPower", _floatstr(m.specularPower))

    if 'GLASS' == d.technique:
        param("DiffuseColorX", '255')
        param("DiffuseColorY", '255')
        param("DiffuseColorZ", '255')
        param("GlassMaterialCCW", d.glass_material_ccw)
        param("GlassMaterialCW", d.glass_material_cw)
        param("GlassSmooth", str(d.glass_smooth))
    else:
        r, g, b = rgb(m.diffuseColor)
        param("DiffuseColorX", str(int(255 * r)))
        param("DiffuseColorY", str(int(255 * g)))
        param("DiffuseColorZ", str(int(255 * b)))

    # only for legacy materials
    if m.couldDefaultNormalTexture and not TextureType.Normal in m.images:
        m.images[TextureType.Normal] = ''

    for texType in TextureType:
        filepath = m.images.get(texType, None)
        if not filepath is None:
            derivedPath = derive_texture_path(settings, filepath)
            if (BAD_PATH.search(derivedPath)):
                settings.error("The %s texture of material '%s' exports with the non-portable path: '%s'. "
                               "Consult the documentation on texture-paths."
                               % (texType.name, mat.name, derivedPath), file=file, node=node)
            param(texType.name + "Texture", derivedPath)
        else:
            e.append(ElementTree.Comment("material has no %sTexture" % texType.name))

    return e

def lod_xml(settings, lodMwmFile: str, lodDistance: int, renderQualities:iter=None):
    e = ElementTree.Element("LOD")
    attrib = OrderedDict()
    attrib['Distance'] = str(lodDistance)
    if not renderQualities is None:
        attrib['RenderQuality'] = ', '.join(renderQualities)
    e.attrib = attrib

    em = ElementTree.SubElement(e, "Model")
    try:
        filePath = os.path.relpath(os.path.join(settings.outputDir, lodMwmFile), settings.baseDir)
    except ValueError:
        filePath = settings.template(settings.names.modelpath, modelfile=os.path.basename(lodMwmFile))
    em.text = filePath
    return e

def mwmbuilder_xml(settings, material_elements, lod_elements, rescale_factor: float = 1, rotation_y: float = 0):
    d = data(settings.scene)
    e = ElementTree.Element("Model", Name=settings.blockname)

    def param(name, value):
        se = ElementTree.SubElement(e, 'Parameter', Name=name)
        if value:
            se.text = value

    param("RescaleFactor", str(round(rescale_factor, 3)))
    param("RescaleToLengthInMeters", "false")
    param("Centered", "false")
    if rotation_y:
        param("RotationY", str(round(rotation_y, 3)))
    param("SpecularPower", _floatstr(d.block_specular_power))
    param("SpecularShininess", _floatstr(d.block_specular_shininess))

    for mat in material_elements:
        e.append(mat)

    for lod in lod_elements:
        e.append(lod)

    # TODO other mwmbuilder.xml parameters:

    # <Parameter Name="PatternScale">4</Parameter>
    # <BoneGridSize>2.5</BoneGridSize>
    # <BoneMapping>
    #    <Bone X="0" Y="0" Z="0" />

    return e