-- © 2002-2005 Peter Thiemann
-- |Haskell98 version of the submission functions.
module WASH.CGI.Submit98
	( InputHandle, HasValue (value)
	, F0 (F0), F1 (F1), F2 (F2), F3 (F3), F4 (F4), F5 (F5), F6 (F6), F7 (F7), F8 (F8)
	, FL (FL), FA (FA)
	, deF0, deF1, deF2, deF3, deF4, deF5, deF6, deF7, deF8
	, deFL, deFA
	, deValueF0, deValueF1, deValueF2, deValueF3, deValueF4, deValueF5, deValueF6, deValueF7, deValueF8
	, deValueFL, deValueFA
	, submit, submit0, defaultSubmit, DTree, submitx, dtleaf, dtnode
	, submitLink, submitLink0, defaultSubmitLink
	, activate
	)
	where

import WASH.CGI.AbstractSelector
import WASH.CGI.CGIInternals
import WASH.CGI.CGIMonad
import WASH.CGI.EventHandlers
import qualified WASH.CGI.HTMLWrapper as H
import WASH.CGI.InputHandle

import Control.Monad

instance HasValue InputField where
  value inf = valueInputField inf

instance InputHandle (InputField a) where
  validate inf = validateInputField inf
  isBound  inf = ifBound inf
  ihNames  inf = [ifName inf]

data F0 x = F0
deF0 :: r -> (F0 x -> r)
deF0 r F0 = r

deValueF0 r F0 = r

instance InputHandle F0 where
  validate F0 = Right F0
  isBound  F0 = True
  ihNames  F0 = []

data F1 a x = F1 (a x)
deF1 :: (a x -> r) -> (F1 a x -> r)
deF1 g (F1 ax) = g ax

deValueF1 g (F1 ax) = g (value ax)

instance InputHandle a => InputHandle (F1 a) where
  validate (F1 ainv) =
    feither id F1 (validate ainv)
  isBound (F1 ainv) = isBound ainv
  ihNames (F1 ainv) = ihNames ainv

data F2 a b x = F2 (a x) (b x)
deF2 :: (a x -> b x -> r) -> (F2 a b x -> r)
deF2 g (F2 ax bx) = g ax bx

deValueF2 g (F2 ax bx) = g (value ax) (value bx)

instance (InputHandle a, InputHandle b) => InputHandle (F2 a b) where
  validate (F2 ainv binv) =
    feither id (uncurry F2)
    (propagate (validate ainv) (validate binv))
  isBound (F2 ainv binv) =
    isBound ainv && isBound binv
  ihNames (F2 ainv binv) =
    ihNames ainv ++ ihNames binv

data F3 a b c x = F3 (a x) (b x) (c x)
deF3 :: (a x -> b x -> c x -> r) -> (F3 a b c x -> r)
deF3 g (F3 ax bx cx) = g ax bx cx

deValueF3 g (F3 ax bx cx) = g (value ax) (value bx) (value cx)

instance (InputHandle a, InputHandle b, InputHandle c) => InputHandle (F3 a b c) where
  validate (F3 ainv binv cinv) =
    feither id (\ (aval,(bval,cval)) -> F3 aval bval cval)
    (propagate (validate ainv)
     (propagate (validate binv) (validate cinv)))
  isBound (F3 ainv binv cinv) =
    isBound ainv && isBound binv && isBound cinv
  ihNames (F3 ainv binv cinv) =
    ihNames ainv ++ ihNames binv ++ ihNames cinv

data F4 a b c d x = F4 (a x) (b x) (c x) (d x)
deF4 :: (a x -> b x -> c x -> d x -> r) -> (F4 a b c d x -> r)
deF4 g (F4 ax bx cx dx) = g ax bx cx dx

deValueF4 g (F4 ax bx cx dx) = g (value ax) (value bx) (value cx) (value dx)

