module Handler.Schedule
  ( getScheduleR, postScheduleR
  ) where

import Import

import Handler.Utils.Form

import Handler.Utils.Schedule.Types
import Handler.Utils.Schedule.Week


getScheduleR, postScheduleR :: Handler Html
getScheduleR = postScheduleR
postScheduleR = do
  uid <- requireAuthId

  -- TODO: local instead of global get params?
  mOptions <- lookupGlobalGetParam GetScheduleOptions

  let
    -- TODO: persist default schedule view in user settings and choose scheduleView accordingly
    defaultScheduleOptions :: ScheduleOptions
    defaultScheduleOptions = ScheduleOptions
                             { scheduleView   = ScheduleViewWeek
                             , scheduleOffset = ScheduleOffsetNone
                             , scheduleOptionsAction = ScheduleSetDefault
                             }

    currentScheduleOptions :: ScheduleOptions
    currentScheduleOptions = fromMaybe defaultScheduleOptions mOptions

    scheduleOptionsForm :: Html -> MForm (HandlerFor UniWorX) (FormResult ScheduleOptions, Widget)
    scheduleOptionsForm csrf = do
      (viewRess, viewWidgets) <- fmap unzip . for (universeF :: [ScheduleView]) $ \sView ->
        mopt (buttonFieldNoParse ScheduleOptions
                                 { scheduleView   = sView
                                 , scheduleOffset = ScheduleOffsetNone
                                 , scheduleOptionsAction = ScheduleSetView
                                 })
             ("" { fsName  = Just $ toPathPiece GetScheduleOptions
                 , fsAttrs = if sView == scheduleView currentScheduleOptions then [("disabled","")] else mempty
                 }) Nothing
      viewRes <- if
        -- TODO: ignoring FormFailures for now
        -- | Just errs <- fromNullable (filter (is _FormFailure) viewRess) -> do
        --   mapM_ formFailure2Alerts errs
        --   $logInfoS "SCHEDULE-VIEW" $ "encountered error(s): " <> tshow errs
        --   (return . FormFailure . mconcat . catMaybes . (fmap $ preview _FormFailure) . toNullable) errs
        | otherwise -> case catMaybes $ catMaybes (formResultToMaybe <$> viewRess) of
          [ScheduleOptions{scheduleView=sView}] -> return $ FormSuccess $ ScheduleOptions
                                                   { scheduleView   = sView
                                                   , scheduleOffset = ScheduleOffsetNone
                                                   , scheduleOptionsAction = ScheduleSetView
                                                   }
          _ -> return $ FormSuccess $ currentScheduleOptions
                                      { scheduleOffset        = ScheduleOffsetNone
                                      , scheduleOptionsAction = ScheduleSetView
                                      }

      let
        offsetBtns = case viewRes of
          FormSuccess ScheduleOptions{scheduleView=ScheduleViewWeek} -> [ ScheduleOffsetDays (-7)
                                                                        , ScheduleOffsetDays (-1)
                                                                        , ScheduleOffsetNone
                                                                        , ScheduleOffsetDays   1
                                                                        , ScheduleOffsetDays   7
                                                                        ] <&> (\sNewOffset -> currentScheduleOptions
                                                                                { scheduleOffset = case sNewOffset of
                                                                                                     ScheduleOffsetNone -> ScheduleOffsetNone
                                                                                                     _ -> (scheduleOffset currentScheduleOptions) `addOffset` sNewOffset
                                                                                , scheduleOptionsAction = ScheduleSetOffset (offsetInDays sNewOffset)
                                                                                })
          _ -> mempty

      (offsetRess, offsetWidgets) <- fmap unzip . for offsetBtns $ \btn ->
        mopt (buttonFieldNoParse btn) ("" { fsName  = Just $ toPathPiece GetScheduleOptions
                                          , fsAttrs = if offsetInDays (scheduleOffset btn) == offsetInDays (scheduleOffset currentScheduleOptions) then [("disabled","")] else mempty
                                          }) Nothing
      offsetRes <- if
        -- TODO: ignoring FormFailures for now
        -- | Just errs <- fromNullable (filter (is _FormFailure) offsetRess) -> do
        --   mapM_ formFailure2Alerts errs
        --   $logInfoS "SCHEDULE-OFFSET" $ "encountered error(s): " <> tshow errs
        --   (return . FormFailure . mconcat . catMaybes . (fmap $ preview _FormFailure) . toNullable) errs
        | otherwise -> case catMaybes $ catMaybes (formResultToMaybe <$> offsetRess) of
          [opt] -> return $ FormSuccess opt
          _ -> return $ FormSuccess currentScheduleOptions

      let
        -- TODO: work in progress
        scheduleResult = case (viewRes, offsetRes) of
          (_, opts@(FormSuccess _)) -> opts
          (opts@(FormSuccess _), _) -> opts
          _ -> FormSuccess currentScheduleOptions
        optionsWidget = $(widgetFile "schedule/options")
      
      return (scheduleResult, optionsWidget)

  ((optionsRes, optionsWidget), optionsEnctype) <- runFormGet scheduleOptionsForm

  let
    schedule = case optionsRes of
      FormSuccess ScheduleOptions{scheduleView=ScheduleViewWeek,..} -> weekSchedule uid scheduleOffset
      _ -> mempty  -- TODO: don't swallow errors!

  siteLayoutMsg MsgMenuSchedule $ do
    setTitleI MsgMenuSchedule
    $(widgetFile "schedule")


-- | Join two ScheduleOffsets by addition
addOffset :: ScheduleOffset -> ScheduleOffset -> ScheduleOffset
addOffset ScheduleOffsetNone offset = offset
addOffset offset ScheduleOffsetNone = offset
addOffset (ScheduleOffsetDays d) (ScheduleOffsetDays d') = ScheduleOffsetDays $ d + d'

-- | Calculate number of offset days from ScheduleOffset
offsetInDays :: ScheduleOffset -> Int
offsetInDays  ScheduleOffsetNone    = 0
offsetInDays (ScheduleOffsetDays d) = d
