#!/usr/bin/env python
import os
import xml.etree.ElementTree as ET
from os import listdir
from os.path import isfile, join

import click

from nirmapper.camera import Camera
from nirmapper.examples import generate_cube_example, generate_elephant_example, generate_tooth_example
from nirmapper.model import Texture, Wavefront
from nirmapper.nirmapper import Mapper


@click.group()
def cli():
    pass


@cli.command()
@click.argument('dst', nargs=1)
@click.option('--type', type=click.Choice(['cube', 'tooth', 'elephant']), default='tooth')
def example(dst, type):
    dst = __check_path(dst)
    type_str = 'Generating ' + type + ' example in ' + dst + '.'
    click.echo(type_str)

    if type == 'cube':
        generate_cube_example(dst)
    elif type == 'tooth':
        generate_tooth_example(dst)
    elif type == 'elephant':
        generate_elephant_example(dst)
    else:
        click.echo('Example type not known!')
        return
    click.echo('Finished - have a nice day!')


@cli.command()
@click.argument('name', nargs=1)
@click.argument('model_src', nargs=1)
@click.argument('texture_src', nargs=1)
@click.argument('dst', nargs=1)
@click.option('--zfactor', default=1, type=float,
              help='The z factor defines how big the z-buffer should be for the visibility analysis. '
                   'If results are bad put this up to 2 or 3. Be careful with values below zero because zfactor'
                   ' is multiplied with resolution of camera and must match aspect ratio of resolution.')
@click.option('--thread/--unthread', default=True)
def mapp(name, model_src, texture_src, dst, zfactor, thread):
    if zfactor <= 0:
        click.echo('The zfactor must be greater than zero.')
        exit()
    texture_src = __check_path(texture_src)
    dst = __check_path(dst)
    textures = []
    files = [f for f in listdir(texture_src) if isfile(join(texture_src, f))]
    for f in files:
        file_name = os.path.splitext(f)[0]
        extension = os.path.splitext(f)[1]
        if extension == '.jpg' or extension == '.png' or extension == '.bmp':
            xml_file_path = texture_src + file_name + '.xml'
            if not os.path.exists(xml_file_path):
                click.echo('XML file not found. Every image must provide a xml-file with the'
                           ' same file name. The XML file provides the camera parameters. '
                           'An example can be found in nirmapper/resources/xmlExample .')
                exit()
            try:
                cam = __parse_xml(texture_src + file_name + '.xml')
                texture = Texture(texture_src + f, cam)
                textures.append(texture)
            except AttributeError:
                click.echo('Malformed XML file. Abort mapping program.')
                exit()

    click.echo("Starting model import...")
    model = Wavefront.import_obj_as_model_list(model_src)[0]
    click.echo("Finished model import...")

    # Create Mapper
    texture_mapper = Mapper(textures, model, dst, name, zfactor)

    texture_mapper.start_texture_mapping(thread)
    click.echo('Finished - have a nice day!')


def __parse_xml(file) -> Camera:
    tree = ET.parse(file)
    root = tree.getroot()

    focal_length = float(root.find('focal-length').text)
    resolution = root.find('resolution')
    resolution_x = int(resolution.find('width').text)
    resolution_y = int(resolution.find('height').text)

    sensor = root.find('sensor')
    sensor_x = float(sensor.find('width').text)
    sensor_y = float(sensor.find('height').text)

    location = root.find('location')
    location = [float(location.find('x').text), float(location.find('y').text), float(location.find('z').text)]

    rotation = root.find('rotation')
    rotation_type = rotation.attrib['type']
    if rotation_type == 'EULER':
        rotation = [float(rotation.find('x').text), float(rotation.find('y').text), float(rotation.find('z').text)]
    elif rotation_type == 'QUAT':
        rotation = [float(rotation.find('w').text), float(rotation.find('x').text), float(rotation.find('y').text),
                    float(rotation.find('z').text)]
    else:
        raise AttributeError('Rotation type must be either euler or quaternion')

    cam = Camera(focal_length, resolution_x, resolution_y, sensor_x, sensor_y, location, rotation, rotation_type)

    return cam


def __check_path(path):
    path = ''.join(path)

    if not os.path.exists(path):
        os.makedirs(path)
    if not path.endswith('/'):
        path += '/'

    return path


if __name__ == '__main__':
    click.echo('This is 3DNIRMapper-CLI:')
    cli()
