"""
@package CurveReader.hcurve_loader
Holds the object for reading an xml
@author Coen Klosters
@version 1.0
"""

from lxml import etree
import os
import hou

POINT_ELEMENT_NAME = "Point"
USER_ELEMENT_NAME = "User"
CAM_ELEMENT_NAME = "Camera"
POINTS_ELEMENT_NAME = "Points"
ROOT_ELEMENT_NAME = "CurveObject"
VERTEX_ELEMENT_NAME = "Vertex"
POLY_ELEMENT_NAME = "PolyLine"
POLY_LINES_ELEMENT_NAME = "PolyLines"

USER_NAME_ATTRIB = "name"
USER_MAIL_ATTRIB = "mail"

CAM_POS_ATTRIB = "position"
CAM_ROT_ATTRIB = "rotation"
CAM_CLIP_ATTRIB = "clipping"
CAM_FOV_ATTRIB = "fov"

POINT_POS_ATTRIB = "position"
POINT_COL_ATTRIB = "color"
POINT_WIDTH_ATTRIB = "width"
POINT_NUM_ATTRIB = "number"

CURVE_NUM_ATTRIB = "number"
VERT_POINT_ATTRIB = "point"

class CameraInfo(object):
    """
    Holds some of the camera info
    """
    
    def __init__(self, inPos, inRot, inClip, inFOV):
        self.Position = inPos
        self.Rotation = inRot
        self.Clipping = inClip
        self.FOV = inFOV

class UserInfo(object):
    """
    Holds the user information
    """

    def __init__(self, inUserName, inUserMail):
        self.Name = inUserName
        self.Mail = inUserMail
        self.FName = self.Name.replace(" ","_").lower()

class CurvePoint(object):
    """
    Simple container for a loaded curve point
    """

    def __init__(self, inPos, inColor, inWidth, inNumber):
        self.Number = inNumber
        self.Color = inColor
        self.Position = inPos
        self.Width = inWidth

class Curve(object):
    """
    Simple container for a curve
    """
    
    def __init__(self, inNumber, inVerts):
        self.Verts = inVerts
        self.Number = inNumber

class CurveLoader(object):
    """
    Simple class for loading the xml and create some objects out of it
    """

    def __init__(self, inFile):
        """
        Initializes the object with the file
        """
        # Set the xml file
        self.XmlFile = inFile
        # The parsed tree
        self.Tree = None
        # The root node
        self.Root = None
        # The User info object
        self.UserInfo = None
        # The Cam info object
        self.CamInfo = None
        # The list of points
        self.Points = []
        # The list of curves
        self.Curves = []
        # The node that holds the cuves
        self.CurveContainer = None



    def Load(self):
        self.Tree = etree.parse(self.XmlFile)
        self.Root = self.Tree.getroot()

        # save the user info
        user_el = self.Root.find(USER_ELEMENT_NAME)
        self.UserInfo = UserInfo(user_el.get(USER_NAME_ATTRIB), user_el.get(USER_MAIL_ATTRIB))

        # save the camera info
        came_el = self.Root.find(CAM_ELEMENT_NAME)
        pos = came_el.get(CAM_POS_ATTRIB).split(',')
        rot = came_el.get(CAM_ROT_ATTRIB).split(',')
        clip = came_el.get(CAM_CLIP_ATTRIB).split(',')
        fov = float(came_el.get(CAM_FOV_ATTRIB))
        self.CamInfo = CameraInfo(self.ConvertToFloatArray(pos), self.ConvertToFloatArray(rot), self.ConvertToFloatArray(clip), fov)

        # save the point info
        points_el = self.Root.find(POINTS_ELEMENT_NAME)
        self.LoadPoints(points_el)

        # save the curve info
        curves_el = self.Root.find(POLY_LINES_ELEMENT_NAME)
        self.LoadCurves(curves_el)
        
        
        
    def LoadPoints(self, inPointsElement):
        """
        Loads the points in to the object
        """
        self.Points = []
        for p in inPointsElement:
            pos = p.get(POINT_POS_ATTRIB).split(',')
            col = p.get(POINT_COL_ATTRIB).split(',')
            width = p.get(POINT_WIDTH_ATTRIB)
            number = p.get(POINT_NUM_ATTRIB)
            
            # make sure to scale the z -1
            point_pos_n = self.ConvertToFloatArray(pos)
            point_pos_n = [point_pos_n[0], point_pos_n[1], point_pos_n[2] * -1]
            
            self.Points.append(CurvePoint(point_pos_n, self.ConvertToFloatArray(col), float(width), int(number)))



    def LoadCurves(self, inCurvesElement):
        """
        Loads the curves in to the object
        """
        self.Curves = []
        for c in inCurvesElement:
            verts = []
            number = c.get(CURVE_NUM_ATTRIB)
            for v in c:
                pnum = int(v.get(VERT_POINT_ATTRIB))
                verts.append(pnum)
            self.Curves.append(Curve(number, verts))



    def CreateCurves(self):
        """
        Creates the curves out of the loaded data
        """
        if self.Points is None:
            raise Exception("Unable to query points for curve creation!")

        if self.Curves is None:
            raise Exception("Unable to query curve data for curve creation!")

        # Create the new curve container
        self.CurveContainer = hou.node("/obj").createNode("geo","CurveContainer")
        for c in self.CurveContainer.children():
            c.destroy()

        # Create the merge node
        merge_node = self.CurveContainer.createNode("merge", "CurveMerger")

        # Add the curves
        count = 0
        for c in self.Curves:
            curve_node = self.CreateHoudiniCurve(c)
            merge_node.setInput(count, curve_node)
            count += 1

        # Make 'm lay in a good way
        self.CurveContainer.layoutChildren()


    def CreateHoudiniCurve(self, inCurve):
        """
        Creates the houdini curve in the scene and returns it
        """
        
        node = self.CurveContainer.createNode("curve", "curve_%s" % inCurve.Number)
        points = []
        
        coordinates = ""
        for v in inCurve.Verts:
            sample_p = self.Points[v]
            point_coords = "%s,%s,%s " % (sample_p.Position[0], sample_p.Position[1], sample_p.Position[2])
            coordinates += point_coords

        node.parm("coords").set(coordinates)
        node.parm("type").set(1)
        node.parm("method").set(1)
        return node


    def UpdateCamera(self, inCameraNode):
        """
        Updates the cam based on the loaded settings
        """

        inCameraNode.parm("tx").set(self.CamInfo.Position[0])
        inCameraNode.parm("ty").set(self.CamInfo.Position[1])
        inCameraNode.parm("tz").set(self.CamInfo.Position[2])
        
        inCameraNode.parm("rx").set(self.CamInfo.Rotation[0])    
        inCameraNode.parm("ry").set(self.CamInfo.Rotation[1])
        inCameraNode.parm("rz").set(self.CamInfo.Rotation[2])

        inCameraNode.parm("near").set(self.CamInfo.Clipping[0])
        inCameraNode.parm("far").set(self.CamInfo.Clipping[1])

        inCameraNode.parm("aperture").set(self.CamInfo.FOV)


    
    def ConvertToFloatArray(self, inNumbers):
        """
        Returns a list of strings as an array of floats
        """
        a = []
        for v in inNumbers:
            a.append(float(v))
        return a