"""Functions to bake from Ladybug geometries into a Rhino document."""
from .fromgeometry import from_point2d, from_arc2d, from_polyline2d, from_mesh2d, \
    from_point3d, from_plane, from_arc3d, from_polyline3d, \
    from_mesh3d, from_face3d, from_polyface3d, from_sphere, from_cone, from_cylinder
from .color import color_to_color, gray
try:
    from System.Drawing import Color
except ImportError as e:
    raise ImportError("Failed to import Windows/.NET libraries\n{}".format(e))
try:
    import Rhino.Geometry as rg
    from Rhino import RhinoMath
    import Rhino.DocObjects as docobj
    from Rhino import RhinoDoc as rhdoc
except ImportError as e:
    raise ImportError("Failed to import Rhino document attributes.\n{}".format(e))
"""____________BAKE 2D GEOMETRY TO THE RHINO SCENE____________"""
[docs]
def bake_vector2d(vector, z=0, layer_name=None, attributes=None):
    """Add ladybug Ray2D to the Rhino scene as a Line with an Arrowhead."""
    doc = rhdoc.ActiveDoc
    seg = (rg.Point3d(0, 0, z), rg.Point3d(vector.x, vector.y, z))
    attrib = _get_attributes(layer_name, attributes)
    attrib.ObjectDecoration = docobj.ObjectDecoration.EndArrowhead
    return doc.Objects.AddLine(seg[0], seg[1], attrib) 
[docs]
def bake_point2d(point, z=0, layer_name=None, attributes=None):
    """Add ladybug Point2D to the Rhino scene as a Point."""
    doc = rhdoc.ActiveDoc
    pt = from_point2d(point, z)
    return doc.Objects.AddPoint(pt, _get_attributes(layer_name, attributes)) 
[docs]
def bake_ray2d(ray, z=0, layer_name=None, attributes=None):
    """Add ladybug Ray2D to the Rhino scene as a Line with an Arrowhead."""
    doc = rhdoc.ActiveDoc
    seg = (from_point2d(ray.p, z), from_point2d(ray.p + ray.v, z))
    attrib = _get_attributes(layer_name, attributes)
    attrib.ObjectDecoration = docobj.ObjectDecoration.EndArrowhead
    return doc.Objects.AddLine(seg[0], seg[1], attrib) 
[docs]
def bake_linesegment2d(line, z=0, layer_name=None, attributes=None):
    """Add ladybug LineSegment2D to the Rhino scene as a Line."""
    doc = rhdoc.ActiveDoc
    seg = (from_point2d(line.p1, z), from_point2d(line.p2, z))
    return doc.Objects.AddLine(seg[0], seg[1], _get_attributes(layer_name, attributes)) 
[docs]
def bake_polygon2d(polygon, z=0, layer_name=None, attributes=None):
    """Add ladybug Polygon2D to the Rhino scene as a Polyline."""
    doc = rhdoc.ActiveDoc
    pgon = [from_point2d(pt, z) for pt in polygon.vertices] + \
        
[from_point2d(polygon[0], z)]
    return doc.Objects.AddPolyline(pgon, _get_attributes(layer_name, attributes)) 
[docs]
def bake_arc2d(arc, z=0, layer_name=None, attributes=None):
    """Add ladybug Arc2D to the Rhino scene as an Arc or a Circle."""
    doc = rhdoc.ActiveDoc
    rh_arc = from_arc2d(arc, z)
    if arc.is_circle:
        return doc.Objects.AddCircle(rh_arc, _get_attributes(layer_name, attributes))
    else:
        return doc.Objects.AddArc(rh_arc, _get_attributes(layer_name, attributes)) 
[docs]
def bake_polyline2d(polyline, z=0, layer_name=None, attributes=None):
    """Add ladybug Polyline2D to the Rhino scene as a Curve."""
    doc = rhdoc.ActiveDoc
    rh_crv = from_polyline2d(polyline, z)
    return doc.Objects.AddCurve(rh_crv, _get_attributes(layer_name, attributes)) 
[docs]
def bake_mesh2d(mesh, z=0, layer_name=None, attributes=None):
    """Add ladybug Mesh2D to the Rhino scene as a Mesh."""
    doc = rhdoc.ActiveDoc
    _mesh = from_mesh2d(mesh, z)
    return doc.Objects.AddMesh(_mesh, _get_attributes(layer_name, attributes)) 
