multipleChoiceQuestionWidget :: forall t m c q a e s. (MonadWidget t m, Show a, Eq a, Ord a,Data a,Data c,Ord c, Buttonable a)
  => Int -- maximum number of tries
  -> [a] -- fixed list of potential answers
  -> ExerciseId
  -> m ()
  -> ConfigWidgetBuilder m t c a
  -> AnswerRenderer c a
  -> (Dynamic t (Map a Score) -> Dynamic t (MultipleChoiceStore c a) -> m ())
  -> (Map c (Map a Score) -> (Int,Int))
  -> Dynamic t (Map String AudioBuffer)
  -> c
  -> Map a Score
  -> Event t ([a],a)
  -> m (Event t ExerciseDatum,Event t (Maybe (Synth ())),Event t c,Event t ExerciseNavigation)

multipleChoiceQuestionWidget maxTries answers exId exInstructions cWidget render eWidget calculateXp sysResources config initialEval newQuestion = elClass "div" "exerciseWrapper" $ mdo

  let initialStore = MultipleChoiceStore { scores = empty, xp = (0,1) } -- normally this would come as an argument, then reset session specific elements of store
  let newScoreMaps = fmap (newScores calculateXp) $ updated evaluations
  let storeUpdates = newScoreMaps
  currentStore <- foldDyn ($) initialStore storeUpdates

  let initialState = initialMultipleChoiceState config answers maxTries
  let newQuestionAndConfig = attachDyn dynConfig newQuestion
  let newQuestion' = fmap (\(c, q) -> newQuestionMultipleChoiceState c q) newQuestionAndConfig
  questionHeard0 <- holdDyn False $ leftmost [False <$ newQuestion, True <$ listenPressed]
  let questionHeard = nubDyn questionHeard0
  let questionHeard' = fmap (const onceQuestionHeard) $ ffilter (== True) $ updated questionHeard
  let answerPressed' = fmap answerSelected answerPressed
  let stateChanges = leftmost [newQuestion', questionHeard', answerPressed']
  multipleChoiceState <- foldDyn ($) initialState stateChanges
  modes <- mapDyn answerButtonModes multipleChoiceState
  modes' <- mapM (\x-> mapDyn (!!x) modes) [0,1..9]
  -- scores <- mapDyn scoreMap multipleChoiceState
  scores <- combineDyn (\x y -> maybe empty id $ Data.Map.lookup x (scoreMap y)) dynConfig multipleChoiceState

  -- MC question controls and answer input.
  -- (Event t ExerciseNavigation, Event t (), Event t a, Event t ExerciseNavigation)
  (closeExercise, listenPressed, answerPressed, nextQuestionNav) <- elClass "div" "topRow" $ do
    w <- elClass "div" "topRowHeader" $ do
      elClass "div" "questionTitle" $ text $ ("Exercise: " ++ showExerciseTitle exId)
      elClass "div" "closeExerciseButton" $ buttonClass "Close" "closeExerciseButton"
    (x,y,z) <- elClass "div" "buttonInterface" $ do
      x <- elClass "div" "listenButton" $ buttonClass "Listen" "listenButton"
      y <- elClass "div" "answerButtonWrapper" $ do
        -- leftmost <$> zipWithM (\f m -> answerButton (show f) m f) answers modes'
        leftmost <$> zipWithM makeButton answers modes'

      z <- elClass "div" "nextButton" $ revealableButton "Next" "nextButton" questionHeard
      return (x, y, z)
    return (CloseExercise <$ w, x, y, InQuestion <$ z)

  -- Instructions and configuration widgets.
  -- (Dynamic t c, Dynamic t (Maybe SourceNodeSpec), Event t (), Event t ())
  (dynConfig, dynSource, playPressed, stopPressed) <- elClass "div" "middleRow" $ do
    elClass "div" "evaluation" $ exInstructions
    elClass "div" "journal" $ do
      text "Configuration"
      elClass "div"  "configWidgetWrapper" $ cWidget sysResources config

  journalData <- elClass "div" "bottomRow" $ do
    elClass "div" "evaluation" $ do
      eWidget scores currentStore
    elClass "div" "journal" $ journalWidget

  let answerEvent = gate (fmap (==AnswerMode) . fmap mode . current $ multipleChoiceState) answerPressed
  let exploreAnswerPressed = gate (fmap (==ExploreMode) . fmap mode . current $ multipleChoiceState) answerPressed

  -- generate sounds to be played
  answer <- holdDyn Nothing $ fmap (Just . snd) newQuestion

  let listenToQuestionPressed = fmapMaybe id $ tagDyn answer listenPressed

  playbackSynthChanged <- connectPlaybackControls
    listenToQuestionPressed exploreAnswerPressed playPressed stopPressed
    dynConfig dynSource sysResources render

  let navEvents = leftmost [closeExercise, nextQuestionNav]

  -- generate data for adaptive questions and analysis
  let questionWhileListened = (\x -> (possibleAnswers x, correctAnswer x)) <$> tagDyn multipleChoiceState listenPressed
  let listenedQuestionData = attachDynWith (\c (q, a)-> ListenedQuestion c q a) dynConfig questionWhileListened

  let questionWhileReference = (\x -> (possibleAnswers x,correctAnswer x)) <$> tagDyn multipleChoiceState playPressed
  let listenedReferenceData = attachDynWith (\c (q, a) -> ListenedReference c q a) dynConfig questionWhileReference
  evaluations <- mapDyn scoreMap multipleChoiceState

  mcsAndConfig <- combineDyn (,) dynConfig multipleChoiceState
  let answerWithContext = attachDynWith (\(c,mcs) s -> (s, c, possibleAnswers mcs, correctAnswer mcs)) mcsAndConfig answerEvent
  let tempHack k m = findWithDefault empty k m
  let answerData = attachDynWith (\e (s,c,q,a) -> Answered s (tempHack c e) (tempHack c e) c q a) evaluations answerWithContext
  let questionWhileExplore = attachDynWith (\x y -> (possibleAnswers x,correctAnswer x,y)) multipleChoiceState answerPressed
  let listenedExploreData = attachDynWith (\c (q,a,s) -> ListenedExplore s c q a) dynConfig questionWhileExplore
  let datums = leftmost [listenedQuestionData,listenedReferenceData, answerData,listenedExploreData,journalData] :: Event t (Datum c [a] a (Map a Score) (MultipleChoiceStore c a))
  let datums' = fmap toExerciseDatum datums
  return (datums', playbackSynthChanged, updated dynConfig, navEvents)
multipleChoiceQuestionWidget :: forall t m c q a e s. (MonadWidget t m, Show a, Eq a, Ord a,Data a,Data c,Ord c, Buttonable a)
  => Int -- maximum number of tries
  -> [a] -- fixed list of potential answers
  -> ExerciseId
  -> m ()
  -> ConfigWidgetBuilder m t c a
  -> AnswerRenderer c a
  -> (Dynamic t (Map a Score) -> Dynamic t (MultipleChoiceStore c a) -> m ())
  -> (Map c (Map a Score) -> (Int,Int))
  -> Dynamic t (Map String AudioBuffer)
  -> c
  -> Map a Score
  -> Event t ([a],a)
  -> m (Event t ExerciseDatum,Event t (Maybe (Synth ())),Event t c,Event t ExerciseNavigation)
multipleChoiceQuestionWidget maxTries answers exId exInstructions cWidget render eWidget calculateXp sysResources config initialEval newQuestion = elClass "div" "exerciseWrapper" $ mdo

  let initialStore = MultipleChoiceStore { scores = empty, xp = (0,1) } -- normally this would come as an argument, then reset session specific elements of store
  let newScoreMaps = fmap (newScores calculateXp) $ updated evaluations
  let storeUpdates = newScoreMaps
  currentStore <- foldDyn ($) initialStore storeUpdates

  let initialState = initialMultipleChoiceState config answers maxTries
  let newQuestionAndConfig = attachDyn dynConfig newQuestion
  let newQuestion' = fmap (\(c, q) -> newQuestionMultipleChoiceState c q) newQuestionAndConfig
  questionHeard0 <- holdDyn False $ leftmost [False <$ newQuestion, True <$ listenPressed]
  let questionHeard = nubDyn questionHeard0
  let questionHeard' = fmap (const onceQuestionHeard) $ ffilter (== True) $ updated questionHeard
  let answerPressed' = fmap answerSelected answerPressed
  let stateChanges = leftmost [newQuestion', questionHeard', answerPressed']
  multipleChoiceState <- foldDyn ($) initialState stateChanges
  modes <- mapDyn answerButtonModes multipleChoiceState
  modes' <- mapM (\x-> mapDyn (!!x) modes) [0,1..9]
  -- scores <- mapDyn scoreMap multipleChoiceState
  scores <- combineDyn (\x y -> maybe empty id $ Data.Map.lookup x (scoreMap y)) dynConfig multipleChoiceState

  -- MC question controls and answer input.
  -- (Event t ExerciseNavigation, Event t (), Event t a, Event t ExerciseNavigation)
  (closeExercise, listenPressed, answerPressed, nextQuestionNav) <- elClass "div" "topRow" $ do
    w <- elClass "div" "topRowHeader" $ do
      elClass "div" "questionTitle" $ text $ ("Exercise: " ++ showExerciseTitle exId)
      elClass "div" "closeExerciseButton" $ buttonClass "Close" "closeExerciseButton"
    (x,y,z) <- elClass "div" "buttonInterface" $ do
      x <- elClass "div" "listenButton" $ buttonClass "Listen" "listenButton"
      y <- elClass "div" "answerButtonWrapper" $ do
        -- leftmost <$> zipWithM (\f m -> answerButton (show f) m f) answers modes'
        leftmost <$> zipWithM makeButton answers modes'

      z <- elClass "div" "nextButton" $ revealableButton "Next" "nextButton" questionHeard
      return (x, y, z)
    return (CloseExercise <$ w, x, y, InQuestion <$ z)

  -- Instructions and configuration widgets.
  -- (Dynamic t c, Dynamic t (Maybe SourceNodeSpec), Event t (), Event t ())
  (dynConfig, dynSource, playPressed, stopPressed) <- elClass "div" "middleRow" $ do
    elClass "div" "evaluation" $ exInstructions
    elClass "div" "journal" $ do
      text "Configuration"
      elClass "div"  "configWidgetWrapper" $ cWidget sysResources config

  journalData <- elClass "div" "bottomRow" $ do
    elClass "div" "evaluation" $ do
      eWidget scores currentStore
    elClass "div" "journal" $ journalWidget

  let answerEvent = gate (fmap (==AnswerMode) . fmap mode . current $ multipleChoiceState) answerPressed
  let exploreAnswerPressed = gate (fmap (==ExploreMode) . fmap mode . current $ multipleChoiceState) answerPressed

  -- generate sounds to be played
  answer <- holdDyn Nothing $ fmap (Just . snd) newQuestion

  let listenToQuestionPressed = fmapMaybe id $ tagDyn answer listenPressed

  playbackSynthChanged <- connectPlaybackControls
    listenToQuestionPressed exploreAnswerPressed playPressed stopPressed
    dynConfig dynSource sysResources render

  let navEvents = leftmost [closeExercise, nextQuestionNav]

  -- generate data for adaptive questions and analysis
  let questionWhileListened = (\x -> (possibleAnswers x, correctAnswer x)) <$> tagDyn multipleChoiceState listenPressed
  let listenedQuestionData = attachDynWith (\c (q, a)-> ListenedQuestion c q a) dynConfig questionWhileListened

  let questionWhileReference = (\x -> (possibleAnswers x,correctAnswer x)) <$> tagDyn multipleChoiceState playPressed
  let listenedReferenceData = attachDynWith (\c (q, a) -> ListenedReference c q a) dynConfig questionWhileReference
  evaluations <- mapDyn scoreMap multipleChoiceState

  mcsAndConfig <- combineDyn (,) dynConfig multipleChoiceState
  let answerWithContext = attachDynWith (\(c,mcs) s -> (s, c, possibleAnswers mcs, correctAnswer mcs)) mcsAndConfig answerEvent
  let tempHack k m = findWithDefault empty k m
  let answerData = attachDynWith (\e (s,c,q,a) -> Answered s (tempHack c e) (tempHack c e) c q a) evaluations answerWithContext
  let questionWhileExplore = attachDynWith (\x y -> (possibleAnswers x,correctAnswer x,y)) multipleChoiceState answerPressed
  let listenedExploreData = attachDynWith (\c (q,a,s) -> ListenedExplore s c q a) dynConfig questionWhileExplore
  let datums = leftmost [listenedQuestionData,listenedReferenceData, answerData,listenedExploreData,journalData] :: Event t (Datum c [a] a (Map a Score) (MultipleChoiceStore c a))
  let datums' = fmap toExerciseDatum datums
  return (datums', playbackSynthChanged, updated dynConfig, navEvents)
multipleChoiceQuestionWidget maxTries answers exId exInstructions cWidget render eWidget calculateXp sysResources config initialEval newQuestion = elClass "div" "exerciseWrapper" $ mdo

  let initialStore = MultipleChoiceStore { scores = empty, xp = (0,1) } -- normally this would come as an argument, then reset session specific elements of store
  let newScoreMaps = fmap (newScores calculateXp) $ updated evaluations
  let storeUpdates = newScoreMaps
  currentStore <- foldDyn ($) initialStore storeUpdates

  let initialState = initialMultipleChoiceState config answers maxTries
  let newQuestionAndConfig = attachDyn dynConfig newQuestion
  let newQuestion' = fmap (\(c, q) -> newQuestionMultipleChoiceState c q) newQuestionAndConfig
  questionHeard0 <- holdDyn False $ leftmost [False <$ newQuestion, True <$ listenPressed]
  let questionHeard = nubDyn questionHeard0
  let questionHeard' = fmap (const onceQuestionHeard) $ ffilter (== True) $ updated questionHeard
  let answerPressed' = fmap answerSelected answerPressed
  let stateChanges = leftmost [newQuestion', questionHeard', answerPressed']
  multipleChoiceState <- foldDyn ($) initialState stateChanges
  modes <- mapDyn answerButtonModes multipleChoiceState
  modes' <- mapM (\x-> mapDyn (!!x) modes) [0,1..9]
  -- scores <- mapDyn scoreMap multipleChoiceState
  scores <- combineDyn (\x y -> maybe empty id $ Data.Map.lookup x (scoreMap y)) dynConfig multipleChoiceState

  -- MC question controls and answer input.
  -- (Event t ExerciseNavigation, Event t (), Event t a, Event t ExerciseNavigation)
  (closeExercise, listenPressed, answerPressed, nextQuestionNav) <- elClass "div" "topRow" $ do
    w <- elClass "div" "topRowHeader" $ do
      elClass "div" "questionTitle" $ text $ ("Exercise: " ++ showExerciseTitle exId)
      elClass "div" "closeExerciseButton" $ buttonClass "Close" "closeExerciseButton"
    (x,y,z) <- elClass "div" "buttonInterface" $ do
      x <- elClass "div" "listenButton" $ buttonClass "Listen" "listenButton"
      y <- elClass "div" "answerButtonWrapper" $ do
        -- leftmost <$> zipWithM (\f m -> answerButton (show f) m f) answers modes'
        leftmost <$> zipWithM makeButton answers modes'

      z <- elClass "div" "nextButton" $ revealableButton "Next" "nextButton" questionHeard
      return (x, y, z)
    return (CloseExercise <$ w, x, y, InQuestion <$ z)

  -- Instructions and configuration widgets.
  -- (Dynamic t c, Dynamic t (Maybe SourceNodeSpec), Event t (), Event t ())
  (dynConfig, dynSource, playPressed, stopPressed) <- elClass "div" "middleRow" $ do
    elClass "div" "evaluation" $ exInstructions
    elClass "div" "journal" $ do
      text "Configuration"
      elClass "div"  "configWidgetWrapper" $ cWidget sysResources config

  journalData <- elClass "div" "bottomRow" $ do
    elClass "div" "evaluation" $ do
      eWidget scores currentStore
    elClass "div" "journal" $ journalWidget

  let answerEvent = gate (fmap (==AnswerMode) . fmap mode . current $ multipleChoiceState) answerPressed
  let exploreAnswerPressed = gate (fmap (==ExploreMode) . fmap mode . current $ multipleChoiceState) answerPressed

  -- generate sounds to be played
  answer <- holdDyn Nothing $ fmap (Just . snd) newQuestion

  let listenToQuestionPressed = fmapMaybe id $ tagDyn answer listenPressed

  playbackSynthChanged <- connectPlaybackControls
    listenToQuestionPressed exploreAnswerPressed playPressed stopPressed
    dynConfig dynSource sysResources render

  let navEvents = leftmost [closeExercise, nextQuestionNav]

  -- generate data for adaptive questions and analysis
  let questionWhileListened = (\x -> (possibleAnswers x, correctAnswer x)) <$> tagDyn multipleChoiceState listenPressed
  let listenedQuestionData = attachDynWith (\c (q, a)-> ListenedQuestion c q a) dynConfig questionWhileListened

  let questionWhileReference = (\x -> (possibleAnswers x,correctAnswer x)) <$> tagDyn multipleChoiceState playPressed
  let listenedReferenceData = attachDynWith (\c (q, a) -> ListenedReference c q a) dynConfig questionWhileReference
  evaluations <- mapDyn scoreMap multipleChoiceState

  mcsAndConfig <- combineDyn (,) dynConfig multipleChoiceState
  let answerWithContext = attachDynWith (\(c,mcs) s -> (s, c, possibleAnswers mcs, correctAnswer mcs)) mcsAndConfig answerEvent
  let tempHack k m = findWithDefault empty k m
  let answerData = attachDynWith (\e (s,c,q,a) -> Answered s (tempHack c e) (tempHack c e) c q a) evaluations answerWithContext
  let questionWhileExplore = attachDynWith (\x y -> (possibleAnswers x,correctAnswer x,y)) multipleChoiceState answerPressed
  let listenedExploreData = attachDynWith (\c (q,a,s) -> ListenedExplore s c q a) dynConfig questionWhileExplore
  let datums = leftmost [listenedQuestionData,listenedReferenceData, answerData,listenedExploreData,journalData] :: Event t (Datum c [a] a (Map a Score) (MultipleChoiceStore c a))
  let datums' = fmap toExerciseDatum datums
  return (datums', playbackSynthChanged, updated dynConfig, navEvents)