gameCycle :: WormsAction ()
gameCycle = do
  (GA timer remainingFood tailSize previousHeadPos score) <- getGameAttribute
  gState <- getGameState
  case gState of
      LevelStart n -> case n of
                        4 -> do
                              congratulations <- findObject "congratulations" "messages"
                              drawObject congratulations
                              if (timer == 0)
                                  then funExit
                                  else (setGameAttribute (GA (timer - 1) remainingFood tailSize previousHeadPos score))
                        _ -> do
                              disableGameFlags
                              level <- findObject ("level" ++ (show n)) "messages"
                              drawObject level
                              if (timer == 0)
                                  then (do setGameState (Level n)
                                           enableGameFlags
                                           snakeHead <- findObject "head" "head"
                                           setObjectAsleep False snakeHead
                                           setObjectPosition initPos snakeHead
                                           setObjectSpeed (0.0,speedMod) snakeHead
                                           setObjectCurrentPicture 5 snakeHead
                                           setGameAttribute (GA defaultTimer remainingFood tailSize previousHeadPos score)
                                           destroyObject level
                                           setNewMap n)
                                  else setGameAttribute (GA (timer - 1) remainingFood tailSize previousHeadPos score)
      Level n -> do
                  if (remainingFood == 0) -- advance level!
                      then  (do setGameState (LevelStart (n + 1))
                                resetTails
                                disableGameFlags
                                setGameAttribute (GA timer maxFood initTailSize initPos score))
                      else if (timer == 0) -- put a new food in the map
                             then (do food <- findObject "food" "food"
                                      newPos <- createNewFoodPosition
                                      setObjectPosition newPos food
                                      newFood <- findObject "food" "food"
                                      setObjectAsleep False newFood
                                      setGameAttribute (GA (-1) remainingFood tailSize previousHeadPos score)
                                      snakeHead <- findObject "head" "head"
                                      checkSnakeCollision snakeHead
                                      snakeHeadPosition <- getObjectPosition snakeHead
                                      moveTail snakeHeadPosition)
                             else if (timer > 0) -- there is no food in the map, so decrease the food timer
                                   then (do setGameAttribute (GA (timer - 1) remainingFood tailSize previousHeadPos score)
                                            snakeHead <- findObject "head" "head"
                                            checkSnakeCollision snakeHead
                                            snakeHeadPosition <- getObjectPosition snakeHead
                                            moveTail snakeHeadPosition)
                                   else (do -- there is a food in the map
                                      food <- findObject "food" "food"
                                      snakeHead <- findObject "head" "head"
                                      col <- objectsCollision snakeHead food
                                      if col
                                          then (do snakeHeadPosition <- getObjectPosition snakeHead
                                                   setGameAttribute (GA defaultTimer (remainingFood-1) (tailSize + 1) snakeHeadPosition (score + 1))
                                                   addTail previousHeadPos
                                                   setObjectAsleep True food)
                                          else (do checkSnakeCollision snakeHead
                                                   snakeHeadPosition <- getObjectPosition snakeHead
                                                   moveTail snakeHeadPosition))
                  showScore

      GameOver -> do
                      disableMapDrawing
                      gameover <- findObject "gameover" "messages"
                      drawMap
                      drawObject gameover
                      if (timer == 0)
                              then funExit
                              else (setGameAttribute (GA (timer - 1) 0 0 (0,0) 0))
