-- | Display callback.
display :: IORef ProgramState -> DisplayCallback
display ps = do
  programState <- get ps

  let l = Lights `elem` programState ^. flags
      f = Main.Fill `elem` programState ^. flags
      CameraShift theta phi radius = programState ^. cameraShift
      (points, focn)  = programState ^. cameraFocus
      Vertex3 x y z = points !! focn

  clear [ColorBuffer, DepthBuffer]

  when (Axis `elem` programState ^. flags)
    (preservingMatrix drawAxis >> return ())

  loadIdentity
  preservingMatrix (renderInfo programState)
  lighting $= if' l Enabled Disabled

  -- Camera movement
  let [nx, ny, nz] = [ radius * cos theta * sin phi
                     , radius * sin theta * sin phi
                     , radius * cos phi
                     ]

  translate $ Vector3 x y (-z + (-radius))
  rotate ((theta - pi) * (180 / pi)) $ Vector3 1 0 0
  rotate ((-phi) * (180/pi)) $ Vector3 0 1 0

  preservingColor . preservingMatrix $ do
    drawCube f

  ps $$! programState & angleState %~ (+ 1.0)
  writeLog ps [nx, ny, nz]
  updateTime ps

  swapBuffers

-- | Draws the X, Y and Z axis from origin towards positive <relatively
-- large number>. X axis is red, Y axis is green and Z axis is green.
-- Additionally, horizontal spacers are drawn every 1 unit on each axis.
display :: IORef ProgramState -> DisplayCallback
display ps = do
  programState <- get ps

  let l = Lights `elem` programState ^. flags
      f = Main.Fill `elem` programState ^. flags
      CameraShift theta phi radius = programState ^. cameraShift
      (points, focn)  = programState ^. cameraFocus
      Vertex3 x y z = points !! focn

  clear [ColorBuffer, DepthBuffer]

  when (Axis `elem` programState ^. flags)
    (preservingMatrix drawAxis >> return ())

  loadIdentity
  preservingMatrix (renderInfo programState)
  lighting $= if' l Enabled Disabled

  -- Camera movement
  let [nx, ny, nz] = [ radius * cos theta * sin phi
                     , radius * sin theta * sin phi
                     , radius * cos phi
                     ]

  translate $ Vector3 x y (-z + (-radius))
  rotate ((theta - pi) * (180 / pi)) $ Vector3 1 0 0
  rotate ((-phi) * (180/pi)) $ Vector3 0 1 0

  preservingColor . preservingMatrix $ do
    drawCube f

  ps $$! programState & angleState %~ (+ 1.0)
  writeLog ps [nx, ny, nz]
  updateTime ps

  swapBuffers

-- | Draws the X, Y and Z axis from origin towards positive <relatively
-- large number>. X axis is red, Y axis is green and Z axis is green.
-- Additionally, horizontal spacers are drawn every 1 unit on each axis.
display ps = do
  programState <- get ps

  let l = Lights `elem` programState ^. flags
      f = Main.Fill `elem` programState ^. flags
      CameraShift theta phi radius = programState ^. cameraShift
      (points, focn)  = programState ^. cameraFocus
      Vertex3 x y z = points !! focn

  clear [ColorBuffer, DepthBuffer]

  when (Axis `elem` programState ^. flags)
    (preservingMatrix drawAxis >> return ())

  loadIdentity
  preservingMatrix (renderInfo programState)
  lighting $= if' l Enabled Disabled

  -- Camera movement
  let [nx, ny, nz] = [ radius * cos theta * sin phi
                     , radius * sin theta * sin phi
                     , radius * cos phi
                     ]

  translate $ Vector3 x y (-z + (-radius))
  rotate ((theta - pi) * (180 / pi)) $ Vector3 1 0 0
  rotate ((-phi) * (180/pi)) $ Vector3 0 1 0

  preservingColor . preservingMatrix $ do
    drawCube f

  ps $$! programState & angleState %~ (+ 1.0)
  writeLog ps [nx, ny, nz]
  updateTime ps

  swapBuffers

-- | Draws the X, Y and Z axis from origin towards positive <relatively
-- large number>. X axis is red, Y axis is green and Z axis is green.
-- Additionally, horizontal spacers are drawn every 1 unit on each axis.