# Implementation of module {path_hp}.
# Last edited on 2021-05-27 08:02:16 by jstolfi

import path_hp
import path
import move
import contact
import block

def get_contacts(ph):
  assert isinstance(ph, path.Path)
  return ph.ctss
  # ----------------------------------------------------------------------

def clear_contacts(oph):
  ph, dr = path.unpack(oph)
  ph.contacts = ([], [])
  return 
  # ----------------------------------------------------------------------

def add_contact(oph, i, ct):
  ph, dr = path.unpack(oph)
  assert type(i) is int and (i == 0 or i == 1)
  ph.contacts[i].append(ct)
  return  
  # ----------------------------------------------------------------------

def get_contacts(oph, i):
  ph, dr = path.unpack(oph)
  assert type(i) is int and (i == 0 or i == 1)
  return ph.contacts[i]
  # ----------------------------------------------------------------------

def get_block(ph):
  assert isinstance(ph, path.Path)
  return ph.block
  # ----------------------------------------------------------------------
 
def set_block(ph, bc):
  assert isinstance(ph, path.Path)
  assert isinstance(bc, block.Block)
  ph.block = bc
  return  
  # ----------------------------------------------------------------------

# The list {ph.links[0]} has all link paths that end
# at {pini(ph)}.  The list {ph.links[1]} has all link paths
# that start at {pfin(ph)}.

def clear_links(oph):
  ph, dr = path.unpack(oph)
  ph.links = ([],[])
  return
  # ----------------------------------------------------------------------
  
def add_link(oph, olk):
  assert tuple(path.pfin(olk)) == tuple(path.pini(oph))
  ph, dr = path.unpack(oph)
  if dr == 0:
    # The link ends at {pini(ph)}:
    ph.links[0].append(olk)
  else:
    # The link ends at {pfin(ph)}
    ph.links[1].append(path.rev(olk))
  return
  # ----------------------------------------------------------------------
  
def set_links(oph, OLKS):
  clear_links(oph);
  for olk in OLKS: add_link(oph, olk)
  return
  # ----------------------------------------------------------------------

def get_links(oph):
  ph, dr = path.unpack(oph)
  if dr == 0:
    OLKS = ph.links[0]
  else:
    OLKS = [path.rev(olk) for olk in ph.links[1]]
  return OLKS
  # ----------------------------------------------------------------------
  
def get_connecting_link(oph0, oph1):
  OLKS0 = get_links(path.rev(oph0))
  OLKS1 = get_links(oph1)
  for olk0 in OLKS0:
    for olk1 in OLKS1:
      if (path.unpack(path.rev(olk0))) == (path.unpack(olk1)):
        olk = olk1
        assert path.pini(olk) == path.pfin(oph0)
        assert path.pfin(olk) == path.pini(oph1)
        return olk
  return None
  # ----------------------------------------------------------------------

def get_blocks_in_contour(cr):
  ph, dr = path.unpack(cr)
  return ph.fill_BCS
  # ----------------------------------------------------------------------

def assign_blocks_to_contours(CRS, BCS):
  
  nbc = len(BCS)
  ncr = len(CRS)

  # Convert each contour {CRS[icr]} to a {shapely} polygon {PGS[icr]}:
  PGS = []
  for cr in CRS:
    PTS_cr = [ move.pini(path.elem(cr, k)) for k in range(path.nelems(cr)) ]
    PGS.append(Polygon(PTS_cr))

  # Get a {shapely} point {PTS[ibc]} on each block:
  PTS_bc = [ Point(path.pini(block.choice(bc, 0))) for bc in BCS ]

  # We will set {icr_inner[ibc]} to the index of the innermost contour that contains {BCS[ibc]}.
  icr_inner = [None]*nbc 
  
  # Scan all contours and all blocks:
  for icr in range(ncr):
    pg = PGS[icr]
    for ibc in range(nbc):
      point = PTS[ibc]
      if (pg.contains(point)): 
        icr_cur = icr_inner[ibc] # Current innermost containg contour.
        if icr_cur == None or pg.contains(PGS[icr_cur]):
          icr_inner[ibc] = icr
        else:
          assert PGS[icr_cur].contains(pg), "contours are not properly nested"
  
  # Now assign blocks to contours:
  for cr in CRS: 
    ph, dr = unpack(cr)
    ph.fill_BCS = []
  for ibc in range(nbc):
    icr = icr_inner[ibc]
    assert icr != None, "block is not inside any contour"
    cr = CRS[icr]
    ph, dr = unpack(cr)
    ph.fill_BCS.append(BCS[ibc])
  return
  # ----------------------------------------------------------------------

# GROUPS

def set_group(oph, igr):
  ph, dr = path.unpack(oph)
  ph.group = igr
  return
  # ----------------------------------------------------------------------
  
def get_group(oph):
  ph, dr = path.unpack(oph)
  return ph.group
  # ----------------------------------------------------------------------

def separate_paths_by_group(OPHS):
  ngr = 0       # Number of distinct groups seen so far.
  maxgr = None  # Largest group index seen so far.
  GRSS = []      # {GRSS[igr]} is list of all elems ofgroup {igr}.
  for oph in OPHS:
    # Obtain and validate {igr}
    igr = path_hp.get_group(oph)
    assert igr != None, "orginal group not defined"
    assert type(igr) is int and  igr >= 0
    # Extend {GRSS} to include {GRSS[igr]}:
    if igr >= len(GRSS): GRSS += [None]*(igr + 1 - len(GRSS))
    # Add element to orig group list:
    if GRSS[igr] == None:
      ngr += 1
      GRSS[igr] = []
    GRSS[igr].append(oph)
  return GRSS, ngr
  # ----------------------------------------------------------------------

