{-# LANGUAGE DataKinds           #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_HADDOCK hide #-}
module Reanimate.Math.Triangulate
  ( Triangulation
  , edgesToTriangulation
  , edgesToTriangulationM
  , trianglesToTriangulation
  , trianglesToTriangulationM
  , triangulate
  )
where

import           Algorithms.Geometry.PolygonTriangulation.Triangulate (triangulate')
import           Algorithms.Geometry.PolygonTriangulation.Types
import           Control.Lens
import           Control.Monad
import           Control.Monad.ST
import           Data.Ext
import           Data.Geometry.PlanarSubdivision                      (PolygonFaceData)
import           Data.Geometry.Point
import           Data.Geometry.Polygon
import qualified Data.IntSet                                          as ISet
import qualified Data.PlaneGraph as Geo
import           Data.Proxy
import qualified Data.Vector                                          as V
import qualified Data.Vector.Mutable                                  as MV
import           Linear.V2
import           Reanimate.Math.Common
-- Max edges: n-2
-- Each edge is represented twice: 2n-4
-- Flat structure:
--   edges   :: V.Vector Int -- max length (2n-4)
--   offsets :: V.Vector Int -- length n
-- Combine the two vectors? < n => offsets, >= n => edges?
type Triangulation = V.Vector [Int]

-- FIXME: Move to Common or a Triangulation module
-- O(n)
edgesToTriangulation :: Int -> [(Int, Int)] -> Triangulation
edgesToTriangulation size edges = runST $ do
  v <- edgesToTriangulationM size edges
  V.unsafeFreeze v

edgesToTriangulationM :: Int -> [(Int, Int)] -> ST s (V.MVector s [Int])
edgesToTriangulationM size edges = do
  v <- MV.replicate size []
  forM_ edges $ \(e1, e2) -> do
    MV.modify v (e1 :) e2
    MV.modify v (e2 :) e1
  forM_ [0 .. size - 1] $ \i -> MV.modify v (ISet.toList . ISet.fromList) i
  return v

trianglesToTriangulation :: Int -> V.Vector (Int, Int, Int) -> Triangulation
trianglesToTriangulation size edges = runST $ do
  v <- trianglesToTriangulationM size edges
  V.unsafeFreeze v

trianglesToTriangulationM
  :: Int -> V.Vector (Int, Int, Int) -> ST s (V.MVector s [Int])
trianglesToTriangulationM size trigs = do
  v <- MV.replicate size []
  forM_ (V.toList trigs) $ \(a, b, c) -> do
    MV.modify v (\x -> b : c : x) a
    MV.modify v (\x -> a : c : x) b
    MV.modify v (\x -> a : b : x) c
  forM_ [0 .. size - 1] $ \i -> MV.modify v (ISet.toList . ISet.fromList) i
  return v


triangulate :: forall a. (Fractional a, Ord a) => Ring a -> Triangulation
triangulate r = edgesToTriangulation (ringSize r) ds
  where
    ds :: [(Int,Int)]
    ds =
      [ (a^.Geo.vData, b^.Geo.vData)
      | (d, Diagonal) <- V.toList (Geo.edges pg)
      , let (a,b) = Geo.endPointData d pg ]
    pg :: Geo.PlaneGraph () Int PolygonEdgeType PolygonFaceData a
    pg = triangulate' Proxy p
    p :: SimplePolygon Int a
    p = fromPoints $
      [ Point2 x y :+ n
      | (n,V2 x y) <- zip [0..] (V.toList (ringUnpack r)) ]
    -- ringUnpack