{-# LANGUAGE UnicodeSyntax, TypeSynonymInstances, MultiParamTypeClasses, FlexibleInstances, ScopedTypeVariables, FlexibleContexts, UndecidableInstances #-}
module Todos.Formatters 
  (OutItem (..),
   Formatter,
   outItem,
   startFormat, newLine,
   ConfigShow (..),
   ConfigAdd (..)
  ) where

import Prelude.Unicode
import Control.Monad
import Control.Monad.Reader
import System.Console.ANSI

import Todos.Types
import Todos.Config
-- import Todos.Default.ConfigInstances ()

-- | Item which could be printed to the console
data OutItem = OutString String
             | OutSetColor ColorIntensity Color
             | SetBold
             | ResetAll
    deriving (Show)

-- | Produce a list of OutItem's depending on PrintConfig
type Formatter c = Reader (PrintConfig c) [OutItem]

-- | Empty Formatter
startFormat  Formatter c
startFormat = return []

-- | Output given string
outString  String  Formatter c
outString s = return [OutString s]

-- | Output new line
newLine  Formatter c
newLine = outString "\n"

class ConfigAdd c a where
  -- | Execute Formatter and a consequently
  (<++>)  Formatter c  a  Formatter c

instance ConfigAdd c (Formatter c) where
  (<++>) = liftM2 ()

instance ConfigAdd c String where
  cm <++> s = cm <++> ((return [OutString s])  Formatter c)

setBold   IO ()
setBold = setSGR [SetConsoleIntensity BoldIntensity]

setColor  ColorIntensity  Color  IO ()
setColor int clr = setSGR [SetColor Foreground int clr]

-- | Reset all (color, bold, ...) attributes
reset   IO ()
reset = setSGR []

-- | Print OutItem to console
outItem   OutItem  IO ()
outItem (OutString s)   = putStr s
outItem (OutSetColor i c) = setColor i c
outItem SetBold         = setBold
outItem ResetAll        = reset

-- | Similar to Show, but output can depend on PrintConfig
class ConfigShow c s where
  configShow  s  Formatter c

instance ConfigShow c String where
  configShow s = return [OutString s]

instance ConfigShow c (Formatter c) where
  configShow = id
  
-- | Output bold (and maybe colored) item name
bold  (RuntimeConfig (PrintConfig c))  TodoItem  Formatter c
bold item = do
  let s = itemName item
  showColors  askBase outColors
  hlOn  askBase outHighlight
  getclr  asks printItemColor
  hlPred  asks doHighlight
  (hlInt, hlClr)  asks printHighlightColor
  return $ if showColors
              then if hlOn && hlPred item
                     then [SetBold, OutSetColor hlInt hlClr, OutString s, ResetAll]
                     else case getclr item of
                           Nothing         [SetBold, OutString s, ResetAll]
                           Just (int,clr)  [SetBold, OutSetColor int clr, OutString s, ResetAll]
              else [OutString s]

-- | Output colored item status
colorStatus  (RuntimeConfig (PrintConfig c))  String  Formatter c
colorStatus st = do
  getclr  asks printStatusColor
  let (int, clr) = getclr st
  colored int clr st

-- | Output string in specified color
colored  (RuntimeConfig (PrintConfig c))  ColorIntensity  Color  String  Formatter c
colored int clr str = do
  col  askBase outColors
  if col
    then return [OutSetColor int clr, OutString str, ResetAll]
    else return [OutString str]

printM  (RuntimeConfig (PrintConfig c))  TodoItem  Formatter c
printM item = askBase outputFormat >>= printf
  where
    printf :: (RuntimeConfig (PrintConfig c))  String  Formatter c
    printf ""          = return []
    printf [c]         = return [OutString [c]]
    printf ('%':c:xs)  = liftM2 () (itemPart c) (printf xs)
    printf ('\\':c:xs) = liftM2 (:) (outChar $ escape c) (printf xs)
    printf (x:xs)      = do r  printf xs
                            return $ (OutString [x]):r
    
    outChar c = return (OutString [c])

    escape '\\' = '\\'
    escape 't'  = '\t'
    escape 'n'  = '\n'
    escape 'b'  = '\b'
    escape 'v'  = '\v'
    escape c    = c

    tags = filter (not  null) $ itemTags item
    string s = return [OutString s]

    itemPart  (RuntimeConfig (PrintConfig c))  Char  Formatter c
    itemPart 'n' = bold item 
    itemPart 't' = if null tags
                     then return []
                     else string ("["  unwords tags  "] ")
    itemPart 's' = colorStatus (itemStatus item)
    itemPart 'p' = string (itemPrefix item)
    itemPart 'd' = string (itemDescr item)
    itemPart 'f' = string (fileName item)
    itemPart 'i' = colored Dull Yellow (makeId item)
    itemPart 'l' = string (show $ lineNr item)
    itemPart 'D' | null dates' = return []
                 | otherwise = string $ "("  dates'  ") "
    itemPart c   = string [c]

    dates' = showDates [StartDate `is` startDate item, EndDate `is` endDate item, Deadline `is` deadline item]

instance (RuntimeConfig (PrintConfig c))  ConfigShow c TodoItem where
    configShow item = (startFormat  Formatter c) <++> (printM item  Formatter c)