{-# LANGUAGE RecordWildCards #-}

module Potato.Flow.RenderCache where

import           Relude

import Potato.Data.Text.Unicode
import           Potato.Flow.Math
import           Potato.Flow.Methods.Types
import           Potato.Flow.SElts
import Potato.Flow.Types
import           Potato.Flow.OwlItem
import Potato.Flow.Owl
import           Potato.Flow.Controller.Types
import           Potato.Flow.Methods.LineTypes


import qualified Data.IntMap             as IM
import qualified Data.Text               as T
import qualified Data.Text.IO as T
import qualified Data.Vector.Unboxed     as V
import qualified Data.Sequence as Seq
import Control.Exception (assert)

-- TODO move these methods to Math
-- | input index must be contained in the box
toPoint :: LBox -> Int -> XY
toPoint :: LBox -> Int -> XY
toPoint (LBox (V2 Int
x Int
y) (V2 Int
w Int
_)) Int
i = forall a. a -> a -> V2 a
V2 (Int
i forall a. Integral a => a -> a -> a
`mod` Int
w forall a. Num a => a -> a -> a
+ Int
x) (Int
i forall a. Integral a => a -> a -> a
`div` Int
w forall a. Num a => a -> a -> a
+ Int
y)

-- | input XY point must be contained in the box
toIndex :: LBox -> XY -> Int
toIndex :: LBox -> XY -> Int
toIndex (LBox (V2 Int
x Int
y) (V2 Int
w Int
_)) (V2 Int
px Int
py) = (Int
pyforall a. Num a => a -> a -> a
-Int
y)forall a. Num a => a -> a -> a
*Int
wforall a. Num a => a -> a -> a
+(Int
pxforall a. Num a => a -> a -> a
-Int
x)

-- | same as above but does bounds checking
toIndexSafe :: LBox -> XY -> Maybe Int
toIndexSafe :: LBox -> XY -> Maybe Int
toIndexSafe LBox
lbx XY
xy = if LBox -> XY -> Bool
does_lBox_contains_XY LBox
lbx XY
xy
  then forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ LBox -> XY -> Int
toIndex LBox
lbx XY
xy
  else forall a. Maybe a
Nothing



-- | WidePChar represents part of a PChar that
-- the Int8 parameter is offset from where the PChar originates from, so for example
-- '😱' <- PChar
--  01 <- Int8 offset parameter
--
-- -1 value for offset means there is no character in the space, the PChar value is ignored in this case
type MWidePChar = (Int8, PChar)

emptyMWidePChar :: MWidePChar 
emptyMWidePChar :: MWidePChar
emptyMWidePChar = (-Int8
1, Char
' ') -- ' ' is a dummy character needed to pad the unboxed vector

-- TODO consider making sparse variant
-- | the LBox may exceed the logical bounding box of the object that is being represented if that object contains wide chars
data PreRender = PreRender (V.Vector (MWidePChar)) LBox deriving (Int -> PreRender -> ShowS
[PreRender] -> ShowS
PreRender -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [PreRender] -> ShowS
$cshowList :: [PreRender] -> ShowS
show :: PreRender -> String
$cshow :: PreRender -> String
showsPrec :: Int -> PreRender -> ShowS
$cshowsPrec :: Int -> PreRender -> ShowS
Show)

emptyPreRender :: PreRender
emptyPreRender :: PreRender
emptyPreRender = Vector MWidePChar -> LBox -> PreRender
PreRender forall a. Unbox a => Vector a
V.empty (XY -> XY -> LBox
LBox XY
0 XY
0)

preRender_lookup :: (HasCallStack) => PreRender -> XY -> MWidePChar
preRender_lookup :: HasCallStack => PreRender -> XY -> MWidePChar
preRender_lookup (PreRender Vector MWidePChar
v LBox
lbox) XY
pos = MWidePChar
r where
  -- we still have to do this check here since toIndex expects point to be contained in box
  r :: MWidePChar
r = if LBox -> XY -> Bool
does_lBox_contains_XY LBox
lbox XY
pos 
    then case Vector MWidePChar
v forall a. Unbox a => Vector a -> Int -> Maybe a
V.!? (LBox -> XY -> Int
toIndex LBox
lbox XY
pos) of
      Maybe MWidePChar
Nothing -> forall a. HasCallStack => Bool -> a -> a
assert Bool
False MWidePChar
emptyMWidePChar
      Just MWidePChar
x -> MWidePChar
x
    else MWidePChar
emptyMWidePChar


-- NOTE OwlIteCache is intended to be used at several stages in the event loop
-- it can be used in Handlers, it can be used when generating SEltDrawers and it can be used for rendering itself
data OwlItemCache =
  -- TODO change to LineAnchorsForRenderList prob
  OwlItemCache_Line LineAnchorsForRender PreRender
  | OwlItemCache_Generic PreRender deriving (Int -> OwlItemCache -> ShowS
[OwlItemCache] -> ShowS
OwlItemCache -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [OwlItemCache] -> ShowS
$cshowList :: [OwlItemCache] -> ShowS
show :: OwlItemCache -> String
$cshow :: OwlItemCache -> String
showsPrec :: Int -> OwlItemCache -> ShowS
$cshowsPrec :: Int -> OwlItemCache -> ShowS
Show)

owlItemCache_preRender :: OwlItemCache -> Maybe PreRender
owlItemCache_preRender :: OwlItemCache -> Maybe PreRender
owlItemCache_preRender = \case
  OwlItemCache_Line LineAnchorsForRender
_ PreRender
x -> forall a. a -> Maybe a
Just PreRender
x
  OwlItemCache_Generic PreRender
x -> forall a. a -> Maybe a
Just PreRender
x

newtype RenderCache = RenderCache {
    -- map for REltId to cache for each owl
    RenderCache -> REltIdMap OwlItemCache
unRenderCache :: REltIdMap OwlItemCache
  } deriving (Int -> RenderCache -> ShowS
[RenderCache] -> ShowS
RenderCache -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [RenderCache] -> ShowS
$cshowList :: [RenderCache] -> ShowS
show :: RenderCache -> String
$cshow :: RenderCache -> String
showsPrec :: Int -> RenderCache -> ShowS
$cshowsPrec :: Int -> RenderCache -> ShowS
Show)

emptyRenderCache :: RenderCache
emptyRenderCache :: RenderCache
emptyRenderCache = REltIdMap OwlItemCache -> RenderCache
RenderCache forall a. IntMap a
IM.empty

renderCache_clearAtKeys :: RenderCache -> [REltId] -> RenderCache
renderCache_clearAtKeys :: RenderCache -> [Int] -> RenderCache
renderCache_clearAtKeys RenderCache
rcache [Int]
rids = REltIdMap OwlItemCache -> RenderCache
RenderCache forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr forall a. Int -> IntMap a -> IntMap a
IM.delete (RenderCache -> REltIdMap OwlItemCache
unRenderCache RenderCache
rcache) [Int]
rids

renderCache_lookup :: RenderCache -> REltId -> Maybe OwlItemCache
renderCache_lookup :: RenderCache -> Int -> Maybe OwlItemCache
renderCache_lookup RenderCache
rcache Int
rid = forall a. Int -> IntMap a -> Maybe a
IM.lookup Int
rid (RenderCache -> REltIdMap OwlItemCache
unRenderCache RenderCache
rcache)


-- UNTESTED
makePreRender :: forall a. (HasOwlTree a) => a -> SEltDrawer -> PreRender
makePreRender :: forall a. HasOwlTree a => a -> SEltDrawer -> PreRender
makePreRender a
ot SEltDrawer {Int
SEltDrawerBoxFn
SEltDrawerRenderFn
_sEltDrawer_maxCharWidth :: SEltDrawer -> Int
_sEltDrawer_renderFn :: SEltDrawer -> SEltDrawerRenderFn
_sEltDrawer_box :: SEltDrawer -> SEltDrawerBoxFn
_sEltDrawer_maxCharWidth :: Int
_sEltDrawer_renderFn :: SEltDrawerRenderFn
_sEltDrawer_box :: SEltDrawerBoxFn
..} = PreRender
r where
  
  lbox' :: LBox
lbox' = SEltDrawerBoxFn
_sEltDrawer_box a
ot 
  lbox :: LBox
lbox@(LBox XY
_ (V2 Int
w Int
_)) = LBox -> (Int, Int, Int, Int) -> LBox
lBox_expand LBox
lbox' (Int
0, Int
_sEltDrawer_maxCharWidthforall a. Num a => a -> a -> a
-Int
1, Int
0, Int
0)
  area :: Int
