# Implementation of module {shape}.
# Last edited on 2021-02-08 20:29:45 by jstolfi

import shape
import rootray
import trafo
import sys
from math import sqrt, sin, cos, floor, ceil, inf, nan, pi

class Shape_IMP:
  # Must be subclassed.

def vacuum():
  return None

def plenum():
  return (1, None)

def is_vacuum(F):
  if F == None: return True
  if isinstance(F,shape.Shape): return False
  assert type(F) is tuple
  assert len(F) >= 2
  if F[0] == 0:
    if len(F) == 2: return True
    if len(F) == 3 and is_vacuum(F[2]): return True
    return False
  elif F[0] == 1:
    if len(F) == 2: return False
    if len(F) == 3 and is_plenum(F[2]): return True
    return False

def is_plenum(F):
  if F == None: return False
  if isinstance(F,shape.Shape): return False
  assert type(F) is tuple
  assert len(F) >= 2
  if F[0] == 0:
    if len(F) == 2: return False
    if len(F) == 3 and is_plenum(F[2]): return True
    return False
  elif F[0] == 1:
    if len(F) == 2: return True
    if len(F) == 3 and is_vacuum(F[2]): return True
    return False

def complement(F):
  if F == None: return plenum()
  if isinstance(F,shape.Shape): return (1,F)
  assert type(F) is tuple
  assert len(F) >= 2
  assert type(F[0]) is int and (F[0] == 0 or F[0] == 1)
  return (1-F[0],) + F[1:]

def union(FL):
  assert type(FL) is list or type(FL) is tuple
  if len(FL) == 0: return vacuum()
  H = [] # List of the non-trivial shapes in {FL}.
  for Fk in TL:
    if is_vacuum(Fk): 
      pass
    elif is_plenum(Fk):
      return plenum()
    else:
      H.append(Tk)
  if len(H) == 0: return vacuum()
  return (0,None) + tuple(H)

def intersection(FL):
  assert type(FL) is list or type(FL) is tuple
  if len(FL) == 0: return plenum()
  H = [] # List of complements of the non-trivial shapes in {FL}.
  for Fk in FL:
    if is_plenum(Fk): 
      pass
    elif is_vacuum(Fk):
      return vacuum()
    else:
      H.append(complement(Fk))
  if len(H) == 0: return plenum()
  return (1,None) + tuple(H)

def difference(F, FL):
  if is_vacuum(F): return None
  if is_plenum(F): return complement(union(FL))
  assert type(FL) is list or type(FL) is tuple
  if len(FL) == 0: return F
  H = [] # List of complements of the  non-trivialshapes in {FL}.
  for Fk in FL:
    if is_vacuum(Fk): 
      pass
    elif is_plenum(Fk):
      return vacuum()
    else:
      H.append(Fk)
  if len(H) == 0: return F
  return (1, None, complement(F)) + tuple(H)

def transform(F,T):
  if is_vacuum(F): return vacuum()
  if is_plenum(F): return plenum()
  if isinstance(F,shape.Shape): return (0,T,F)
  assert type(F) is tuple
  assert len(F) >= 2
  Tnew = trafo.compose((F[1], T))
  return (F[0],Tnew) + F[2:]
    
def cbit(F):
  if F == None or isinstance(F, shape.Shape): return 0
  assert type(F) is tuple
  assert len(F) >= 2
  return F[0]

def trafo(F):
  if F == None or isinstance(F, shape.Shape): return None
  assert type(F) is tuple
  assert len(F) >= 2
  return F[1]
  
def nterms(F):
  if F == None: return 0
  if isinstance(F, shape.Shape): return 1
  assert type(F) is tuple
  m = len(F)
  assert m >= 2
  return m - 2
  
def term(F,k):
  assert F != None, "{None} has no terms"
  if isinstance(F, shape.Shape): 
    assert k == 0, "invalid term index"
    return F
  assert type(F) is tuple
  m = len(F)
  assert m >= 2 and 0 <= k and k < m-2
  return F[k+2]

def simplify(F):
  if F == None or isinstance(F, shape.Shape): return F
  sys.stderr.write("??? {shape.simplify}: to be written ???\n")
  return F
  
