module Reanimate.Math.Render where

import           Codec.Picture.Types
import           Control.Monad
-- import           Data.List
import qualified Data.Text                  as T
import qualified Data.Vector                as V
import           Linear.V2
import           Reanimate.Animation
import           Reanimate.ColorMap
-- import           Reanimate.Constants
import           Reanimate.LaTeX
-- import           Reanimate.Math.Compatible
-- import           Reanimate.Math.EarClip
import           Reanimate.Math.Polygon
import           Reanimate.Math.Common
import           Reanimate.Math.SSSP
import           Reanimate.Math.Triangulate
-- import           Reanimate.Math.Visibility
import           Reanimate.Scene
import           Reanimate.Svg


-- drawVisibility :: Polygon -> Animation
-- drawVisibility p' = mkAnimation 5 $ \t ->
--   let p = cyclePolygon p' (t::Double) in
--   centerUsing (polygonShape p) $
--   mkGroup
--   [ withFillColor "grey" $ polygonShape p
--   , withFillColor "grey" $ polygonDots p
--   , withFillColor "white" $ mkLinePathClosed
--     [ (x,y) | V2 x y <- visibility (map (fmap realToFrac) $ V.toList $ polygonPoints p) ]
--   , let V2 x y = fmap realToFrac $ pAccess p 0 in
--     translate x y $ withFillColor "red" $ mkCircle 0.1
--   -- , withFillColor "blue" $ latex $ T.pack $ show (t)
--   ]

-- drawSSSPVisibility :: Polygon -> Animation
-- drawSSSPVisibility p' = mkAnimation 5 $ \t ->
--   let p = pSetOffset p' (round $ t*(fromIntegral $ pSize p'-1))
--       vis = ssspVisibility p in
--   centerUsing (polygonShape p) $
--   mkGroup
--   [ withFillColor "grey" $ polygonShape p
--   -- , withFillColor "grey" $ polygonDots p
--   -- , withFillColor "white" $ polygonShape vis
--   , let V2 x y = fmap realToFrac $ pAccess p 0 in
--     translate x y $ withFillColor "red" $ mkCircle 0.1
--   -- , withFillColor "blue" $ latex $ T.pack $ show (t)
--   ]

-- drawSSSPVisibilityFast :: Polygon -> Animation
-- drawSSSPVisibilityFast p' = mkAnimation 5 $ \t ->
--   let root = min (pSize p-1) $ (floor $ t*(fromIntegral $ pSize p))
--       p = pSetOffset p' root
--       vis = ssspVisibility p in
--   -- centerUsing (polygonShape p) $
--   mkGroup
--   [ withFillColor "grey" $ polygonShape p
--   , withFillColor "white" $ polygonShape vis
--   , withFillColor "grey" $ polygonNumDots p
--   , let V2 x y = fmap realToFrac $ pAccess p 0 in
--     translate x y $ withFillColor "red" $ mkCircle 0.09
--   -- , withFillColor "blue" $ latex $ T.pack $ show (t)
--   ]

-- drawCompatible :: Polygon -> Polygon -> Animation
-- drawCompatible a b = sceneAnimation $ do
--   newSpriteSVG $ translate (-3) 0 $ mkGroup
--     [ withFillColor "grey" $ polygonShape a
--     , withFillColor "grey" $ polygonNumDots a
--     ]
--   newSpriteSVG $ translate (3) 0 $ mkGroup
--     [ withFillColor "grey" $ polygonShape b
--     , withFillColor "grey" $ polygonNumDots b
--     ]
--   let compat = compatiblyTriangulateP a b
--   forM_ compat $ \(l, r) -> do
--     fork $ play $ staticFrame 1 $
--       translate (-3) 0 $ withStrokeColor "white" $ withStrokeWidth (defaultStrokeWidth*0.2) $
--       withFillOpacity 0 $ polygonShape l
--     fork $ play $ staticFrame 1 $
--       translate (3) 0 $ withStrokeColor "white" $ withStrokeWidth (defaultStrokeWidth*0.2) $
--       withFillOpacity 0 $ polygonShape r

-- drawWindow :: Polygon -> SVG
-- drawWindow p =
--   let mWins = ssspWindows p
--       in
--   mkGroup
--   [ mkGroup
--     [ mkLine (x1,y1) (x2,y2)
--     | (a,b) <- mWins
--     , let V2 x1 y1 = realToFrac <$> a
--           V2 x2 y2 = realToFrac <$> b
--     ]
--   ]

-- drawWindowOverlap :: Polygon -> Int -> Int -> SVG
-- drawWindowOverlap p a b =
--   let aWins = ssspWindows (modOffset p a)
--       bWins = ssspWindows (modOffset p b)
--       in
--   mkGroup
--   [ mkGroup $
--     [ withStrokeColor "green" $mkGroup
--       [ mkLine (x1,y1) (x2,y2)
--       , mkLine (x1',y1') (x2',y2') ]
--     | (a,b) <- aWins
--     , (i,j) <- bWins
--     , a == i || a == j || b == i || b == j
--     , not (sort [a,b] == sort [i,j])
--     , let V2 x1 y1 = realToFrac <$> a
--           V2 x2 y2 = realToFrac <$> b
--           V2 x1' y1' = realToFrac <$> i
--           V2 x2' y2' = realToFrac <$> j
--     ]
--   ]

