{-# LANGUAGE OverloadedStrings, RecordWildCards, FlexibleInstances, CPP #-}
module DumpFormat
    ( DumpFormat(..)
    , readDumpFormat
    , dumpActivity
    , dumpSample
    , dumpSamples
    ) where

import Data.MyText (unpack, null, pack, Text)
import Data.Aeson
import qualified Data.ByteString.Lazy as LBS
import Data.Time
import Data.Time.Zones
import Data.Char
import Data.Foldable (toList)
import Control.Applicative ((<$>), (<|>), (<*>), pure)

import Data
import Text.Printf
import Data.List hiding (null)
import Prelude hiding (null)

data DumpFormat
    = DFShow
    | DFHuman
    | DFJSON
    deriving (Show, Eq)

instance ToJSON Text where
    toJSON = toJSON . unpack
instance FromJSON Text where
    parseJSON x = pack <$> parseJSON x

instance ToJSON (TimeLogEntry CaptureData) where
    toJSON (TimeLogEntry {..}) = object [
        "date" .= tlTime,
        "rate" .= tlRate,
        "inactive" .= cLastActivity tlData,
        "windows" .= cWindows tlData,
        "desktop" .= cDesktop tlData
        ]
    toEncoding (TimeLogEntry {..}) = pairs
        $ "date" .= tlTime
        <> "rate" .= tlRate
        <> "inactive" .= cLastActivity tlData
        <> "windows" .= cWindows tlData
        <> "desktop" .= cDesktop tlData

instance FromJSON (TimeLogEntry CaptureData) where
    parseJSON = withObject "TimeLogEntry" $ \v -> do
        tlTime <- v .: "date"
        tlRate <- v .: "rate"
        cLastActivity <- v .: "inactive"
        cWindows  <- v .: "windows"
        cDesktop <- v .: "desktop"
        let tlData = CaptureData {..}
        let entry = TimeLogEntry {..}
        pure entry

instance ToJSON WindowData where
    toJSON WindowData{..} = object
        [ "active" .= wActive
        , "hidden" .= wHidden
        , "title" .= wTitle
        , "program" .= wProgram
        , "desktop" .= wDesktop
        ]
    toEncoding WindowData{..} = pairs
        $  "active" .= wActive
        <> "hidden" .= wHidden
        <> "title" .= wTitle
        <> "program" .= wProgram
        <> "desktop" .= wDesktop

instance FromJSON WindowData where
    parseJSON = withObject "window" $ \v -> do
        wActive <- v .: "active"
        wHidden <- v .:! "hidden" .!= not wActive
        wTitle <- v .: "title"
        wProgram <- v .: "program"
        wDesktop <- v .:! "desktop" .!= ""
        pure WindowData{..}

readDumpFormat :: String -> Maybe DumpFormat
readDumpFormat arg =
    case map toLower arg of
        "human"      -> return DFHuman
        "show"       -> return DFShow
        "json"       -> return DFJSON
        _            -> Nothing

dumpActivity :: TimeLog (CaptureData, ActivityData) -> IO ()
dumpActivity = mapM_ go
 where
    go tle = do
        dumpHeader (tlTime tle) (cLastActivity cd)
        dumpDesktop (cDesktop cd)
        mapM_ dumpWindow (cWindows cd)
        dumpTags ad
      where
        (cd, ad) = tlData tle

dumpTags :: ActivityData -> IO ()
dumpTags = mapM_ go
  where go act = printf "    %s\n" (show act)

dumpHeader :: UTCTime -> Integer -> IO ()
dumpHeader time lastActivity = do
    tz <- loadLocalTZ
    printf "%s (%dms inactive):\n"
        (formatTime defaultTimeLocale "%F %X" (utcToLocalTimeTZ tz time))
        lastActivity

dumpWindow :: WindowData -> IO ()
dumpWindow WindowData{..} = do
    printf "    (%c)%-*s %-15s %s\n" a (dw :: Int) d p t
  where a | wActive   = '*'
          | wHidden   = ' '
          | otherwise = '.'
        (dw, d) | wDesktop == "" = (0, "")
                | otherwise      = (15, " [" ++ unpack wDesktop  ++ "]")
        p = unpack wProgram ++ ":"
        t = unpack wTitle

dumpDesktop :: Text -> IO ()
dumpDesktop d
    | null d    = return ()
    | otherwise = printf "    Current Desktop: %s\n" (unpack d)

dumpSample :: TimeLogEntry CaptureData -> IO ()
dumpSample tle = do
    dumpHeader (tlTime tle) (cLastActivity (tlData tle))
    dumpDesktop (cDesktop (tlData tle))
    mapM_ dumpWindow (cWindows (tlData tle))

dumpSamples :: DumpFormat -> TimeLog CaptureData -> IO ()
dumpSamples DFShow = mapM_ print

dumpSamples DFHuman = mapM_ dumpSample

dumpSamples DFJSON = enclose . sequence_ . intersperse (putStrLn ",") . map (LBS.putStr . encode)
  where
    enclose m = putStrLn "[" >> m >> putStrLn "]"
