{-# LANGUAGE DataKinds #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
module Graphics.Liveplot.Types where

import Linear (V2, M33)
import Data.Vinyl
import Graphics.Rendering.OpenGL (GLfloat, Position(..), Size(..))
import Graphics.GLUtil.Camera2D
import Graphics.UI.GLFW
import MVC (Pipe)
import Data.Set (Set)
import Control.Concurrent.STM
import qualified Pipes.Prelude as Pipes

import Graphics.Liveplot.Utils

-- OpenGL events
data Event =
    Timestep Double
  | Keys     (Set Key)
  | Buttons  (Set MouseButton)
  | MousePos (V2 Double)
  | WinSize  (V2 Int)
  | Quit
  deriving Show

-- A record each drawing function will receive.
type Viewport = (Position, Size)
type AppInfo = FieldRec '[ '("cam", M33 GLfloat) ]

data GLApp = GLApp AppInfo Viewport
  deriving Show

data SensorReading a = Reading String a
  deriving (Eq, Show)

instance Functor SensorReading where
  fmap f (Reading s v) = (Reading s (f v))

type PlotInit a = (TVar (Maybe [a]), (SensorReading a) -> IO (), Maybe [a] -> GraphInfo -> IO ())

class Plottable a where
  initplot :: GraphInfo -> IO (PlotInit a)

accepts :: String -> SensorReading t -> Bool
accepts n (Reading s' _) = n == s'

named :: Monad m => String -> Pipe a (SensorReading a) m r
named n = Pipes.map (\x -> Reading n x)

-- buffer value and values in TVar
bufferTVar :: Fractional a =>
             String -> Int -> TVar (Maybe [a]) -> SensorReading a -> IO ()
bufferTVar name buflen tvar sample@(Reading _ val) = do
  case accepts name sample of
    True -> atomically $ do
        mtvar <- readTVar tvar
        case mtvar of
          Just cval -> writeTVar tvar $ Just $ rpad buflen 0.0 $ take buflen $ val:cval
          Nothing -> writeTVar tvar $ Just $ replicate buflen 0.0
    _ -> return ()

data GraphColor = Red | Green
  deriving (Show, Eq, Ord)

data GraphInfo = GraphInfo {
      graph_name :: String
    , graph_appinfo :: AppInfo
    , graph_viewport :: Viewport
    , graph_samples :: Int
    , graph_resolution :: Int
    , graph_color :: GraphColor
    , graph_points :: Int
    , graph_scale :: (Float, Float)
    , graph_offset :: (Int, Int)
    }
  deriving (Show, Eq, Ord)

defaultCam :: Camera GLfloat
defaultCam = camera2D

defaultViewport :: Viewport
defaultViewport = (Position 0 0, Size 1920 1080)

defaultAppInfo :: AppInfo
defaultAppInfo = SField =: camMatrix defaultCam

defGI :: GraphInfo
defGI = GraphInfo {
      graph_name = "unnamed"
    , graph_appinfo = defaultAppInfo
    , graph_viewport = defaultViewport
    , graph_samples = 100
    , graph_resolution = 100
    , graph_color = Red
    , graph_points = 100
    , graph_scale = (1, 1)
    , graph_offset = (0, 0)
   }