#! /bin/usr/python3
# Test program for module {contact}
# Last edited on 2021-03-21 16:02:42 by jstolfi

import contact
import contact_example
import move
import move_parms
import path
import block
import palette
import hacks
import job_parms
import rn
import pyx
import sys
from math import sqrt, sin, cos, floor, ceil, pi, nan, inf

parms = job_parms.typical()
parms['solid_raster_width'] = 1.00
parms['contour_trace_width'] = 0.50

mp_jump = move_parms.make_for_jumps(parms)
mp_cont = move_parms.make_for_contours(parms)
mp_fill = move_parms.make_for_fillings(parms)

wd_fill = move_parms.width(mp_fill)
wd_cont = move_parms.width(mp_cont)

def test_make_etc():

  sys.stderr.write("--- testing {make,from_moves,from_move_lists,from_paths} ---\n")
  
  wdf = move_parms.width(mp_fill)
  wdc = move_parms.width(mp_cont)
  y0 = 1
  y1 = y0 + (wdf+wdc)/2
  y2 = y1 + wdc
  
  eps = 0.02
  
  pa0 = (  0, y0 )
  qa0 = (  2, y0 )
  pa1 = (  4, y0 )
  qa1 = ( 11, y0 )
  
  pb0 = (  1, y1 + 0.1*eps )
  qb0 = (  8, y1 - 0.3*eps )
  pb1 = ( 10, y1 )
  qb1 = ( 11, y1 )
  
  pc0 = (  0, y2 )
  qc0 = (  2, y2 )
  pc1 = (  4, y2 )
  qc1 = (  5, y2 )
  pc2 = (  7, y2 )
  qc2 = ( 11, y2 )
  
  tra0 = move.make(pa0, qa0, mp_fill)
  tra1 = move.make(pa1, qa1, mp_fill)
  
  trb0 = move.make(pb0, qb0, mp_cont)
  trb1 = move.make(pb1, qb1, mp_cont)

  trc0 = move.make(pc0, qc0, mp_cont)
  trc1 = move.make(pc1, qc1, mp_cont)
  trc2 = move.make(pc2, qc2, mp_cont)
  
  jmb0b1 = move.make(qb0, pb1, mp_jump)
  jma0c0 = move.make(qa0, pc0, mp_jump)
  jmc0a1 = move.make(qc0, pa1, mp_jump)
  jma1c2 = move.make(qa1, pc2, mp_jump)

  ctA = contact.from_moves(tra0, trb0, 0.9, 0.49)
  assert ctA != None
  assert contact.side(ctA, 0) == tra0
  assert contact.side(ctA, 1) == trb0
  ptA = contact.endpoints(ctA)
  exA = ( (1, y0 + wdf/2), (2, y0 + wdf/2) )
  sys.stderr.write("ctA.endpoints = ( %.9f %.9f ) ( %.9f %.9f )" % (ptA[0]+ptA[1]))
  sys.stderr.write(" length = %.9f\n" % rn.dist(ptA[0], ptA[1]))
  sys.stderr.write("expected      = ( %.9f %.9f ) ( %.9f %.9f )\n" % (exA[0]+exA[1]))
  assert rn.dist(ptA[0], exA[0]) < eps
  assert rn.dist(ptA[1], exA[1]) < eps
 
  # Check length limits:
  ctAx = contact.from_moves(tra0, trb0, 1.1, 0.00)
  assert ctAx == None
  ctAy = contact.from_moves(tra0, trb0, 0.0, 0.51)
  assert ctAy == None

  ctB = contact.from_moves(tra0, trc0, 0, 0)
  assert ctB == None
 
  ctC = contact.from_moves(tra0, jmb0b1, 0, 0)
  assert ctC == None
  
  MVS0 = [ tra0, jma0c0, trc0, jmc0a1, tra1, jma1c2, trc2, ]
  MVS1 = [ trb0, jmb0b1, ]
  ph0 = path.from_moves(MVS0)
  ph1 = path.from_moves(MVS1)
  for ifun in range(2):
    szmin = 0.9
    rszmin = 0.19
    if ifun == 0:
      CTS = contact.from_move_lists(MVS0, MVS1, szmin, rszmin)
    else:
      CTS = contact.from_paths(ph0, ph1, szmin, rszmin) 
    for k in range(len(CTS)):
      ctk = CTS[k]
      sys.stderr.write("contact %d:\n" % k)
      contact.show(sys.stderr, ctk)
    assert len(CTS) == 4
    assert contact.side(CTS[0], 0) == tra0
    assert contact.side(CTS[0], 1) == trb0
    assert contact.side(CTS[1], 0) == trc0
    assert contact.side(CTS[1], 1) == trb0
    assert contact.side(CTS[2], 0) == tra1
    assert contact.side(CTS[2], 1) == trb0
    assert contact.side(CTS[3], 0) == trc2
    assert contact.side(CTS[3], 1) == trb0

  return 
  # ----------------------------------------------------------------------