area = LBox -> Int
lBox_area LBox
lbox

  -- the (Int8, Int8, PChar) is (distance from prev wide pchar, width of prev wide pchar, wide pchar)
  -- width of prev wide pchar could be determined from wide pchar of course but may as well cache it
  unfoldrfn :: (Int, Maybe (Int8, Int8, PChar)) -> Maybe (MWidePChar, (Int, Maybe (Int8, Int8, PChar)))
  unfoldrfn :: (Int, Maybe (Int8, Int8, Char))
-> Maybe (MWidePChar, (Int, Maybe (Int8, Int8, Char)))
unfoldrfn (Int
i, Maybe (Int8, Int8, Char)
mprevwidechar) = Maybe (MWidePChar, (Int, Maybe (Int8, Int8, Char)))
r2 where
    pt :: XY
pt = LBox -> Int -> XY
toPoint LBox
lbox Int
i
    mchar :: Maybe Char
mchar = SEltDrawerRenderFn
_sEltDrawer_renderFn a
ot XY
pt
    mcharwithwidth :: Maybe (Char, Int8)
mcharwithwidth = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\Char
pch -> (Char
pch, Char -> Int8
getPCharWidth Char
pch)) Maybe Char
mchar
    eol :: Bool
eol = (Int
iforall a. Num a => a -> a -> a
+Int
1) forall a. Integral a => a -> a -> a
`mod` Int
w forall a. Eq a => a -> a -> Bool
== Int
0

    moutputchar :: MWidePChar