instance (InputHandle a, InputHandle b, InputHandle c, InputHandle d)
       => InputHandle (F4 a b c d) where
  validate (F4 ainv binv cinv dinv) =
    feither id (\ (aval,(bval,(cval,dval))) -> F4 aval bval cval dval)
    (propagate (validate ainv) 
     (propagate (validate binv)
      (propagate (validate cinv) (validate dinv))))
  isBound (F4 ainv binv cinv dinv) =
    isBound ainv && isBound binv && isBound cinv && isBound dinv
  ihNames (F4 ainv binv cinv dinv) =
    ihNames ainv ++ ihNames binv ++ ihNames cinv ++ ihNames dinv

data F5 a b c d e x = F5 (a x) (b x) (c x) (d x) (e x)
deF5 :: (a x -> b x -> c x -> d x -> e x -> r) -> (F5 a b c d e x -> r)
deF5 g (F5 ax bx cx dx ex) = g ax bx cx dx ex

deValueF5 g (F5 ax bx cx dx ex) = g (value ax) (value bx) (value cx) (value dx) (value ex)

instance (InputHandle a, InputHandle b, InputHandle c, InputHandle d, InputHandle e)
       => InputHandle (F5 a b c d e) where
  validate (F5 ainv binv cinv dinv einv) =
    feither id (\ (aval,(bval,(cval,(dval,eval)))) -> F5 aval bval cval dval eval)
    (propagate (validate ainv) 
     (propagate (validate binv)
      (propagate (validate cinv)
       (propagate (validate dinv) (validate einv)))))
  isBound (F5 ainv binv cinv dinv einv) =
    isBound ainv && isBound binv && isBound cinv && isBound dinv && isBound einv
  ihNames (F5 ainv binv cinv dinv einv) =
    ihNames ainv ++ ihNames binv ++ ihNames cinv ++ ihNames dinv ++ ihNames einv

data F6 a b c d e f x = F6 (a x) (b x) (c x) (d x) (e x) (f x)
deF6 :: (a x -> b x -> c x -> d x -> e x -> f x -> r) -> (F6 a b c d e f x -> r)
deF6 g (F6 ax bx cx dx ex fx) = g ax bx cx dx ex fx

deValueF6 g (F6 ax bx cx dx ex fx) = g (value ax) (value bx) (value cx) (value dx) (value ex) (value fx)

instance (InputHandle a, InputHandle b, InputHandle c, InputHandle d, InputHandle e, InputHandle f)
       => InputHandle (F6 a b c d e f) where
  validate (F6 ainv binv cinv dinv einv finv) =
    feither id (\ (aval,(bval,(cval,(dval,(eval, fval))))) -> F6 aval bval cval dval eval fval)
    (propagate (validate ainv) 
     (propagate (validate binv)
      (propagate (validate cinv)
       (propagate (validate dinv) 
        (propagate (validate einv) (validate finv))))))
  isBound (F6 ainv binv cinv dinv einv finv) =
    isBound ainv && isBound binv && isBound cinv && isBound dinv && isBound einv && isBound finv
  ihNames (F6 ainv binv cinv dinv einv finv) =
    ihNames ainv ++ ihNames binv ++ ihNames cinv ++ ihNames dinv ++ ihNames einv ++ ihNames finv

data F7 a b c d e f g x = F7 (a x) (b x) (c x) (d x) (e x) (f x) (g x)
deF7 :: (a x -> b x -> c x -> d x -> e x -> f x -> g x -> r) -> (F7 a b c d e f g x -> r)
deF7 g (F7 ax bx cx dx ex fx gx) = g ax bx cx dx ex fx gx

deValueF7 g (F7 ax bx cx dx ex fx gx) = g (value ax) (value bx) (value cx) (value dx) (value ex) (value fx) (value gx)

