{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecursiveDo #-}
{-# LANGUAGE ScopedTypeVariables #-}
import Control.Lens
import Control.Monad.Identity
import Control.Monad.IO.Class
import Data.Dependent.Map (DMap)
import Data.Functor.Constant
import Data.Functor.Misc
import Data.List
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Monoid
import Data.Ord
import qualified Data.Text as T
import Data.Time.Clock
import Reflex.Dom
import System.Random

main :: IO ()
main = mainWidget $ do
  let f _ (a, b, c) = el "tr" $ do
        mapM_ (el "td" . text . T.pack . show) [a, b, c]
        _ <- el "td" $ inputElement def
        return ()
  let numKeys = 1000 :: Int
      keys = take numKeys [0 :: Int ..]
      values :: [(Int, Int, Int)]
      values = zip3 (randoms (mkStdGen 0)) (randoms (mkStdGen 1)) (randoms (mkStdGen 2))
      testMap = Map.fromList $ zip keys values
      resortButtons = el "div" $ leftmost <$> sequence
        [ (comparing (view _1) <$) <$> button "A ASC"
        , (flip (comparing (view _1)) <$) <$> button "A DESC"
        , (comparing (view _2) <$) <$> button "B ASC"
        , (flip (comparing (view _2)) <$) <$> button "B DESC"
        , (comparing (view _3) <$) <$> button "C ASC"
        , (flip (comparing (view _3)) <$) <$> button "C DESC"
        ]
  el "h1" $ text "Improved list sorting"
  el "p" $ text $ "This app shows a table of " <> T.pack (show numKeys) <> " rows, and allows you to re-sort by columns a, b, and c, which are full of pseudorandom numbers.  Each row also includes an element with internal state (a textbox) to demonstrate that the state is preserved, only when using the new way."
  el "h3" $ text "Re-sort the list the OLD way (by redrawing everything)"
  resortSlow <- resortButtons
  displayRedrawTime resortSlow
  el "h3" $ text "Re-sort the list the NEW way (by reordering existing elements)"
  resort <- resortButtons
  displayRedrawTime resort
  el "hr" blank
  el "table" $ do
    el "tr" $ do
      el "th" $ text "A"
      el "th" $ text "B"
      el "th" $ text "C"
      el "th" $ text "Stateful Element (textbox)"
    simpleSortableList f testMap resort resortSlow
  return ()

displayRedrawTime :: (DomBuilder t m, MonadHold t m, PostBuild t m, PerformEvent t m, TriggerEvent t m, MonadIO (Performable m)) => Event t a -> m ()
displayRedrawTime e = do
  et1 <- performEvent $ liftIO getCurrentTime <$ e
  et1' <- delay 0 et1
  diff <- performEvent $ ffor et1' $ \t1 -> do
    t2 <- liftIO getCurrentTime
    return $ t2 `diffUTCTime` t1
  text "Time: "
  dynText =<< holdDyn "not yet run" (T.pack . show <$> diff)

simpleSortableList :: forall t m k v. (MonadHold t m, MonadFix m, Adjustable t m, Ord k) => (k -> v -> m ()) -> Map k v -> Event t (v -> v -> Ordering) -> Event t (v -> v -> Ordering) -> m ()
simpleSortableList f m0 resortFunc resortSlowFunc = do
  rec let resortPatchFast = attachWith (flip patchThatSortsMapWith) (currentIncremental m) resortFunc
          redrawPatch :: Map k v -> (v -> v -> Ordering) -> PatchMapWithMove k v
          redrawPatch d cmp = unsafePatchMapWithMove $ fmap (MapEdit_Insert False) $ Map.fromList $ zip (Map.keys d) (sortBy cmp $ Map.elems d)
          resortPatchSlow = attachWith redrawPatch (currentIncremental m) resortSlowFunc
          resortPatch = leftmost
            [ resortPatchFast
            , resortPatchSlow
            ]
      m <- holdIncremental m0 resortPatch
  _ <- mapMapWithAdjustWithMove f m0 resortPatch
  return ()