"""____________BAKE 3D GEOMETRY TO THE RHINO SCENE____________"""
[docs]
def bake_vector3d(vector, layer_name=None, attributes=None):
    """Add ladybug Ray2D to the Rhino scene as a Line with an Arrowhead."""
    doc = rhdoc.ActiveDoc
    seg = (rg.Point3d(0, 0, 0), rg.Point3d(vector.x, vector.y, vector.z))
    attrib = _get_attributes(layer_name, attributes)
    attrib.ObjectDecoration = docobj.ObjectDecoration.EndArrowhead
    return doc.Objects.AddLine(seg[0], seg[1], attrib) 
[docs]
def bake_point3d(point, layer_name=None, attributes=None):
    """Add ladybug Point3D to the Rhino scene as a Point."""
    doc = rhdoc.ActiveDoc
    pt = from_point3d(point)
    return doc.Objects.AddPoint(pt, _get_attributes(layer_name, attributes)) 
[docs]
def bake_ray3d(ray, layer_name=None, attributes=None):
    """Add ladybug Ray2D to the Rhino scene as a Line with an Arrowhead."""
    doc = rhdoc.ActiveDoc
    seg = (from_point3d(ray.p), from_point3d(ray.p + ray.v))
    attrib = _get_attributes(layer_name, attributes)
    attrib.ObjectDecoration = docobj.ObjectDecoration.EndArrowhead
    return doc.Objects.AddLine(seg[0], seg[1], attrib) 
[docs]
def bake_plane(plane, layer_name=None, attributes=None):
    """Add ladybug Plane to the Rhino scene as a Rectangle."""
    doc = rhdoc.ActiveDoc
    rh_pln = from_plane(plane)
    r = 10  # default radius for a plane object in rhino model units
    interval = rg.Interval(-r / 2, r / 2)
    rect = rg.Rectangle3d(rh_pln, interval, interval)
    return doc.Objects.AddRectangle(rect, _get_attributes(layer_name, attributes)) 
[docs]
def bake_linesegment3d(line, layer_name=None, attributes=None):
    """Add ladybug LineSegment3D to the Rhino scene as a Line."""
    doc = rhdoc.ActiveDoc
    seg = (from_point3d(line.p1), from_point3d(line.p2))
    return doc.Objects.AddLine(seg[0], seg[1], _get_attributes(layer_name, attributes)) 
[docs]
def bake_arc3d(arc, layer_name=None, attributes=None):
    """Add ladybug Arc3D to the Rhino scene as an Arc or Circle."""
    doc = rhdoc.ActiveDoc
    rh_arc = from_arc3d(arc)
    if arc.is_circle:
        return doc.Objects.AddCircle(rh_arc, _get_attributes(layer_name, attributes))
    else:
        return doc.Objects.AddArc(rh_arc, _get_attributes(layer_name, attributes)) 
[docs]
def bake_polyline3d(polyline, layer_name=None, attributes=None):
    """Add ladybug Polyline3D to the Rhino scene as a Curve."""
    doc = rhdoc.ActiveDoc
    rh_crv = from_polyline3d(polyline)
    return doc.Objects.AddCurve(rh_crv, _get_attributes(layer_name, attributes)) 
[docs]
def bake_mesh3d(mesh, layer_name=None, attributes=None):
    """Add ladybug Mesh3D to the Rhino scene as a Mesh."""
    doc = rhdoc.ActiveDoc
    _mesh = from_mesh3d(mesh)
    return doc.Objects.AddMesh(_mesh, _get_attributes(layer_name, attributes)) 
[docs]
def bake_face3d(face, layer_name=None, attributes=None):
    """Add ladybug Face3D to the Rhino scene as a Brep."""
    doc = rhdoc.ActiveDoc
    _face = from_face3d(face)
    return doc.Objects.AddBrep(_face, _get_attributes(layer_name, attributes)) 
[docs]
def bake_polyface3d(polyface, layer_name=None, attributes=None):
    """Add ladybug Polyface3D to the Rhino scene as a Brep."""
    doc = rhdoc.ActiveDoc
    rh_polyface = from_polyface3d(polyface)
    return doc.Objects.AddBrep(rh_polyface, _get_attributes(layer_name, attributes)) 
[docs]
def bake_sphere(sphere, layer_name=None, attributes=None):
    """Add ladybug Sphere to the Rhino scene as a Brep."""
    doc = rhdoc.ActiveDoc
    rh_sphere = from_sphere(sphere).ToBrep()
    return doc.Objects.AddBrep(rh_sphere, _get_attributes(layer_name, attributes)) 
[docs]
def bake_cone(cone, layer_name=None, attributes=None):
    """Add ladybug Cone to the Rhino scene as a Brep."""
    doc = rhdoc.ActiveDoc
    rh_cone = from_cone(cone).ToBrep()
    return doc.Objects.AddBrep(rh_cone, _get_attributes(layer_name, attributes)) 
