{-# LANGUAGE ApplicativeDo #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -Wall #-}
{-# OPTIONS_GHC -Wredundant-constraints #-}

module Web.Page.Rep.Input
  ( repInput
  , repMessage
  , sliderI
  , slider
  , dropdown
  , datalist
  , dropdownSum
  , colorPicker
  , textbox
  , textarea
  , checkbox
  , toggle
  , button
  , chooseFile
  , maybeRep
  , fiddle
  , viaFiddle
  ) where

import Codec.Picture.Types (PixelRGB8(..))
import Control.Category (id)
import Control.Lens
import Data.Attoparsec.Text
import Data.Biapplicative
import Data.Bifunctor (Bifunctor(..))
import Data.HashMap.Strict
import Data.Text (pack, Text)
import Lucid
import Protolude hiding ((<<*>>), Rep)
import Web.Page.Bootstrap
import Web.Page.Html
import Web.Page.Html.Input
import Web.Page.Types
import Web.Page.Rep
import Box.Cont ()

-- | create a sharedRep from an Input
repInput :: (Monad m, ToHtml a) => Parser a -> (a -> Text) -> Input a -> a -> SharedRep m a
repInput p pr i a =
  SharedRep $ do
    name <- zoom _1 genName
    zoom _2 (modify (insert name (pr a)))
    pure $
      Rep
      (toHtml $ #inputVal .~ a $ #inputId .~ name $ i)
      (\s ->
        (s, join $
        maybe (Left "lookup failed") Right $
        either (Left . (\x -> name <> ": " <> x) . pack) Right . parseOnly p <$> lookup name s))

-- | does not put a value into the HashMap on instantiation, consumes the value when found in the HashMap, and substitutes a default on lookup failure
repMessage :: (Monad m, ToHtml a) => Parser a -> (a -> Text) -> Input a -> a -> a -> SharedRep m a
repMessage p _ i def a =
  SharedRep $ do
    name <- zoom _1 genName
    pure $
      Rep
      (toHtml $ #inputVal .~ a $ #inputId .~ name $ i)
      (\s ->
        (delete name s, join $
        maybe (Right $ Right def) Right $
        either (Left . pack) Right . parseOnly p <$> lookup name s))

slider :: (Monad m) => Maybe Text -> Double -> Double -> Double -> Double ->
  SharedRep m Double
slider label l u s v = repInput double show
  (Input v label mempty (Slider [min_ (pack $ show l), max_ (pack $ show u), step_ (pack $ show s)])) v

sliderI :: (Monad m, ToHtml a, Integral a, Show a) => Maybe Text -> a -> a -> a -> a ->
  SharedRep m a
sliderI label l u s v = repInput decimal show
  (Input v label mempty (Slider [min_ (pack $ show l), max_ (pack $ show u), step_ (pack $ show s)])) v

textbox :: (Monad m) => Maybe Text -> Text -> SharedRep m Text
textbox label v = repInput takeText id
  (Input v label mempty TextBox) v

textarea :: (Monad m) => Int -> Maybe Text -> Text -> SharedRep m Text
textarea rows label v = repInput takeText id
  (Input v label mempty (TextArea rows)) v

colorPicker :: (Monad m) => Maybe Text -> PixelRGB8 -> SharedRep m PixelRGB8
colorPicker label v = repInput fromHex toHex
  (Input v label mempty ColorPicker) v

dropdown :: (Monad m, ToHtml a) =>
  Parser a -> (a -> Text) -> Maybe Text -> [Text] -> a -> SharedRep m a
dropdown p pr label opts v = repInput p pr
  (Input v label mempty (Dropdown opts)) v

datalist :: (Monad m) => Maybe Text -> [Text] -> Text -> Text -> SharedRep m Text
datalist label opts v id'' = repInput takeText show
  (Input v label mempty (Datalist opts id'')) v

dropdownSum :: (Monad m, ToHtml a) =>
  Parser a -> (a -> Text) -> Maybe Text -> [Text] -> a -> SharedRep m a
dropdownSum p pr label opts v = repInput p pr
  (Input v label mempty (DropdownSum opts)) v

checkbox :: (Monad m) => Maybe Text -> Bool -> SharedRep m Bool
checkbox label v = repInput ((=="true") <$> takeText) (bool "false" "true")
  (Input v label mempty (Checkbox v)) v

toggle :: (Monad m) => Maybe Text -> Bool -> SharedRep m Bool
toggle label v = repInput ((=="true") <$> takeText) (bool "false" "true")
  (Input v label mempty (Toggle v label)) v

button :: (Monad m) => Maybe Text -> SharedRep m Bool
button label = repMessage (pure True) (bool "false" "true")
  (Input False label mempty Button) False False

chooseFile :: (Monad m) => Maybe Text -> Text -> SharedRep m Text
chooseFile label v = repInput takeText show
  (Input v label mempty ChooseFile) v

checkboxShowJs :: (Monad m) => Maybe Text -> Text -> Bool -> SharedRep m Bool
checkboxShowJs label cl v =
  SharedRep $ do
    name <- zoom _1 genName
    zoom _2 (modify (insert name (bool "false" "true" v)))
    pure $
      Rep
      (toHtml (Input v label name (Checkbox v)) <> scriptToggleShow name cl)
      (\s ->
        (s, join $
        maybe (Left "lookup failed") Right $
        either (Left . pack) Right . parseOnly ((=="true") <$> takeText) <$>
        lookup name s))

-- | represent a Maybe type using a checkbox hiding the underlying content on Nothing
maybeRep :: (Monad m) => Maybe Text -> Bool -> SharedRep m a ->
  SharedRep m (Maybe a)
maybeRep label st sa = SharedRep $ do
  className <- zoom _1 genName
  unrep $ bimap (hmap className) mmap (checkboxShowJs label className st) <<*>> sa
  where
    hmap cl a b =
      cardify (a, []) Nothing
       ((Lucid.with div_
        [class__ cl, style_
                   ("display:" <> bool "none" "block" st)]
        b),
        [style_ "padding-top: 0.25rem; padding-bottom: 0.25rem;"])
    mmap a b = bool Nothing (Just b) a

-- | representation of web concerns (css, js & html)
fiddle :: (Monad m) => Concerns Text -> SharedRep m (Concerns Text, Bool)
fiddle (Concerns c j h) =
  bimap
  (\c' j' h' up -> (Lucid.with div_ [class__ "fiddle "] $ mconcat [up,h',j',c']))
  (\c' j' h' up -> (Concerns c' j' h', up))
  (textarea 10 (Just "css") c) <<*>>
  textarea 10 (Just "js") j <<*>>
  textarea 10 (Just "html") h <<*>>
  button (Just "update")

-- | turns a SharedRep into a fiddle
viaFiddle
  :: (Monad m)
  => SharedRep m a
  -> SharedRep m (Bool, Concerns Text, a)
viaFiddle sr = SharedRep $ do
  sr'@(Rep h _) <- unrep sr
  hrep <- unrep $ textarea 10 (Just "html") (toText h)
  crep <- unrep $ textarea 10 (Just "css") mempty
  jrep <- unrep $ textarea 10 (Just "js") mempty
  u <- unrep $ button (Just "update")
  pure $
    bimap
    (\up a b c _ -> (Lucid.with div_ [class__ "fiddle "] $ mconcat [up, a, b, c]))
    (\up a b c d -> (up, Concerns a b c, d))
    u <<*>>
    crep <<*>>
    jrep <<*>>
    hrep <<*>>
    sr'