def test_plot_to_files():

  sys.stderr.write("--- testing {plot_single} ---\n")

  tag = "plot_to_files"
  CTS, OPHS, TRS = contact_example.misc_B(mp_fill, mp_jump)
  nph = len(OPHS)

  CLRS = [ pyx.color.rgb(0.300, 0.600, 0.000), ]
  nclr = len(CLRS)

  wd_axes = 0.05*wd_fill   
  clr_ct = pyx.color.rgb.red # Color for contact lines

  for tics in (False, True):
    for ct_arrows in (False, True):
      if not (tics and ct_arrows):
        fname = ("tests/out/contact_TST_%s_tc%d_ar%d" % (tag,int(tics),int(ct_arrows)))
        contact.plot_to_files(fname, CTS, clr_ct, OPHS, CLRS, wd_axes, tics, ct_arrows)
  return
  # ----------------------------------------------------------------------

def test_plot_single():

  sys.stderr.write("--- testing {plot_single} ---\n")

  tag = "plot_single"
  CTS, OPHS, TRS = contact_example.misc_B(mp_fill, mp_jump)
  nph = len(OPHS)

  CLRS = hacks.trace_colors(nph)
  nclr = len(CLRS)

  # Get the enclosing box of the paths:
  box = path.bbox(OPHS)
  pbox = hacks.round_box(box, 1)
  
  wd_axes = 0.05*wd_fill   
  wd_ct = 1.5*wd_axes
  clr_ct = pyx.color.rgb.red # Color for contact lines

  for tics in (False, True):
    for ct_arrows in (False, True):
      if not (tics and ct_arrows):
        c = pyx.canvas.canvas()
        wd_grid = 0.03
        hacks.plot_grid(c, None, wd_grid, None, pbox, 0.20, 1, 1)

        axes = False
        dots = True
        ph_arrows = True
        matter = False
        path.plot_standard(c, OPHS, None, None, CLRS, wd_axes, axes, dots, ph_arrows, matter)

        sz_tics = wd_ct if tics else 0
        nct = len(CTS)
        for k in range(nct):
          contact.plot_single(c, CTS[k], None, clr=clr_ct, wd_line=wd_ct, sz_tic=sz_tics, arrow=ct_arrows)

        hacks.write_plot(c, ("tests/out/contact_TST_%s_tc%d_ar%d" % (tag,int(tics),int(ct_arrows))))

  return
  # ----------------------------------------------------------------------

def test_endpoints_sides():
  sys.stderr.write("--- testing {endpoints,pmid,side,which_side,covindices} ---\n")
  
  CTS, OPHS, TRS = contact_example.misc_B(mp_fill, mp_jump)
  
  # Testing {pmid}
  q00, q01 = contact.endpoints(CTS[0])
  m1a = rn.mix(0.50, q00, 0.50, q01)
  m1b = contact.pmid(CTS[0])
  assert rn.dist(m1a, m1b) < 1.0e-8
 
  # This validation depends on the specific {OPHS,CTS} created above:
  for   ct,    mv0,   mv1,   oph0,   ix0, oph1,   ix1 in ( 
      ( CTS[0], TRS[0], TRS[1], OPHS[0], 0,   OPHS[2], 2 ),
      ( CTS[1], TRS[1], TRS[2], OPHS[2], 2,   OPHS[1], 0 ),
      ( CTS[2], TRS[0], TRS[2], OPHS[0], 0,   OPHS[1], 0 ),
      ( CTS[3], TRS[2], TRS[3], OPHS[1], 0,   OPHS[2], 0 ),
      ( CTS[4], TRS[1], TRS[4], OPHS[2], 2,   OPHS[1], 4 ),
      ( CTS[5], TRS[7], TRS[9], OPHS[3], 0,   OPHS[4], 0 ),
      ( CTS[6], TRS[8], TRS[9], OPHS[3], 2,   OPHS[4], 0 ),
      ( CTS[7], TRS[7], TRS[5], OPHS[3], 0,   OPHS[1], 2 ),
   ):
      contact.show(sys.stderr, ct)
      assert contact.side(ct, 0) == mv0
      assert contact.side(ct, 1) == mv1
      assert contact.which_side(mv0, ct) == 0
      assert contact.which_side(mv1, ct) == 1
      assert contact.which_side(TRS[6], ct) == None

      omv0 = path.elem(oph0, ix0)
      omv1 = path.elem(oph1, ix1)
      mv0a, drm0 = move.unpack(omv0)
      mv1a, drm1 = move.unpack(omv1)
      assert mv0a == mv0
      assert mv1a == mv1
      
      assert contact.covindices(oph0, ct) == ( ix0, None )
      assert contact.covindices(oph1, ct) == ( None, ix1 )

      ph0, drp0 = path.unpack(oph0)
      ph1, drp1 = path.unpack(oph1)
      if ph0 != ph1:
        nmv0 = path.nelems(oph0)
        nmv1 = path.nelems(oph1)
        gap = (path.pfin(oph0) != path.pini(oph1))
        use_jumps = True
        use_links = False
        oph01 = path.concat((oph0, oph1), use_jumps, use_links, mp_jump)
        assert path.nelems(oph01) == nmv0 + int(gap) + nmv1
        assert contact.covindices(oph01, ct) == ( ix0, nmv0+int(gap)+ix1 )

  return
  # ----------------------------------------------------------------------

