{-# LANGUAGE RecursiveDo #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE Rank2Types #-}
{-# OPTIONS_GHC -ddump-simpl -ddump-rules -ddump-spec -ddump-to-file -dsuppress-coercions -dsuppress-idinfo #-}
module Main (main) where

import Control.Monad.State
import Data.Monoid
import Data.Text (Text)
import qualified Data.Text as T
import Reflex.Dom
import System.Random
import qualified Data.Map as Map
import Data.Dependent.Map (DMap (..))
import qualified Data.Dependent.Map as DMap
import Data.Dependent.Sum (DSum (..))
import Data.Functor.Misc
import Data.Functor.Identity
import Data.Functor.Compose
import Data.IORef
import GHCJS.DOM.Types (JSM)
import qualified Data.Some as Some
import Data.Functor.Constant
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IntMap
import Data.FastMutableIntMap

import qualified GHCJS.DOM.Element as DOM
import qualified GHCJS.DOM.Types as DOM

--{-
main :: IO ()
main = mainWidget' $ do
  create1000 <- button "Create 1,000 rows"
  let row k _ = el "li" $ text $ T.pack $ show k
  el "ul" $ traverseIntMapWithKeyWithAdjust row IntMap.empty $ PatchIntMap (IntMap.fromDistinctAscList [(x, Just ()) | x <- [1 :: Int .. 1000]]) <$ create1000
  return ()
--}


{-
main :: IO ()
main = do
  mainWidget' $ do
    create1000 <- button "Create 1,000 rows"
    partialUpdate <- button "Update every 10th row"
    elClass "table" "table table-hover table-striped test-data" $ el "tbody" $ do
      --runWithReplace blank $ forM_ [1..1000] row <$ create1000
      rec let u = leftmost
                [ PatchIntMap (IntMap.fromDistinctAscList [(x, Just ()) | x <- [1..1000]]) <$ create1000
                , PatchIntMap (IntMap.fromDistinctAscList [(x, Just ()) | x <- [1, 11..1000]]) <$ partialUpdate
                , PatchIntMap <$> deletes
                ]
          (_, deletes) <- runEventWriterT $ traverseIntMapWithKeyWithAdjust (\k _ -> row k) mempty u
      return ()

row :: MonadWidget t m => Int -> EventWriterT t (IntMap (Maybe ())) m ()
row rowNum = el "tr" $ do
  elClass "td" "col-md-1" $ text $ T.pack $ show rowNum
  (sel, _) <- elClass' "td" "col-md-4" $ el "a" $ text "asdf"
  (del, _) <- elClass' "td" "col-md-1" deleteButton
  tellEvent $ IntMap.singleton rowNum Nothing <$ domEvent Click del
  elClass "td" "col-md-6" blank

deleteButton :: MonadWidget t m => m ()
deleteButton = el "a" $ elAttr "span" ("aria-hidden" =: "true" <> "class" =: "glyphicon glyphicon-remove") $ text "X"

--}

--
{-
type RNG = StdGen
type Entropy     = Int
type RowPosition = Int
type RowNumber   = Int
data Row   = Row { pos :: RowPosition, num :: RowNumber , txt :: Text } deriving (Eq, Show)
type Table = Map RowPosition Row
data Model = Model { rng :: RNG, table :: Table, nextNum :: RowNumber, selected :: Maybe Row } deriving Show

{- Widgets -}

titleW :: MonadWidget t m => m ()
titleW = text "Reflex-Dom"

headW :: MonadWidget t m => m ()
headW = do
  el "title" titleW
  elAttr "link" ("href" =: "/css/currentStyle.css" <> "rel" =: "stylesheet") blank

bodyW :: MonadWidget t m => Entropy -> m ()
bodyW seed = divClass "main" $ divClass "container" $ mdo
  let initial = Model { rng = mkStdGen seed, table = empty, nextNum = 1, selected = Nothing }
  dynModel <- foldDyn ($) initial $ mergeWith (.) $ rowEvents : buttonEvents

  buttonEvents :: [Event t (Model -> Model)] <- divClass "jumbotron" $ divClass "row" $ do
    divClass "col-md-6" $ el "h1" titleW
    divClass "col-md-6" $ divClass "row" $ sequence [
      buttonW "run"      "Create 1,000 rows"     $ createRows 1000,
      buttonW "runlots"  "Create 10,000 rows"    $ createRows 10000,
      buttonW "add"      "Append 1,000 rows"     $ appendRows 1000,
      buttonW "update"   "Update every 10th row" $ updateRows (\p -> mod p 10 == 0) (<> " !!!"),
      buttonW "clear"    "Clear"                 $ clearRows,
      buttonW "swaprows" "Swap Rows"             $ swapRows (4, 9)
      ]

  rowEvents' <- tableW dynModel
  let rowEvents = foldl (.) id <$> rowEvents'

  blank

buttonW :: MonadWidget t m => Text -> Text -> a -> m (Event t a)
buttonW id txt val = divClass "col-sm-6 smallpad" $ buttonW' ("class" =: "btn btn-primary btn-block" <> "id" =: id)
  where
    buttonW' attrs = do
      (b, _) <- elAttr' "button" attrs $ text txt
      pure $ val <$ domEvent Click b

tableW :: MonadWidget t m => Dynamic t Model -> m (Event t (Map RowPosition (Model -> Model)))
tableW dynM =
  elClass "table" "table table-hover table-striped test-data"
  $ el "tbody"
  $ listViewWithKey (table <$> dynM) (\_ dynRow -> rowW (selectedIs <$> dynM <*> dynRow) dynRow)
  where
    selectedIs m r = selected m == Just r

rowW :: MonadWidget t m => Dynamic t Bool -> Dynamic t Row -> m (Event t (Model -> Model))
rowW dynSelected dynRow = elDynClass "tr" trClass $
  do
    elClass "td" "col-md-1". dynText $ pack . show . num <$> dynRow
    (sel, _) <- elClass' "td" "col-md-4". el "a" . dynText $ txt <$> dynRow
    (del, _) <- elClass' "td" "col-md-1" deleteW
    elClass "td" "col-md-6" blank
    pure $ mergeWith (.) $ zipWith tagClick [selectRow, deleteRow] [sel, del]
  where
    trClass = (\b -> if b then "danger" else "") <$> dynSelected
    tagClick f el = (tag . current) (f <$> dynRow) (domEvent Click el)


{- Domain logic -}

clearRows :: Model -> Model
clearRows (Model {..}) = Model { table = empty, ..  }

updateRows :: (RowPosition -> Bool) -> (Text -> Text) -> Model -> Model
updateRows p f (Model {..}) = Model { table = (update <$> targets) <> table, ..  }
  where
    targets = filterWithKey (const . p) table
    update  = \(Row{..}) -> Row { txt = f txt, ..}

swapRows :: (RowPosition, RowPosition) -> Model -> Model
swapRows (a,b) (Model {..}) = Model { table = (a =: val b) <> (b =: val a) <> table, .. }
  where val = (table !)

selectRow, deleteRow :: Row -> Model -> Model
selectRow r m = m { selected = Just r }
deleteRow r (Model {..}) = Model { table = shifted table, .. }
  where
    shifted = fromList . zipWith update [0..] . elems . delete (pos r)
    update p (Row{..}) = (p, Row { pos = p, .. })

createRows, appendRows :: Int -> Model -> Model
createRows = newRows False
appendRows = newRows True

newRows :: Bool -> Int -> Model -> Model
newRows keepOld count (Model {..}) = Model { rng = rng', table = table', nextNum = nextNum', .. }
  where
    nextPos = if keepOld then length table else 0
    rowsST = sequence (rowST <$ [0..count-1])
    (rows, (nextPos', nextNum', rng')) = runState rowsST (nextPos, nextNum, rng)
    combine = if keepOld then (<>) else \_ new -> new
    table' = fromList . zip [0..] $ combine (elems table) rows

rowST :: State (RowPosition, RowNumber, RNG) Row
rowST  = state (\(p,n,g) -> let (e, g') = next g in (Row {pos = p, num = n, txt = randomName e}, (p+1,n+1,g')))

randomName :: Entropy -> Text
randomName e = intercalate " " $ (!!% e) <$> [adjectives, colours, nouns]
  where xs !!% n = xs !! mod n (length xs)

adjectives,colours,nouns :: [Text]
adjectives = ["pretty", "large", "big", "small", "tall", "short", "long", "handsome", "plain", "quaint", "clean", "elegant", "easy", "angry", "crazy", "helpful", "mushy", "odd", "unsightly", "adorable", "important", "inexpensive", "cheap", "expensive", "fancy"]
colours = ["red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black", "orange"]
nouns = ["table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger", "pizza", "mouse", "keyboard"]

-}