#!/usr/bin/env python

from __future__ import print_function

import colorsys
import glob
import os
import pybullet as p
import random
import sys

import numpy as np
from lxml import etree

from pybullet_tools.pr2_utils import DRAKE_PR2_URDF, set_group_conf
from pybullet_tools.utils import STATIC_MASS, CLIENT, connect, \
    disconnect, set_pose, wait_for_user, load_model, HideOutput, base_values_from_pose, create_shape, \
    get_mesh_geometry
from pybullet_tools.utils import quaternion_from_matrix

# https://docs.python.org/3.5/library/xml.etree.elementtree.html
# https://lxml.de/tutorial.html

def parse_array(element):
    return np.array(element.text.split(), dtype=np.float)

def parse_pose(element):
    homogenous = [0, 0, 0, 1]
    matrix = np.reshape(np.concatenate([parse_array(element), homogenous]), [4, 4])
    point = matrix[:3, 3]
    quat = quaternion_from_matrix(matrix)
    return (point, quat)

def parse_boolean(element):
    text = element.text
    if text == 'true':
        return True
    if text == 'false':
        return False
    raise ValueError(text)

MAX_INT = sys.maxsize + 1

def parse_object(obj, mesh_directory):
    name = obj.find('name').text
    mesh_filename = obj.find('geom').text
    geom = get_mesh_geometry(os.path.join(mesh_directory, mesh_filename))
    pose = parse_pose(obj.find('pose'))
    movable = parse_boolean(obj.find('moveable'))

    color = (.75, .75, .75, 1)
    if 'red' in name:
        color = (1, 0, 0, 1)
    elif 'green' in name:
        color = (0, 1, 0, 1)
    elif 'blue' in name:
        color = (0, 0, 1, 1)
    elif movable: # TODO: assign new color
        #size = 2 * MAX_INT
        #size = 255
        #n = id(obj) % size
        #n = hash(obj) % size
        #h = float(n) / size
        h = random.random()
        r, g, b = colorsys.hsv_to_rgb(h, .75, .75)
        color = (r, g, b, 1)
    print(name, mesh_filename, movable, color)

    collision_id, visual_id = create_shape(geom, color=color)
    body_id = p.createMultiBody(baseMass=STATIC_MASS, baseCollisionShapeIndex=collision_id,
                                baseVisualShapeIndex=visual_id, physicsClientId=CLIENT)
    set_pose(body_id, pose)

    return body_id

def parse_robot(robot):
    name = robot.find('name').text
    urdf = robot.find('urdf').text
    fixed_base = not parse_boolean(robot.find('movebase'))
    print(name, urdf, fixed_base)
    pose = parse_pose(robot.find('basepose'))
    torso = parse_array(robot.find('torso'))
    left_arm = parse_array(robot.find('left_arm'))
    right_arm = parse_array(robot.find('right_arm'))
    assert (name == 'pr2')

    with HideOutput():
        robot_id = load_model(DRAKE_PR2_URDF, fixed_base=True)
    set_group_conf(robot_id, 'base', base_values_from_pose(pose))
    set_group_conf(robot_id, 'torso', torso)
    set_group_conf(robot_id, 'left_arm', left_arm)
    set_group_conf(robot_id, 'right_arm', right_arm)
    #set_point(robot_id, Point(z=point_from_pose(pose)[2]))
    # TODO: base pose isn't right
    # print(robot.tag)
    # print(robot.attrib)
    # print(list(robot.iter('basepose')))
    return robot_id

DISC = 'disc'
BLOCK = 'cube'
PEG = 'peg'

def main():
    benchmark = 'tmp-benchmark-data'
    problem = 'problem1' # Hanoi
    #problem = 'problem2' # Blocksworld
    #problem = 'problem3' # Clutter
    #problem = 'problem4' # Nonmono

    root_directory = os.path.dirname(os.path.abspath(__file__))
    directory = os.path.join(root_directory, '..', 'problems', benchmark, problem)
    [mesh_directory] = list(filter(os.path.isdir, (os.path.join(directory, o)
                                                 for o in os.listdir(directory) if o.endswith('meshes'))))
    [xml_path] = [os.path.join(directory, o) for o in os.listdir(directory) if o.endswith('xml')]
    if os.path.isdir(xml_path):
        xml_path = glob.glob(os.path.join(xml_path, '*.xml'))[0]

    print(mesh_directory)
    print(xml_path)
    xml_data = etree.parse(xml_path)

    connect(use_gui=True)
    #add_data_path()
    #load_pybullet("plane.urdf")

    #root = xml_data.getroot()
    #print(root.items())
    for obj in xml_data.findall('/objects/obj'):
        parse_object(obj, mesh_directory)
    for robot in xml_data.findall('/robots/robot'):
        parse_robot(robot)
    wait_for_user()
    disconnect()

if __name__ == '__main__':
    main()