{-|
    A Grapefruit example which demonstrates the use of incremental signals and container views.

    A list of kinds of fruit is displayed. Below this list, there are several buttons for
    manipulating the list. The changing list is represented by an incremental signal over sequences.
    Note that not every list manipulation is applicable at every time. If you select a
    non-applicable operation, you get a runtime error. This is caused by the generic checking for
    incremental signals which is provided by the 'iSignal' function.
-}
module Examples.Grapefruit.ListView (

    mainCircuit

) where

    -- Control
    import Control.Applicative as Applicative
    import Control.Arrow       as Arrow

    -- Data
    import Data.Foldable        as Foldable
    import Data.Sequence        as Seq
    import Data.Fraction        as Fraction
    import Data.Colour.RGBSpace as RGBSpace
    import Data.Record          as Record

    -- FRP.Grapefruit
    import FRP.Grapefruit.Signal.Discrete             as DSignal
    import FRP.Grapefruit.Signal.Incremental          as ISignal
    import FRP.Grapefruit.Signal.Incremental.Sequence as SeqISignal

    -- Graphics.UI.Grapefruit
    import Graphics.UI.Grapefruit.Item              as UIItem             hiding (box)
    import Graphics.UI.Grapefruit.Circuit           as UICircuit
    import Graphics.UI.Grapefruit.Backend.Basic     as BasicUIBackend
    import Graphics.UI.Grapefruit.Backend.Container as ContainerUIBackend

    -- |The circuit describing the whole application.
    mainCircuit :: (ContainerUIBackend uiBackend) =>
                   UICircuit Window uiBackend era () (DSignal era ())
    mainCircuit = proc _ -> do
        X :& Closure := closure
             `With` X
             `With` _           <- mainWindow -< X :& Title := pure "List view"
                                                      `With` X
                                                      `With` ()
        returnA -< closure where

        mainWindow = window `with` box Vertical `with` content

    content :: (ContainerUIBackend uiBackend) => UICircuit Widget uiBackend era () ()
    content = proc _ -> do
        rec let

                fruits           = ISignal.construct (Seq.singleton Grapefruit)
                                                     (fmap (Diff . Seq.singleton) atomicFruitDiffs)

                atomicFruitDiffs = DSignal.unions [insertions, deletions, shifts, updates]

                cols             = ISignal.construct (Seq.fromList [nameCol,tastinessCol])
                                                     (fmap (Diff . Seq.singleton) atomicColDiffs)

                nameCol          = Column "name" nameDisplay textCell

                nameDisplay      = \fruit -> TextCellDisplay (name fruit) (color fruit)

                tastinessCol     = Column "tastiness" tastinessDisplay progressCell

                tastinessDisplay = \fruit -> ProgressCellDisplay (tastiness fruit) Nothing

                atomicColDiffs   = colSwaps

            _          <- display    -< (fruits,cols)
            insertions <- inserter   -< ()
            deletions  <- deleter    -< ()
            shifts     <- shifter    -< ()
            updates    <- updater    -< ()
            colSwaps   <- colSwapper -< ()

        returnA -< ()

    display :: (ContainerUIBackend uiBackend) =>
               UICircuit Widget
                         uiBackend
                         era
                         (ISignal era (Seq Fruit),ISignal era (Seq (Column uiBackend Fruit)))
                         ()
    display = proc (fruits,cols) -> do
        X :& Selection := selection <- just listView -< X :& Elements := fruits
                                                          :& Columns  := cols
        X                           <- just label    -< X :& Text     := fmap (show . toList)
                                                                              selection
        returnA -< ()

    inserter :: (BasicUIBackend uiBackend) =>
                UICircuit Widget uiBackend era () (DSignal era (AtomicDiff Fruit))
    inserter = proc _ -> do
        X :& Push := push <- just pushButton -< X :& Text := pure ("Insert an apple " ++
                                                                   "at the beginning")
        returnA -< Insertion 0 (Seq.singleton Apple) <$ push

    deleter :: (BasicUIBackend uiBackend) =>
               UICircuit Widget uiBackend era () (DSignal era (AtomicDiff Fruit))
    deleter = proc _ -> do
        X :& Push := push <- just pushButton -< X :& Text := pure "Delete the second element"
        returnA -< Deletion 1 1 <$ push

    shifter :: (BasicUIBackend uiBackend) =>
               UICircuit Widget uiBackend era () (DSignal era (AtomicDiff Fruit))
    shifter = proc _ -> do
        X :& Push := push <- just pushButton -< X :& Text := pure "Swap the first two elements"
        returnA -< Shift 0 1 1 <$ push

    updater :: (BasicUIBackend uiBackend) =>
               UICircuit Widget uiBackend era () (DSignal era (AtomicDiff Fruit))
    updater = proc _ -> do
        X :& Push := push <- just pushButton -< X :& Text := pure ("Replace the third element " ++
                                                                   "with a banana")
        returnA -< Update 2 (Seq.singleton Banana) <$ push

    colSwapper :: (BasicUIBackend uiBackend) =>
                  UICircuit Widget uiBackend era ()
                                                 (DSignal era (AtomicDiff (Column uiBackend Fruit)))
    colSwapper = proc _ -> do
        X :& Push := push <- just pushButton -< X :& Text := pure "Swap columns"
        returnA -< Shift 0 1 1 <$ push

    data Fruit = Grapefruit | Apple | Banana deriving (Show)

    name :: Fruit -> String
    name Grapefruit = "grapefruit"
    name Apple      = "apple"
    name Banana     = "banana"

    tastiness :: Fruit -> Fraction
    tastiness Grapefruit = Fraction.fromPercentage 100
    tastiness Apple      = Fraction.fromPercentage 25
    tastiness Banana     = Fraction.fromPercentage 50

    color :: Fruit -> RGB Fraction
    color Grapefruit = RGB (fromFactor 1) (fromFactor 0.8) (fromFactor 0)
    color Apple      = RGB (fromFactor 1) (fromFactor 0) (fromFactor 0)
    color Banana     = RGB (fromFactor 1) (fromFactor 1) (fromFactor 0)