import Rhino
import scriptcontext
import rhinoscriptsyntax as rs

class EditPoint(object):
    """
    object to represent polycurve sub-curves connection point
    """
    def __init__(self, previous_crv, next_crv):
        self.Previous = previous_crv
        self.Next = next_crv
        if self.Next:
            self.Pt = self.Next.PointAtStart
        else:
            self.Pt = self.Previous.PointAtEnd
            
        self.DocObj = None
    
    def Update(self):
        if self.DocObj:
            self.Pt = rs.PointCoordinates(self.DocObj)
    
    def CleanUp(self):
        if self.DocObj:
            rs.DeleteObject(self.DocObj)

class EditSegment(object):
    """
    object to represent polycurve sub-curve
    """
    def __init__(self, segment_crv, start_epoint,end_epoint):
        self.Crv = segment_crv
        if isinstance(self.Crv,Rhino.Geometry.ArcCurve):
            self.Crv = self.Crv.ToNurbsCurve()
        self.StartPt = self.Crv.PointAtStart
        self.EndPt = self.Crv.PointAtEnd
        self.LineCurve = Rhino.Geometry.LineCurve(self.StartPt,self.EndPt)
        self.StartNew = start_epoint
        self.EndNew = end_epoint
        
    def Morph(self):
        

        tol = scriptcontext.doc.ModelAbsoluteTolerance
        if self.StartPt.EpsilonEquals(self.StartNew.Pt,tol) and self.EndPt.EpsilonEquals(self.EndNew.Pt,tol):
            #print 'equal'
            return
        
        line_curve_to = Rhino.Geometry.LineCurve(self.StartNew.Pt,self.EndNew.Pt)
        FlowMorph = Rhino.Geometry.Morphs.FlowSpaceMorph(self.LineCurve,line_curve_to,False)
        FlowMorph.Morph(self.Crv)
        #scriptcontext.doc.Objects.AddCurve(self.Crv)
        
        
def EditPolyCurve(crv_obj):
    if not rs.IsPolyCurve(crv_obj): return
    poly_crv = rs.coercecurve(crv_obj)
    #poly_crv.RemoveNesting()

    subcurves = []
    for i in range(poly_crv.SegmentCount):
        segment = poly_crv.SegmentCurve(i)
        if isinstance(segment,Rhino.Geometry.PolylineCurve):
            #rs.AddPoint(segment.PointAtStart)
            poly_segments = segment.DuplicateSegments()
            if poly_segments:
                for poly_segment in poly_segments:
                    subcurves.append(poly_segment)
            else:
                subcurves.append(segment)
        else:
            subcurves.append(segment)
    
    #generate editpoints
    epoints = []
    for i,sub_crv in enumerate(subcurves):
        # if first segment
        if i == 0:
            if poly_crv.IsClosed:
                epoints.append(EditPoint(subcurves[-1],sub_crv))
            else:
                epoints.append(EditPoint(None,sub_crv))
        # other segments
        else:
            epoints.append(EditPoint(subcurves[i-1],sub_crv))
    #extra epoint for open poly curves
    if not poly_crv.IsClosed:
        epoints.append(EditPoint(subcurves[-1],None))

    esegments = []
    for i,sub_crv in enumerate(subcurves):
        if sub_crv == subcurves[-1] and poly_crv.IsClosed:
            esegments.append(EditSegment(sub_crv,epoints[i],epoints[0]))
        else:
            esegments.append(EditSegment(sub_crv,epoints[i],epoints[i+1]))
            
    #N = 0
    for epoint in epoints:
        epoint.DocObj = rs.AddPoint(epoint.Pt)
        #N+=1
        #rs.AddTextDot(N,epoint.Pt)

    comm_str = '_Move w{} '
    while True:
        
        pts = rs.GetObjects('select point to edit',3,objects=None,maximum_count=1)
        if not pts:
            break
        rs.UnselectAllObjects()
        rs.SelectObject(pts[0])
        rs.Command(comm_str.format(rs.PointCoordinates(pts[0])))
            
            
            
    _ = [epoint.Update() for epoint in epoints]
    _ = [eseg.Morph() for eseg in esegments]
    _ = [epoint.CleanUp() for epoint in epoints]
    
    
    new_polycurve = Rhino.Geometry.PolyCurve()
    _ = [new_polycurve.Append(eseg.Crv) for eseg in esegments]
    
    
    scriptcontext.doc.Objects.Replace(crv_obj,new_polycurve)
    rs.Redraw()
    
    

def Main():
    obj = rs.GetObject('select curve to test edit',4)
    if not obj: return
    EditPolyCurve(obj)

Main()








