{-# LANGUAGE OverloadedStrings   #-}
{-# LANGUAGE GADTs               #-}
{-# LANGUAGE FlexibleContexts    #-}
{-# LANGUAGE RankNTypes          #-}
{-# LANGUAGE RecursiveDo         #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DeriveGeneric       #-}
{-# LANGUAGE RecordWildCards     #-}
{-# LANGUAGE DataKinds           #-}

module Main ( main ) where

import           GHC.Generics (Generic)
import           Control.Monad (void)
import           Control.Monad.Fix (MonadFix)
import           Control.Applicative ((<*>), (<$>))
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as B8
import qualified Data.ByteString.Lazy.Char8 as L8
import           Data.ByteString (ByteString)
import           Data.Proxy
import           Data.Hex (hex, unhex)
import qualified Data.Map as M
import qualified Data.Text as T
import           Data.Text (Text)
import           Data.Map (Map, fromList)
import           Data.Monoid ((<>))
import           Data.Maybe
import           Reflex.Dom.Core
import           Reflex.Dom.SemanticUI
import           Reflex.Dom.Path
import           Servant.API
import           Servant.Reflex
import           Servant.Reflex.Multi
import           Safe (readMay)
import           Zero.Argon2 (Hash(..), Options(..), performCredentialsHash, performHash, credentialsFromHash, defaultOptions)
import           Zero.GHCJS
import           Zero.View (View(..))
import           Zero.Index (Index(..))
import           Zero.Authentication.Internal
import           Zero.Identification.API
import           Zero.Identification.Internal (Certificate(..), buildIdentityAssertion)
import           Zero.Identification.Client
import           Zero.SessionToken.Internal
import           Zero.KeyFetchToken.Internal (KeyFetchState(..))
import           Zero.KeyFetchToken.Client
import           Zero.Settings.Client (baseUrl)
import           Zero.Sjcl.BitArray
import qualified Zero.Sjcl.BitArray as BitArray
import           Zero.Token
import           Zero.Navigation
import           Zero.Widget
import           Zero.Registration.Widget
import           Zero.Verification.Widget
import           Zero.Authentication.Widget
import           Zero.Account.Profile.Widget

------------------------------------------------------------------------------

routePath :: forall t m. (
    SupportsServantReflex t m
  , MonadWidget t m
  ) => Behavior t (Maybe WidgetState) -> Route -> m (Event t Route, Event t WidgetState)
routePath bhRouteEvent RegistrationRoute =
  registrationLayout
routePath bhRouteEvent VerificationRoute =
  verificationLayout bhRouteEvent
routePath bhRouteEvent AuthenticationRoute =
  authenticationLayout
routePath bhRouteEvent ProfileRoute = do
  (evRoute, evState) <- profileWidget bhRouteEvent
  return ( (\_ -> ProfileRoute) <$> evRoute
         , ProfileRouteState <$> evState
         )

------------------------------------------------------------------------------

main :: IO ()
main = do
  mainWidgetWithCss (B8.pack $ T.unpack semanticCSS) $ run

run :: forall t m. ( SupportsServantReflex t m,
                     DomBuilder t m,
                     DomBuilderSpace m ~ GhcjsDomSpace,
                     MonadFix m,
                     PostBuild t m,
                     MonadHold t m,
                     MonadWidget t m
                   ) => m ()
run = do
  -- routeEventGen has type Dynamic t (Event t WidgetState), meaning
  -- an event which changes the state of the next widget is produced
  -- on creation of the pathWidget.

  -- We extract the Event from the dynamic and freeze the value which
  -- occurs in a behavior and then pass that to the next widget because
  -- the event fires before the next widget has had the chance to load.
  rec routeEventGen <- pathWidget (routePath bhRouteEvent)
      let evRouteEvent = switchPromptlyDyn routeEventGen
      bhRouteEvent <- hold Nothing (Just <$> evRouteEvent)

  divClass "ui hidden divider" (return ())

  footer