instance (InputHandle a, InputHandle b, InputHandle c, InputHandle d, InputHandle e, InputHandle f, InputHandle g)
       => InputHandle (F7 a b c d e f g) where
  validate (F7 ainv binv cinv dinv einv finv ginv) =
    feither id (\ (aval,(bval,(cval,(dval,(eval, (fval, gval)))))) -> F7 aval bval cval dval eval fval gval)
    (propagate (validate ainv) 
     (propagate (validate binv)
      (propagate (validate cinv)
       (propagate (validate dinv) 
        (propagate (validate einv)
	 (propagate (validate finv) (validate ginv)))))))
  isBound (F7 ainv binv cinv dinv einv finv ginv) =
    isBound ainv && isBound binv && isBound cinv && isBound dinv && isBound einv && isBound finv && isBound ginv
  ihNames (F7 ainv binv cinv dinv einv finv ginv) =
    ihNames ainv ++ ihNames binv ++ ihNames cinv ++ ihNames dinv ++ ihNames einv ++ ihNames finv ++ ihNames ginv

data F8 a b c d e f g h x = F8 (a x) (b x) (c x) (d x) (e x) (f x) (g x) (h x)
deF8 :: (a x -> b x -> c x -> d x -> e x -> f x -> g x -> h x -> r) -> (F8 a b c d e f g h x -> r)
deF8 g (F8 ax bx cx dx ex fx gx hx) = g ax bx cx dx ex fx gx hx

deValueF8 g (F8 ax bx cx dx ex fx gx hx) = g (value ax) (value bx) (value cx) (value dx) (value ex) (value fx) (value gx) (value hx)

instance (InputHandle a, InputHandle b, InputHandle c, InputHandle d, InputHandle e, InputHandle f, InputHandle g, InputHandle h)
       => InputHandle (F8 a b c d e f g h) where
  validate (F8 ainv binv cinv dinv einv finv ginv hinv) =
    feither id (\ (aval,(bval,(cval,(dval,(eval, (fval, (gval, hval))))))) -> F8 aval bval cval dval eval fval gval hval)
    (propagate (validate ainv) 
     (propagate (validate binv)
      (propagate (validate cinv)
       (propagate (validate dinv) 
        (propagate (validate einv) 
	 (propagate (validate finv)
	  (propagate (validate ginv) (validate hinv))))))))
  isBound (F8 ainv binv cinv dinv einv finv ginv hinv) =
    isBound ainv && isBound binv && isBound cinv && isBound dinv && isBound einv && isBound finv && isBound ginv && isBound hinv
  ihNames (F8 ainv binv cinv dinv einv finv ginv hinv) =
    ihNames ainv ++ ihNames binv ++ ihNames cinv ++ ihNames dinv ++ ihNames einv ++ ihNames finv ++ ihNames ginv ++ ihNames hinv

-- |'FL' is required to pass an unknown number of handles of the same
-- type need to the callback function in a form submission. The
-- handles need to be collected in a list and then wrapped in the 'FL' data constructor
data FL a x = FL [a x]
deFL :: ([a x] -> r) -> (FL a x -> r)
deFL g (FL axs) = g axs

deValueFL g (FL axs) = g (map value axs)

instance InputHandle a => InputHandle (FL a) where
  validate (FL ainvs) =
    g (map validate ainvs)		 -- [Either [ValidationError] (h VALID)]
    where 
    g = foldr h (Right (FL []))
    h ev evs = feither id (\ (v, FL vs) -> FL (v : vs)) (propagate ev evs)
  isBound (FL ainvs) =
    all isBound ainvs
  ihNames (FL ainvs) =
    concatMap ihNames ainvs

-- |'FA' comes handy when you want to tag an input handle with some extra
-- information, which is not itsefl an input handle and which is not validated
-- by a form submission. The tag is the first argument and the handle is the
-- second argument of the data constructor.
data FA a b x = FA a (b x)
deFA :: (a -> b x -> r) -> (FA a b x -> r)
deFA g (FA a bx) = g a bx

deValueFA g (FA a bx) = g a (value bx)

instance InputHandle b => InputHandle (FA a b) where
  validate (FA a binv) =
    feither id (FA a) (validate binv)
  isBound (FA a binv) =
    isBound binv
  ihNames (FA a binv) =
    ihNames binv

-- |Create a submission button with attached action.
submit :: (CGIMonad cgi, InputHandle h)
	=> h INVALID		-- ^input field handles to be validated and passed to callback action 
	-> (h VALID -> cgi ())	-- ^callback maps valid input handles to a CGI action
	-> HTMLField cgi x y ()	-- ^returns a field so that attributes can be attached
