# -*- coding: utf-8 -*-
"""
Tests to verify accuracy of polyline Hausdorff distance calculation

Created on Mon May  3 10:14:29 2021

@author: bjkronenfeld
"""

import polyline_hausdorff as h
import utils_hausdorff as hu
from test_utils import case, plot_hausdorff_solution
import unittest
import random

class hausdorff_tests(unittest.TestCase):
    """Tests functions in the geom_utils.py module"""
    def setUp(self):
        self.verbose = False
        self.numits = 10;
    def test_transform_matches(self):
        # we are going to take all test cases, apply a randomly generated
        # affine transformation to both A & B,
        # and make sure that the Hausdorff distance between the transformed
        # polylines is the same as the original polylines
        def random_transform(A,B):
            # randomly shift, rescale and rotate points
            # shift
            dx = 10000 * (random.random()-0.5)
            dy = 10000 * (random.random()-0.5)
            A_prime = [(x+dx,y+dy) for x,y in A]
            B_prime = [(x+dx,y+dy) for x,y in B]
            return A_prime, B_prime
        # loop through all test cases
        print("working through {} random iterations".format(self.numits))
        for itnum in range(self.numits):
            print("iteration {}".format(itnum+1))
            for s in range(case(-1)):
                # obtain two polylines
                A,B = case(s)
                Aprime,Bprime = random_transform(A,B)
                # check the Hausdorff distance
                H,srcloc,trglocs = h.polyline_hausdorff(A, B)
                Hprime,srclocprime,trglocsprime = h.polyline_hausdorff(Aprime, Bprime)
                self.assertAlmostEqual(H, Hprime)
                # check the Hausdorff distance from each segment
                def segHausdorff_wrapper(C,D,seg):
                    startNearSeg = hu.nearSegment(C, D, seg)
                    endNearSeg = hu.nearSegment(C, D, seg+1)
                    startcomp, startd = hu.nearComponent(C, D, seg, startNearSeg)
                    endcomp, endd = hu.nearComponent(C, D, seg+1, endNearSeg)
                    candidates = hu.candidateComponents(C,D,seg)
                    d,k,c1,c2 = h.hausdorff_segment(C, D, seg, startcomp, startd, endcomp, endd, candidates)
                    return d,k,c1,c2
                def make_comparison(A,B,Aprime,Bprime,seg):
                    # original
                    d,k,c1,c2 = segHausdorff_wrapper(A,B,seg)
                    # transformed
                    dprime,kprime,c1prime,c2prime = segHausdorff_wrapper(Aprime,Bprime,seg)
                    # compare
                    report = "Case: {} seg: {}".format(s,seg)
                    # visualize on error
                    if abs(d-dprime) > 0.001:
                        print("dprime: {}".format(dprime))
                        print("\nORIGINAL")
                        test_hausdorff_segment(A,B,seg,True,report + " original ")
                        print("\nTRANSFORMED")
                        test_hausdorff_segment(Aprime,Bprime,seg,True,report + " transformed ")
                    self.assertAlmostEqual(d,dprime,msg=report)
                # from A
                for seg in range(len(A)-1):
                    make_comparison(A,B,Aprime,Bprime,seg)
                # from B                
                for seg in range(len(B)-1):
                    make_comparison(B,A,Bprime,Aprime,seg)
            
            


def test_hausdorff(A,B):
    H,srcloc, trglocs = h.polyline_hausdorff(A, B)
    plot_hausdorff_solution(A,B,H,srcloc)


def test_hausdorff_unidirectional(A,B):
    d,loc,srcComp, trgComp1,trgComp2 = h.hausdorff_unidirectional(A,B)
    title = "A{} to B{},B{}".format(hu.component_label(srcComp), hu.component_label(trgComp1),hu.component_label(trgComp2))
    plot_hausdorff_solution(A,B,d,loc,title)
        
def test_hausdorff_segment(A,B,seg,verbose=False, titleprefix=""):    
    startNearSeg = hu.nearSegment(A, B, seg)
    endNearSeg = hu.nearSegment(A, B, seg+1)
    startcomp, startd = hu.nearComponent(A, B, seg, startNearSeg)
    endcomp, endd = hu.nearComponent(A, B, seg+1, endNearSeg)
    candidates = hu.candidateComponents(A,B,seg)
    d,k,c1,c2 = h.hausdorff_segment(A, B, seg, startcomp, startd, endcomp, endd, candidates,verbose)
    msg = "Aseg{} ".format(seg)
    if c2==None:
        msg += "to B{}".format(hu.component_label(c1))
    else:
        msg += "to B{},B{}".format(hu.component_label(c1),hu.component_label(c2))
    msg += '   (dist: {:.3f})'.format(d)
    x = A[seg][0] + k * (A[seg+1][0]-A[seg][0])
    y = A[seg][1] + k * (A[seg+1][1]-A[seg][1])
    plot_hausdorff_solution(A, B, d, (x,y), titleprefix + msg,[(True,seg)])

def test_all_hausdorff_segments(A,B,verbose=False,titleprefix=""):
    for a in range(len(A)-1):
        print("polyline A segment {}...".format(a))
        test_hausdorff_segment(A,B,a,verbose,titleprefix)
    for b in range(len(B)-1):
        print("polyline B segment {}...".format(b))
        test_hausdorff_segment(B,A,b,verbose,titleprefix)

def printComponentInfo(A,B,a,comps):
    # print out switch points
    print("\n*** switch points ***")
    colheads = ""
    for c in comps:
        colheads += "\t{}".format(hu.component_label(c))
    t1msgs = []
    t2msgs = []
    for c1 in comps:
        dr1 = hu.distanceRepresentation(A, B, a, c1)
        t1msg = hu.component_label(c1)
        t2msg = hu.component_label(c1)
        for c2 in comps:
            dr2 = hu.distanceRepresentation(A, B, a, c2)
            swpts = hu.switchPoint(dr1, dr2)
            while len(swpts) < 2:
                swpts.append("-")
            t1msg += "\t{}".format(swpts[0])
            t2msg += "\t{}".format(swpts[1])
        t1msgs.append(t1msg)
        t2msgs.append(t2msg)
    print(colheads)
    for msg in t1msgs:
        print(msg)
    print("")
    print(colheads)
    for msg in t2msgs:
        print(msg)

    # print out effective intervals
    print("\n*** effective intervals ***")
    for c in comps:
        cei = hu.effectiveInterval(A, B, a, c)
        print("{}: {}".format(hu.component_label(c),cei))
    
    print("\n")

def test_all_cases_visually():
    for s in range(case(-1)):
        print("CASE {}".format(s))
        A,B = case(s)
        test_all_hausdorff_segments(A, B,titleprefix="Case {}: ".format(s))
    print("\nTO CHECK RESULTS:")
    print("Review all diagrams. The red point should be the location on the")
    print("highlighted orange segment that is furthest from the blue polyline.")
    print("The gray circle should touch the blue polyline at one or two ")
    print("locations, indicating the location(s) on the blue polyline that are ")
    print("closest to the red point.")

unittest.main()
test_all_cases_visually()

print("\nfinished.")