gameCycle :: WormsAction ()
gameCycle = do
  (GA timer remainingFood tailSize previousHeadPos score) <- getGameAttribute
  gState <- getGameState
  case gState of
      LevelStart n -> case n of
                        4 -> do
                              congratulations <- findObject "congratulations" "messages"
                              drawObject congratulations
                              if (timer == 0)
                                  then funExit
                                  else (setGameAttribute (GA (timer - 1) remainingFood tailSize previousHeadPos score))
                        _ -> do
                              disableGameFlags
                              level <- findObject ("level" ++ (show n)) "messages"
                              drawObject level
                              if (timer == 0)
                                  then (do setGameState (Level n)
                                           enableGameFlags
                                           snakeHead <- findObject "head" "head"
                                           setObjectAsleep False snakeHead
                                           setObjectPosition initPos snakeHead
                                           setObjectSpeed (0.0,speedMod) snakeHead
                                           setObjectCurrentPicture 5 snakeHead
                                           setGameAttribute (GA defaultTimer remainingFood tailSize previousHeadPos score)
                                           destroyObject level
                                           setNewMap n)
                                  else setGameAttribute (GA (timer - 1) remainingFood tailSize previousHeadPos score)
      Level n -> do
                  if (remainingFood == 0) -- advance level!
                      then  (do setGameState (LevelStart (n + 1))
                                resetTails
                                disableGameFlags
                                setGameAttribute (GA timer maxFood initTailSize initPos score))
                      else if (timer == 0) -- put a new food in the map
                             then (do food <- findObject "food" "food"
                                      newPos <- createNewFoodPosition
                                      setObjectPosition newPos food
                                      newFood <- findObject "food" "food"
                                      setObjectAsleep False newFood
                                      setGameAttribute (GA (-1) remainingFood tailSize previousHeadPos score)
                                      snakeHead <- findObject "head" "head"
                                      checkSnakeCollision snakeHead
                                      snakeHeadPosition <- getObjectPosition snakeHead
                                      moveTail snakeHeadPosition)
                             else if (timer > 0) -- there is no food in the map, so decrease the food timer
                                   then (do setGameAttribute (GA (timer - 1) remainingFood tailSize previousHeadPos score)
                                            snakeHead <- findObject "head" "head"
                                            checkSnakeCollision snakeHead
                                            snakeHeadPosition <- getObjectPosition snakeHead
                                            moveTail snakeHeadPosition)
                                   else (do -- there is a food in the map
                                      food <- findObject "food" "food"
                                      snakeHead <- findObject "head" "head"
                                      col <- objectsCollision snakeHead food
                                      if col
                                          then (do snakeHeadPosition <- getObjectPosition snakeHead
                                                   setGameAttribute (GA defaultTimer (remainingFood-1) (tailSize + 1) snakeHeadPosition (score + 1))
                                                   addTail previousHeadPos
                                                   setObjectAsleep True food)
                                          else (do checkSnakeCollision snakeHead
                                                   snakeHeadPosition <- getObjectPosition snakeHead
                                                   moveTail snakeHeadPosition))
                  showScore

      GameOver -> do
                      disableMapDrawing
                      gameover <- findObject "gameover" "messages"
                      drawMap
                      drawObject gameover
                      if (timer == 0)
                              then funExit
                              else (setGameAttribute (GA (timer - 1) 0 0 (0,0) 0))
gameCycle = do
  (GA timer remainingFood tailSize previousHeadPos score) <- getGameAttribute
  gState <- getGameState
  case gState of
      LevelStart n -> case n of
                        4 -> do
                              congratulations <- findObject "congratulations" "messages"
                              drawObject congratulations
                              if (timer == 0)
                                  then funExit
                                  else (setGameAttribute (GA (timer - 1) remainingFood tailSize previousHeadPos score))
                        _ -> do
                              disableGameFlags
                              level <- findObject ("level" ++ (show n)) "messages"
                              drawObject level
                              if (timer == 0)
                                  then (do setGameState (Level n)
                                           enableGameFlags
                                           snakeHead <- findObject "head" "head"
                                           setObjectAsleep False snakeHead
                                           setObjectPosition initPos snakeHead
                                           setObjectSpeed (0.0,speedMod) snakeHead
                                           setObjectCurrentPicture 5 snakeHead
                                           setGameAttribute (GA defaultTimer remainingFood tailSize previousHeadPos score)
                                           destroyObject level
                                           setNewMap n)
                                  else setGameAttribute (GA (timer - 1) remainingFood tailSize previousHeadPos score)
      Level n -> do
                  if (remainingFood == 0) -- advance level!
                      then  (do setGameState (LevelStart (n + 1))
                                resetTails
                                disableGameFlags
                                setGameAttribute (GA timer maxFood initTailSize initPos score))
                      else if (timer == 0) -- put a new food in the map
                             then (do food <- findObject "food" "food"
                                      newPos <- createNewFoodPosition
                                      setObjectPosition newPos food
                                      newFood <- findObject "food" "food"
                                      setObjectAsleep False newFood
                                      setGameAttribute (GA (-1) remainingFood tailSize previousHeadPos score)
                                      snakeHead <- findObject "head" "head"
                                      checkSnakeCollision snakeHead
                                      snakeHeadPosition <- getObjectPosition snakeHead
                                      moveTail snakeHeadPosition)
                             else if (timer > 0) -- there is no food in the map, so decrease the food timer
                                   then (do setGameAttribute (GA (timer - 1) remainingFood tailSize previousHeadPos score)
                                            snakeHead <- findObject "head" "head"
                                            checkSnakeCollision snakeHead
                                            snakeHeadPosition <- getObjectPosition snakeHead
                                            moveTail snakeHeadPosition)
                                   else (do -- there is a food in the map
                                      food <- findObject "food" "food"
                                      snakeHead <- findObject "head" "head"
                                      col <- objectsCollision snakeHead food
                                      if col
                                          then (do snakeHeadPosition <- getObjectPosition snakeHead
                                                   setGameAttribute (GA defaultTimer (remainingFood-1) (tailSize + 1) snakeHeadPosition (score + 1))
                                                   addTail previousHeadPos
                                                   setObjectAsleep True food)
                                          else (do checkSnakeCollision snakeHead
                                                   snakeHeadPosition <- getObjectPosition snakeHead
                                                   moveTail snakeHeadPosition))
                  showScore

      GameOver -> do
                      disableMapDrawing
                      gameover <- findObject "gameover" "messages"
                      drawMap
                      drawObject gameover
                      if (timer == 0)
                              then funExit
                              else (setGameAttribute (GA (timer - 1) 0 0 (0,0) 0))