# Hasty conversion from the Rust in prosha/src/examples.rs & Barbs

import numpy as np

import xform

# Mnemonics:
X = np.array([1.0, 0.0, 0.0])
Y = np.array([0.0, 1.0, 0.0])
Z = np.array([0.0, 0.0, 1.0])

class Barbs(object):
    def __init__(self, scale_min=0.02):
        self.scale_min = scale_min
        # Incremental transform from each stage to the next:
        self.base_incr = (xform.Transform().
                          translate(0, 0, 1).
                          rotate(Z, 0.15).
                          rotate(X, 0.1).
                          scale(0.95))
        self.barb_incr = (xform.Transform().
                          translate(0, 0, 0.5).
                          rotate(Y, -0.2).
                          scale(0.8))
        # 'Base' vertices, used throughout:
        self.base = np.array([
            [-0.5, -0.5, 0.0],
            [-0.5,  0.5, 0.0],
            [ 0.5,  0.5, 0.0],
            [ 0.5, -0.5, 0.0],
        ])
        # self.sides[i] gives transformation from a 'base' layer to
        # the i'th side (0 to 3):
        self.sides = [
            xform.Transform().
            rotate(Z, -i * np.pi/2).
            rotate(Y, -np.pi/2).
            translate(0.5, 0.0, 0.5)
            for i in range(4)
        ]
        # Face & vertex accumulators:
        self.faces = []
        # self.faces will be a list of tuples (each one of length 4
        # and containing indices into self.verts)
        self.verts = []
        # self.verts will be a list of np.array of shape (3,) but will
        # be converted last-minute to tuples. (Why: we need to refer
        # to prior vertices and arithmetic is much easier from an
        # array, but Blender eventually needs tuples.)
        self.creases_side = set()
        self.creases_joint = set()

    def run(self, iters) -> (list, list):
        # 'iters' is ignored for now
        # 
        # Make seed vertices, use them for 'bottom' face, and recurse:
        self.verts.extend(self.base)
        self.faces.append((0, 1, 2, 3))
        self.main(iters, xform.Transform(), [0,1,2,3])
        verts = [tuple(v) for v in self.verts]
        faces = [tuple(f) for f in self.faces]
        return verts, faces

    def limit_check(self, xform: xform.Transform, iters) -> bool:
        # Assume all scales are the same (for now)
        sx,_,_ = xform.get_scale()
        return sx < self.scale_min

    def main(self, iters, xform, bound):

        if self.limit_check(xform, iters):
            # Note opposite winding order
            verts = [bound[i] for i in [3,2,1,0]]
            self.faces.append(verts)
            return

        xform2 = xform.compose(self.base_incr)
        g = xform2.apply_to(self.base)
        a0 = len(self.verts)
        self.verts.extend(g)

        # TODO: Turn this to a cleaner loop?
        self.main(iters - 1, xform2, [a0, a0 + 1, a0 + 2, a0 + 3])
        self.barb(iters - 1, xform.compose(self.sides[0]),
                  [bound[0], bound[1], a0 + 1, a0 + 0])
        self.barb(iters - 1, xform.compose(self.sides[1]),
                  [bound[1], bound[2], a0 + 2, a0 + 1])
        self.barb(iters - 1, xform.compose(self.sides[2]),
                  [bound[2], bound[3], a0 + 3, a0 + 2])
        self.barb(iters - 1, xform.compose(self.sides[3]),
                  [bound[3], bound[0], a0 + 0, a0 + 3])
        for i in range(4):
            j = (i + 1) % 4
            self.creases_joint.add((a0 + i, a0 + j))
            self.creases_side.add((bound[i], a0 + i))

    def barb(self, iters, xform, bound):
        if self.limit_check(xform, iters):
            # Note opposite winding order
            verts = [bound[i] for i in [3,2,1,0]]
            self.faces.append(verts)
            return

        xform2 = xform.compose(self.barb_incr)
        g = xform2.apply_to(self.base)
        offset = len(self.verts)
        self.verts.extend(g)

        # Connect parallel faces:
        n = len(self.base)
        for i, b0 in enumerate(bound):
            j = (i + 1) % n
            b1 = bound[j]
            a0 = offset + i
            a1 = offset + j
            self.faces.append([a0, a1, b1, b0])

        self.barb(iters-1, xform2, [offset, offset + 1, offset + 2, offset + 3])
