module Geometry.Flat.TwoD.Tessellation.Triangular
  ( tessellate
  , Triangle(..)
  , Vertex(..)
  ) where

import Geometry.Flat.TwoD.Space

data Triangle a = Triangle a a a deriving Show

data Vertex = Vertex{ vPos, vTex :: !Point, vSpots, vWings, vBody :: !Double }
  deriving Show

tessellate :: Int -> Int -> Double -> Point -> Point -> [Triangle Vertex]
tessellate p q s (Point lx ly) (Point hx hy) =
  let t = s * sqrt 3 / 2
      y = floor (ly / t)
      x = floor (lx / s)
      w = ceiling ((hx - lx) / s)
      h = ceiling ((hy - ly) / t)
      g = grid p q
      pc u v = (Point (s * (fromIntegral u + fromIntegral v / 2)) (t * fromIntegral v), g u v)
      b (p0@(Point x0 y0), h0) (Point x1 y1, h1) (Point x2 y2, h2) =
        let pTL = p0
            pTM = Point ((2 * x0 + x1) / 3) ((2 * y0 + y1) / 3)
            pTR = Point ((x0 + 2 * x1) / 3) ((y0 + 2 * y1) / 3)
            pBL = Point ((2 * x0 + x2) / 3) ((2 * y0 + y2) / 3)
            pBR = Point ((x0 + x1 + x2) / 3) ((y0 + y1 + y2) / 3)
            tTL = Point 0    1
            tTM = Point 0.5  1
            tTR = Point 1    1
            tBL = Point 0.25 0
            tBR = Point 0.75 0
            v pp tt = Vertex pp tt h0 h2 h1
            vTL = v pTL tTL
            vTM = v pTM tTM
            vTR = v pTR tTR
            vBL = v pBL tBL
            vBR = v pBR tBR
        in  [ Triangle vTL vTM vBL
            , Triangle vTM vTR vBR
            , Triangle vBL vTM vBR
            ]
      bs v0 v1 v2 = concat
        [ b v0 v1 v2
        , b v1 v2 v0
        , b v2 v0 v1
        ]
      tris u v = concat
        [ bs (pc u v) (pc (u+1) v) (pc u (v+1))
        , bs (pc (u+1) v) (pc (u+1) (v+1)) (pc u (v+1))
        ]
  in  concat
        [ tris (u - z) v
        | v <- [y .. y + h]
        , let z = (v + 1) `div` 2
        , u <- [x .. x + w]
        ]

grid :: Int -> Int -> Int -> Int -> Double
grid p q =
  let n = p * p + q * q + p * q
      i = p `gcd` q
      j = n `div` i
      o = head [ o' | o' <- [0 .. j - 1], (p + q) `mod` j == (o' * i * (q `div` i)) `mod` j ]
  in \x y ->
        let d = i * (y `div` i)
        in  fromIntegral (((x + o * d) `mod` j) + j * (y `mod` i))