[docs]
def bake_cylinder(cylinder, layer_name=None, attributes=None):
    """Add ladybug Cylinder to the Rhino scene as a Brep."""
    doc = rhdoc.ActiveDoc
    rh_cylinder = from_cylinder(cylinder).ToBrep()
    return doc.Objects.AddBrep(rh_cylinder, _get_attributes(layer_name, attributes)) 
"""________ADDITIONAL 3D GEOMETRY TRANSLATORS________"""
[docs]
def bake_mesh3d_as_hatch(mesh, layer_name=None, attributes=None):
    """Add ladybug Mesh3D to the Rhino scene as a colored hatch."""
    doc = rhdoc.ActiveDoc
    # get a list of colors that align with the mesh faces
    if mesh.colors is not None:
        if mesh.is_color_by_face:
            colors = [color_to_color(col) for col in mesh.colors]
        else:  # compute the average color across the vertices
            colors, v_cols = [], mesh.colors
            for face in mesh.faces:
                red = int(sum(v_cols[f].r for f in face) / len(face))
                green = int(sum(v_cols[f].g for f in face) / len(face))
                blue = int(sum(v_cols[f].b for f in face) / len(face))
                colors.append(Color.FromArgb(255, red, green, blue))
    else:
        colors = [gray()] * len(mesh.faces)
    # create hatches from each of the faces and get an aligned list of colors
    hatches, hatch_colors = [], []
    vertices = mesh.vertices
    for face, color in zip(mesh.faces, colors):
        f_verts = [from_point3d(vertices[f]) for f in face]
        f_verts.append(f_verts[0])
        p_line = rg.PolylineCurve(f_verts)
        if p_line.IsPlanar():
            hatches.append(rg.Hatch.Create(p_line, 0, 0, 0)[0])
            hatch_colors.append(color)
        elif len(face) == 4:
            p_line_1 = rg.PolylineCurve(f_verts[:3] + [f_verts[0]])
            p_line_2 = rg.PolylineCurve(f_verts[-3:] + [f_verts[-3]])
            hatches.append(rg.Hatch.Create(p_line_1, 0, 0, 0)[0])
            hatches.append(rg.Hatch.Create(p_line_2, 0, 0, 0)[0])
            hatch_colors.extend((color, color))
    # bake the hatches into the scene
    guids = []
    for hatch, color in zip(hatches, hatch_colors):
        attribs = _get_attributes(layer_name, attributes)
        attribs.ColorSource = docobj.ObjectColorSource.ColorFromObject
        attribs.ObjectColor = color
        guids.append(doc.Objects.AddHatch(hatch, attribs))
    # group the hatches so that they are easy to handle in the Rhino scene
    group_t = doc.Groups
    docobj.Tables.GroupTable.Add(group_t, guids)
    return guids 
"""________________EXTRA HELPER FUNCTIONS________________"""
def _get_attributes(layer_name=None, attributes=None):
    """Get Rhino object attributes."""
    doc = rhdoc.ActiveDoc
    attributes = doc.CreateDefaultAttributes() if attributes is None else attributes
    if layer_name is None:
        return attributes
    elif isinstance(layer_name, int):
        attributes.LayerIndex = layer_name
    elif layer_name is not None:
        attributes.LayerIndex = _get_layer(layer_name)
    return attributes
def _get_layer(layer_name):
    """Get a layer index from the Rhino document from the layer name."""
    doc = rhdoc.ActiveDoc
    layer_table = doc.Layers  # layer table
    layer_index = layer_table.FindByFullPath(layer_name, RhinoMath.UnsetIntIndex)
    if layer_index == RhinoMath.UnsetIntIndex:
        all_layers = layer_name.split('::')
        parent_name = all_layers[0]
        layer_index = layer_table.FindByFullPath(parent_name, RhinoMath.UnsetIntIndex)
        if layer_index == RhinoMath.UnsetIntIndex:
            parent_layer = docobj.Layer()
            parent_layer.Name = parent_name
            layer_index = layer_table.Add(parent_layer)
        for lay in all_layers[1:]:
            parent_name = '{}::{}'.format(parent_name, lay)
            parent_index = layer_index
            layer_index = layer_table.FindByFullPath(
                parent_name, RhinoMath.UnsetIntIndex)
            if layer_index == RhinoMath.UnsetIntIndex:
                parent_layer = docobj.Layer()
                parent_layer.Name = lay
                parent_layer.ParentLayerId = layer_table[parent_index].Id
                layer_index = layer_table.Add(parent_layer)
    return layer_index