polygonShape :: Polygon -> SVG
polygonShape p = mkLinePathClosed
  [ (x,y) | V2 x y <- map (fmap realToFrac) $ V.toList (polygonPoints p) ]

polygonDots :: Polygon -> SVG
polygonDots p = mkGroup
  [ translate x y $ mkCircle 0.1 | V2 x y <- V.toList $ V.map (fmap realToFrac) $ polygonPoints p ]

polygonNumDots :: Polygon -> SVG
polygonNumDots p = mkGroup $ reverse
    [ mkGroup
      [ colored n $
        translate x y $ mkCircle circR
      , withFillColor "black" $
        translate x y $ ppNum n ]
    | n <- [0..pSize p-1]
    , let V2 x y = realToFrac <$> pAccess p n ]
  where
    circR = 0.05
    colored n =
      let c = promotePixel $ turbo (fromIntegral (n+2) / fromIntegral (pSize p-1+2))
      in withStrokeColorPixel c . withFillColorPixel c
    ppNum n =
      scaleToSize (circR*1.7) (circR*1.5) $
      center $ latex $ T.pack $ "\\texttt{" ++ show n ++ "}"

-- drawSSSP :: Polygon -> (Polygon -> SSSP) -> Animation
-- drawSSSP p gen = mkAnimation 5 $ \t -> centerUsing outline $
--   let p' = cyclePolygons p !! (round $ t*(fromIntegral $ pSize p-1)) in
--   mkGroup
--   [ outline
--   , renderSSSP p' (gen p')
--   , withFillColor "grey" $ polygonNumDots $ p'
--   ]
--   where
--     outline =
--       withFillColor "grey" $ mkLinePathClosed
--         [ (x,y) | V2 x y <- map (fmap realToFrac) (V.toList (polygonPoints p)  ++ [pAccess p 0]) ]

drawSSSP :: Polygon -> Animation
drawSSSP p = mkAnimation 5 $ \t -> centerUsing (polygonShape p) $
  let root = round $ t*fromIntegral (pSize p-1)
      sTree = polygonSSSP p V.! root in
  mkGroup
  [ polygonShape p
  , renderSSSP p sTree
  , polygonNumDots p
  ]

-- Inline such that 'trees' is only computed once.
{-# INLINE drawSSSPNaive #-}
drawSSSPNaive :: Polygon -> Animation
drawSSSPNaive p = mkAnimation 5 $ \t -> centerUsing (polygonShape p) $
  let root = round $ t*fromIntegral (pSize p-1) in
  mkGroup
  [ polygonShape p
  , renderSSSP p (trees !! root)
  , polygonNumDots p
  ]
  where
    trees =
      [ naive $ pRing $ pCopy $ pSetOffset p i
      | i <- [0 .. pSize p-1]]

renderSSSP :: Polygon -> SSSP -> SVG
renderSSSP p s = withFillOpacity 0 $ withStrokeColor "white" $ mkGroup
  [ mkLinePath (lineFrom i)
  | i <- [0 .. length s-1] ]
  where
    lineFrom i =
      let V2 ax ay = realToFrac <$> pAccess p i
          next = (s V.! i)
      in (ax,ay) : if next == i then [] else lineFrom next

drawTriangulation :: Polygon -> (Ring Rational -> [Triangulation]) -> Animation
drawTriangulation p gen = sceneAnimation $
  forM_ (gen $ pRing p) $ \t -> play $ staticFrame 1 $ renderTriangulation p t

renderTriangulation :: Polygon -> Triangulation -> SVG
renderTriangulation p t = center $ mkGroup
  [ withFillColor "grey" $ polygonShape p
  , withStrokeColor "white" $ mkGroup $ concat
    [ [ mkLine (ax,ay) (bx,by) ]
    | i <- [0..pSize p-1]
    , y <- t V.! i
    , let V2 ax ay = realToFrac <$> rawAccess i
          V2 bx by = realToFrac <$> rawAccess y
    ]
  , withFillColor "grey" $ polygonNumDots p
  ]
  where
    rawAccess x = polygonPoints p V.! x

renderDual :: Ring Rational -> Dual -> SVG
renderDual ring d = case d of
    Dual (a,b,c) l r -> mkGroup
      [ withFillColor "blue" $ mkTrig a b c
      , worker c a l
      , worker b c r
      ]
  where
    mkTrig a b c =
      let V2 x1 y1 = realToFrac <$> ringAccess ring a
          V2 x2 y2 = realToFrac <$> ringAccess ring b
          V2 x3 y3 = realToFrac <$> ringAccess ring c
      in mkLinePathClosed [ (x1, y1), (x2,y2), (x3,y3) ]
    worker _p1 _p2 EmptyDual = mkGroup []
    worker p1 p2 (NodeDual x l r) = mkGroup
      [ mkTrig p1 p2 x
      , worker x p2 l
      , worker p1 x r
      ]