module Main where

import Prelude hiding (putStrLn)
import System.IO.UTF8
import BrownPLT.JavaScript
import BrownPLT.JavaScript.PrettyPrint
import Control.Monad
import Language.JSMW
import Language.JSMW.Arith

import BrownPLT.JavaScript.Syntax
import Control.Monad.RWS
import Foreign.WebIDL.Dom.Node
import Foreign.WebIDL.Dom.Text
import Foreign.WebIDL.Dom.Document
import Foreign.WebIDL.Dom.Element
import Foreign.WebIDL.Html2.HTMLDocument
import Foreign.WebIDL.Html2.HTMLHRElement
import Foreign.WebIDL.Html2.HTMLInputElement
import Foreign.WebIDL.Keycodes.KeyEvent
import Foreign.WebIDL.Html2.HTMLElement
import Foreign.WebIDL.Events.Event
import Foreign.WebIDL.Html2.HTMLBodyElement

-- Main function: generate Javascript.

main = putStrLn $ show $ stmt $ 
  FunctionStmt undefined (Id undefined "main") [] (getBlock (runJSMWWith currDocBody 0 q))

-- Toplevel expression.

q = do
  passive (mkText $ string 
    "Example 1: Press Enter to increase value, Shift-Enter to decrease value")
  passive mkHr
  mkInput `container` (do 
    setHandler "keypress" plusOne
    ask >>= set'value (string "0") >>= focus)

-- Handler for the input element.

plusOne :: OnHandler TKeyEvent THTMLInputElement

plusOne e = do
  c <- getm'keyCode e
  switch (c) $ do
    cDOM_VK_ENTER --> do i <- ask 
                         v <- getm'value i
                         vv <- switch v $ do
                           "" --> stringM "0"
                           none (return v)
                         n <- parseInt vv 0
                         shft <- get'shiftKey e
                         n2 <- switch shft $ do
                           True --> return (n - number 1)
                           False --> return (n + number 1)
                         once =<< (toString n2 >>= flip set'value i)
                         return false
    none (return true)

-- Helper functions.

-- These instances are needed in order to use HTML elements as containers.

instance JContainer THTMLBodyElement
instance JContainer THTMLInputElement

-- Element creation function type.

type ECRF e n = Expression THTMLDocument -> JSMW e (Expression n)

-- Type for an event handler.

type OnHandler e c = Expression e -> JSMW c (Expression Bool)

-- Body of the current document to use as a toplevel container.

currDocBody :: Expression THTMLBodyElement
currDocBody = VarRef THTMLBodyElement (Id THTMLBodyElement "window.document.body")

-- Insert a passive element into a current container

passive :: (CNode n, CElement e)
        => ECRF e n
        -> JSMW e (Expression ())

passive crf = do
  cntr <- ask
  doc <- get'ownerDocument cntr
  e <- once =<< crf doc
  once =<< addChild e cntr
  return $ NullLit ()

-- Specify a new container, nested into the current one.

container :: (JContainer n, CElement n, CElement e)
          => ECRF e n
          -> JSMW n (Expression x)
          -> JSMW e (Expression ())

container crf cnt = do
  curc <- once =<< ask
  doc <- once =<< get'ownerDocument curc
  newc <- once =<< crf doc
  once =<< addChild newc curc
  carg <- mkNewVar
  st <- get
  let et = exprType newc
      (finx, fins, stms) = runJSMWWith (VarRef et (Id et carg)) st cnt
      blk = getBlock (finx, fins, stms)
      fun = ParenExpr () (FuncExpr () [Id () carg] blk)
      call = CallExpr () fun [newc /\ ()]
  writeStmt (ExprStmt () call)
  put fins
  return $ NullLit ()

-- Install an event handler on an element.

setHandler :: (JContainer c, CHTMLElement c, CEvent e)
            => String -> OnHandler e c -> JSMW c (Expression ())

setHandler s x = do
  ctr <- once =<< ask
  earg <- mkNewVar
  st <- get
  let et = undefined :: e
      prop = "on" ++ s
      evar = VarRef et (Id et earg)
      (finx, fins, stms) = runJSMWWith ctr st (x evar)
      msievent = IfSingleStmt () (PrefixExpr () PrefixLNot (evar /\ ()))
                   (BlockStmt () [ExprStmt ()
                                   (AssignExpr () OpAssign (evar /\ ())
                                                           (VarRef () (Id () "window.event")))])
      blk = getBlock (finx, fins, msievent : stms)    
      fun = FuncExpr () [Id () earg] blk              
      seth = ExprStmt () $ AssignExpr () OpAssign (DotRef () (ctr /\ ()) (Id () prop)) (fun /\ ())
  writeStmt seth
  put fins
  return (NullLit ())

-- Create a text node (short-cut for createTextNode)

mkText :: (Monad mn, CDocument this) => 
   Expression String -> Expression this -> mn (Expression TText)
mkText = createTextNode

-- Insert a child element into a node (short-cut for appendChild)

addChild :: (Monad m, CNode c, CNode p) => Expression c -> Expression p -> m (Expression c)
addChild = appendChild