moutputchar = case Maybe (Int8, Int8, Char)
mprevwidechar of
      Maybe (Int8, Int8, Char)
Nothing        -> case Maybe Char
mchar of  
        Just Char
pch -> (Int8
0, Char
pch)
        Maybe Char
Nothing -> MWidePChar
emptyMWidePChar 
      Just (Int8
a,Int8
_,Char
pch) -> (Int8
a, Char
pch)

    mnextwidechar :: Maybe (Int8, Int8, Char)
mnextwidechar = if Bool
eol 
      then forall a. Maybe a
Nothing 
      else case Maybe (Int8, Int8, Char)
mprevwidechar of 
        Maybe (Int8, Int8, Char)
Nothing -> case Maybe (Char, Int8)
mcharwithwidth of 
          Maybe (Char, Int8)
Nothing -> forall a. Maybe a
Nothing
          Just (Char
pch, Int8
width) -> if Int8
width forall a. Ord a => a -> a -> Bool
> Int8
1
            then forall a. a -> Maybe a
Just (Int8
1,Int8
width,Char
pch)
            else forall a. Maybe a
Nothing
        Just (Int8
a,Int8
b,Char
_) | Int8
a forall a. Eq a => a -> a -> Bool
== Int8
bforall a. Num a => a -> a -> a
-Int8
1 -> forall a. Maybe a
Nothing
        Just (Int8
a,Int8
b,Char
pch) -> forall a. a -> Maybe a
Just (Int8
aforall a. Num a => a -> a -> a
+Int8
1, Int8
b, Char
pch)

    r2 :: Maybe (MWidePChar, (Int, Maybe (Int8, Int8, Char)))
r2 = if Int
i forall a. Eq a => a -> a -> Bool
== Int
area 
      then forall a. Maybe a
Nothing
      else forall a. a -> Maybe a
Just (MWidePChar
moutputchar, (Int
iforall a. Num a => a -> a -> a
+Int
1, Maybe (Int8, Int8, Char)
mnextwidechar))

  r :: PreRender
r = Vector MWidePChar -> LBox -> PreRender
PreRender (forall a b. Unbox a => (b -> Maybe (a, b)) -> b -> Vector a
V.unfoldr (Int, Maybe (Int8, Int8, Char))
-> Maybe (MWidePChar, (Int, Maybe (Int8, Int8, Char)))
unfoldrfn (Int
0,forall a. Maybe a
Nothing)) LBox
lbox