-- | GUI programming in a web browser
-- Examples introducing ThreepennyFudgets
-- Functional Programming course 2018.
-- Thomas Hallgren

{-
This started as a skeleton, the definitions were filled in
during the lecture.
-}

import qualified Graphics.UI.Threepenny as UI
import Graphics.UI.Threepenny.Core
import ThreepennyFudgets

main :: IO ()
main = runF' init mainF
  where
    init w = UI.addStyleSheet w "demo.css"

mainF = h1F (textF "Hello!")
        +<
        pF (textF "Introducining ThreepennyFudgets")
        +<
        pF ex1 +< pF ex2 +< pF ex3 +< pF ex4 +< pF ex5 +< pF ex6 +< pF ex7
        
--------------------------------------------------------------------------
-- * Example 1: a program to test the factorial function

--ex1 = numberF =>= mapF fac =>= showF
--ex1 = showF =<= mapF fac =<= numberF

ex1 =     pF (textF "n = " +< numberF)
      =>= mapF fac
      =>= pF (textF "n! = " +< showF)
      
fac n = product [1..n]

--------------------------------------------------------------------------
-- * Example 2: counter example

ex2 = showF =<= stateF count 0 =<= buttonF "+1"
  where
    count n _ = (n+1,[n+1])

--------------------------------------------------------------------------------
-- * Example 3: up/down counter

ex3 = showF =<= stateF count 0 =<= (buttonF "+1" >+< buttonF "-1")
  where
    count n (Left _) = (n+1,[n+1])
    count n (Right _) = (n-1,[n-1])


--------------------------------------------------------------------------------
-- * Example 4: up/down/reset counter

data CounterButton = Up | Down | Reset
                     deriving (Eq,Show,Enum)
                     
counterButtonsF = listF [(b,buttonF (show b)) | b <- [Up ..]]

ex4 = showF =<= stateF count 0 =<= counterButtonsF

count n (Up,_) = (n+1,[n+1])
count n (Down,_) = (n-1,[n-1])
count n (Reset,_) = (0,[0])

--------------------------------------------------------------------------------
-- * Example 5: loadable up/down/reset counter

ex5 = loopLeftF (mapF Left =<= numberF =<= stateF count' 0)
      =<= counterButtonsF
  where
    count' n (Left new) = (new,[new])
    count' n (Right b)  = count n b



--------------------------------------------------------------------------------
-- * Example 6: prototype user interface for a board game

ex6 = loopF (stateF game False
             =<= tableF width squaresF `classF` "board")

game b (p,_) = (not b,[(p,if b then "X" else "O")])


squaresF = listF [((x,y),squareF (x,y)) | y<-[1..height], x<-[1..width]]
squareF _ = mapMaybeF fromLeft =<=
            buttonGroupF stringDisplayF `withF` [style=:css]
            =<= mapF Right
  where
    css = [("width","30px"),("height","30px")]

width = 8
height = 8


-------------------------------------------------------------------------
-- * Illustrates the use of a canvas for graphical output

ex7 = (pF sidesF >+< pF angleF) =>= gatherF =>= mapF polygon =>=
      canvasF (300,300) `withF` [set style css]
  where
    sidesF = textF "Draw a polygon with " +< numberF >+ textF " sides."
    
    angleF = toggleButtonF "Spin" =>= mapF setTimer =>= timerF
              =>= putF 0 (stateF angle 0)
    setTimer spin = if spin then Just 40 else Nothing
    angle a _ = ((a+1) `mod` 360 ,[pi*real a/180])
    
    css = [("border","1px solid black"),
           ("background","white")]

-- A function to draw a polygon and some text
polygon :: (Int,Double) -> UI.Canvas -> UI ()
polygon (n,a0) c = do fillPath (UI.solidColor (UI.RGB 255 255 128)) ps c
                      strokePath "#00f" ps c
                      UI.set' UI.fillStyle (UI.solidColor (UI.RGB 0 0 0)) c
                      UI.fillText "A simple canvas example" (20,20) c
  where
    ps = map corner [0..n]

    (cx,cy) = (150,150) -- center
    r = 100 -- radius of enclosing circle
    
    corner i = (cx+r*cos a,cy+r*sin a)
      where
        a = 2*pi*(real i/ real n+0.25-0.5/real n) + a0

real x = fromIntegral x