submit = submitInternal False

-- |Create a continuation button that takes no parameters.
submit0 :: (CGIMonad cgi) => cgi () -> HTMLField cgi x y ()
submit0 cont = submit F0 (\F0 -> cont)

-- |Create a submission button whose attached action is fired whenever the form
-- is submitted without explicitly clicking any submit button. This can happen if
-- an input field has an attached onclick="submit()" action.
defaultSubmit :: (CGIMonad cgi, InputHandle h) =>
  h INVALID -> (h VALID -> cgi ()) -> HTMLField cgi x y ()
defaultSubmit = submitInternal True

-- |Create an ordinary link serving as a submission button.
submitLink :: (CGIMonad cgi, InputHandle h) =>
  h INVALID -> (h VALID -> cgi ()) -> H.HTMLCons x y cgi ()
submitLink = submitInternalLink False

-- |Create a continuation link.
submitLink0 :: (CGIMonad cgi) => cgi () -> H.HTMLCons x y cgi ()
submitLink0 cont = submitLink F0 (const cont)

defaultSubmitLink :: (CGIMonad cgi, InputHandle h) =>
  h INVALID -> (h VALID -> cgi ()) -> H.HTMLCons x y cgi ()
defaultSubmitLink = submitInternalLink True

-- |Abstract type of decisions trees. These trees provide structured validation.
newtype DTree cgi x y = DTree { unDTree :: HTMLField cgi x y () }

-- |Create a submission button whose validation proceeds according to a decision
-- tree. Trees are built using 'dtleaf' and 'dtnode'.
submitx :: DTree cgi x y -> HTMLField cgi x y ()
submitx = unDTree

-- |Create a leaf in a decision tree from a CGI action. 
dtleaf :: (CGIMonad cgi) => cgi () -> DTree cgi x y
dtleaf action = DTree $ submit0 action

-- |Create a node in a decision tree. Takes an invalid input field and a
-- continuation. Validates the input field and passes it to the continuation if
-- the validation was successful. The continuation can dispatch on the value of
-- the input field and produces a new decision tree.
dtnode :: (CGIMonad cgi, InputHandle h) =>
  h INVALID -> (h VALID -> DTree cgi x y) -> DTree cgi x y
dtnode hinv next =
  if isBound hinv then
  case validate hinv of
    Right hval ->
      next hval
    Left ss ->
      DTree $ internalSubmitField False (Left ss)
  else 
    DTree $ internalSubmitField False (Left [])

submitInternal isDefault hinv g =
  internalSubmitField isDefault (validator hinv g)

validator hinv g =
  either Left (Right . g) (validate hinv)

submitInternalLink isDefault hinv g =
  internalSubmitLink isDefault (validator hinv g)
    

instance HasValue RadioGroup where
  value rg = valueRadioGroup rg

instance InputHandle (RadioGroup a) where
  validate rg = validateRadioGroup rg
  isBound rg = radioBound rg
  ihNames rg = [radioName rg]

instance HasValue SelectionGroup where
  value sg = valueSelectionGroup sg

instance InputHandle (SelectionGroup a) where
  validate sg = validateSelectionGroup sg
  isBound sg = selectionBound sg
  ihNames sg = [selectionName sg]

-- |Attach a CGI action to the value returned by the input field. Activation
-- means that data is submitted as soon as it is entered.
activate :: (CGIMonad cgi, InputHandle (i a), HasValue i) =>
  (a -> cgi ()) -> HTMLField cgi x y (i a INVALID) -> HTMLField cgi x y (i a INVALID)
activate actionFun inputField attrs =
  do invalid_inf <- inputField (do attrs
				   onChange $ "WASHSubmit(this.name);")
     let r = validate invalid_inf
	 rv = either Left (Right . value) r
     when (isBound invalid_inf) $
       activateInternal actionFun (head $ ihNames invalid_inf) rv
     return invalid_inf