def test_coverage_times():
  sys.stderr.write("--- testing {covtime,covtimes,tcool,max_tcool,min_tcov} ---\n")

  CTS, OPHS, TRS = contact_example.misc_B(mp_fill, mp_jump)
  
  for dr in 0, 1:
    # Variable {dr} defines the orientation of the two test paths.
    for od in 0, 1:
      # Variable {od} defines the order of the two test paths.
      # sys.stderr.write("- - - - - - - - - - - - - - - - - - - - - - - - \n")
      # sys.stderr.write("od = %d  dr = %d\n" % (od,dr))
      nclosed = 0
      nactive = 0
      ninactive = 0
      # Make a test path from two of the paths:
      if od == 0:
        oph0 = path.spin(OPHS[1],dr)
        oph1 = path.spin(OPHS[2],dr)
      else:
        oph0 = path.spin(OPHS[2],dr)
        oph1 = path.spin(OPHS[1],dr)
      use_jump = True
      use_link = False
      oph = path.concat([oph0, oph1,], use_jump, use_link, mp_jump)
      assert path.nelems(oph) == path.nelems(oph0) + 1 + path.nelems(oph1)

      # Compute the connector time:
      omv_prev = move.rev(path.elem(path.rev(oph0),0))
      omv_next = path.elem(oph1,0)
      tconn = move.connector_extime(omv_prev, omv_next, use_jump, use_link, mp_jump)
      assert abs((path.extime(oph0) + tconn + path.extime(oph1)) - path.extime(oph)) < 1.0e-8
      
      # Compute {max_tcool,pair_max_tcool,min_tcov} by hand:
      maxtcool = -inf
      pmaxtcool = -inf
      mintcov = +inf
      for ct in CTS:
        ixs = contact.covindices(oph, ct)
        tcs = contact.covtimes(oph, ct)
        tc0 = contact.covtime(oph, ixs[0], ct, 0); assert tcs[0] == tc0
        tc1 = contact.covtime(oph, ixs[1], ct, 1); assert tcs[1] == tc1
        # sys.stderr.write("tcs = %s\n" % str(tcs))
        for i in range(2):
          # Check {covindices} and {covtimes}: 
          imv = ixs[i]
          if imv == None:
            assert path.find(oph, contact.side(ct, i)) == None
            assert tcs[i] == None
          else:
            omvk = path.elem(oph, imv)
            mvk, drk = move.unpack(omvk)
            assert mvk == contact.side(ct, i)
            if drk == 0:
              assert tcs[i] == path.tini(oph,imv) + contact.tcov(ct, i)
            else:
              assert tcs[i] == path.tfin(oph,imv) - contact.tcov(ct, i)
            assert tcs[i] <= path.extime(oph)
        # Recompute {max_tcool,min_tcov} from scratch:
        tcool = contact.tcool(oph, ct)
        if tcs[0] == None and tcs[1] == None:
          # Contact is inactive rel to {oph}:
          assert tcool == None
          ninactive += 1
        elif tcs[0] == None or tcs[1] == None:
          # Contact is active rel to {oph}:
          assert tcool == None
          tci = tcs[0] if tcs[0] != None else tcs[1]
          if tci < mintcov: mintcov = tci
          nactive += 1
        else:
          # Contact is closed by {oph}:
          # sys.stderr.write("tcs: %s tcool: %.6f\n" % (str(tcs),tcool))
          assert tcool == abs(tcs[0] - tcs[1])
          if tcool > maxtcool: maxtcool = tcool
          nclosed += 1
        ixs0 = contact.covindices(oph0, ct)
        ixs1 = contact.covindices(oph1, ct)
        ptcool = contact.pair_tcool(oph0, tconn, oph1, ct)
        if ixs0[0] == None or ixs1[1] == None:
          assert ptcool == None
        else:
          assert abs(ptcool - tcool) < 1.0e-8
          if ptcool > pmaxtcool: pmaxtcool = ptcool
      # These tests are specific for the {create_test} above:
      assert nclosed == 3
      assert nactive == 3
      assert ninactive == 2
      assert contact.max_tcool(oph, CTS) == maxtcool
      assert contact.pair_max_tcool(oph0, tconn, oph1, CTS) == pmaxtcool
      assert contact.min_tcov(oph, CTS) == mintcov

  return
  # ----------------------------------------------------------------------

test_make_etc()
test_endpoints_sides()
test_coverage_times()
test_plot_single()
test_plot_to_files()
