module GUI.Timeline.Motion (
    zoomIn, zoomOut, zoomToFit,
    scrollLeft, scrollRight, scrollToBeginning, scrollToEnd, scrollTo, centreOnCursor,
    vscrollDown, vscrollUp,
  ) where

import GUI.Timeline.Types
import GUI.Timeline.Sparks
import Events.HECs

import Graphics.UI.Gtk

import Data.IORef
import Control.Monad
-- import Text.Printf
-- import Debug.Trace

-------------------------------------------------------------------------------
-- Zoom in works by expanding the current view such that the
-- left hand edge of the original view remains at the same
-- position and the zoom in factor is 2.
-- For example, zoom into the time range 1.0 3.0
-- produces a new view with the time range 1.0 2.0

zoomIn :: TimelineState -> Timestamp -> IO ()
zoomIn  = zoom (/2)

zoomOut :: TimelineState -> Timestamp -> IO ()
zoomOut  = zoom (*2)

zoom :: (Double -> Double) -> TimelineState -> Timestamp -> IO ()
zoom factor TimelineState{timelineAdj, scaleIORef} cursor = do
  scaleValue <- readIORef scaleIORef
  -- TODO: we'd need HECs, as below, to fit maxScale to graphs at hand
  let maxScale = 10000000000  -- big enough for hours of eventlogs
      clampedFactor =
        if factor scaleValue < 0.2 || factor scaleValue > maxScale
        then id
        else factor
      newScaleValue = clampedFactor scaleValue
  writeIORef scaleIORef newScaleValue

  hadj_value <- adjustmentGetValue timelineAdj
  hadj_pagesize <- adjustmentGetPageSize timelineAdj -- Get size of bar

  let newPageSize = clampedFactor hadj_pagesize
  adjustmentSetPageSize timelineAdj newPageSize

  let cursord = fromIntegral cursor
  when (cursord >= hadj_value && cursord < hadj_value + hadj_pagesize) $
    adjustmentSetValue timelineAdj $
        cursord - clampedFactor (cursord - hadj_value)

  let pageshift = 0.9 * newPageSize
  let nudge     = 0.1 * newPageSize

  adjustmentSetStepIncrement timelineAdj nudge
  adjustmentSetPageIncrement timelineAdj pageshift

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

zoomToFit :: TimelineState -> Maybe HECs -> IO ()
zoomToFit TimelineState{scaleIORef, maxSpkIORef,timelineAdj,
                        timelineDrawingArea} mb_hecs = do
  case mb_hecs of
    Nothing   -> return ()
    Just hecs -> do
      let lastTx = hecLastEventTime hecs
          upper = fromIntegral lastTx
          lower = 0
      (w, _) <- widgetGetSize timelineDrawingArea
      let newScaleValue = upper / fromIntegral w
          (sliceAll, profAll) = treesProfile newScaleValue 0 lastTx hecs
          -- TODO: verify that no empty lists possible below
          maxmap l = maximum (0 : map (maxSparkRenderedValue sliceAll) l)
          maxAll = map maxmap profAll
          newMaxSpkValue = maximum (0 : maxAll)

      writeIORef scaleIORef newScaleValue
      writeIORef maxSpkIORef newMaxSpkValue

      -- Configure the horizontal scrollbar units to correspond to micro-secs.
      adjustmentSetLower    timelineAdj lower
      adjustmentSetValue    timelineAdj lower
      adjustmentSetUpper    timelineAdj upper
      adjustmentSetPageSize timelineAdj upper
      -- TODO: this seems suspicious:
      adjustmentSetStepIncrement timelineAdj 0
      adjustmentSetPageIncrement timelineAdj 0

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

scrollLeft, scrollRight, scrollToBeginning, scrollToEnd :: TimelineState -> IO ()

scrollLeft        = scroll (\val page l _ -> l `max` (val - page/2))
scrollRight       = scroll (\val page _ u -> (u - page) `min` (val + page/2))
scrollToBeginning = scroll (\_   _    l _ ->  l)
scrollToEnd       = scroll (\_   _    _ u ->  u)

scrollTo :: TimelineState -> Double -> IO ()
scrollTo s x      = scroll (\_   _    _ _ ->  x) s

centreOnCursor :: TimelineState -> Timestamp -> IO ()

centreOnCursor state cursor =
  scroll (\_ page l _u -> max l (fromIntegral cursor - page/2)) state

scroll :: (Double -> Double -> Double -> Double -> Double)
       -> TimelineState -> IO ()
scroll adjust TimelineState{timelineAdj} = do
  hadj_value <- adjustmentGetValue timelineAdj
  hadj_pagesize <- adjustmentGetPageSize timelineAdj
  hadj_lower <- adjustmentGetLower timelineAdj
  hadj_upper <- adjustmentGetUpper timelineAdj
  let newValue = adjust hadj_value hadj_pagesize hadj_lower hadj_upper
      newValue' = max hadj_lower (min (hadj_upper - hadj_pagesize) newValue)
  adjustmentSetValue timelineAdj newValue'

vscrollDown, vscrollUp :: TimelineState -> IO ()
vscrollDown = vscroll (\val page _l  u -> (u - page) `min` (val + page/8))
vscrollUp   = vscroll (\val page  l _u -> l `max` (val - page/8))

vscroll :: (Double -> Double -> Double -> Double -> Double)
        -> TimelineState -> IO ()
vscroll adjust TimelineState{timelineVAdj} = do
  hadj_value <- adjustmentGetValue timelineVAdj
  hadj_pagesize <- adjustmentGetPageSize timelineVAdj
  hadj_lower <- adjustmentGetLower timelineVAdj
  hadj_upper <- adjustmentGetUpper timelineVAdj
  let newValue = adjust hadj_value hadj_pagesize hadj_lower hadj_upper
  adjustmentSetValue timelineVAdj newValue
  adjustmentValueChanged timelineVAdj

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