{-# LANGUAGE DeriveGeneric #-}
{-|
Module      : Idris.Colours
Description : Support for colours within Idris.

License     : BSD3
Maintainer  : The Idris Community.
-}
module Idris.Colours (
    IdrisColour(..)
  , ColourTheme(..)
  , defaultTheme
  , colouriseKwd, colouriseBound, colouriseImplicit, colourisePostulate
  , colouriseType, colouriseFun, colouriseData, colouriseKeyword
  , colourisePrompt, colourise, ColourType(..), hStartColourise, hEndColourise
  ) where

import GHC.Generics (Generic)
import System.Console.ANSI
import System.IO (Handle)

data IdrisColour = IdrisColour { colour    :: Maybe Color
                               , vivid     :: Bool
                               , underline :: Bool
                               , bold      :: Bool
                               , italic    :: Bool
                               }
                   deriving (Eq, Show)

mkColour :: Color -> IdrisColour
mkColour c = IdrisColour (Just c) True False False False

data ColourTheme = ColourTheme { keywordColour   :: IdrisColour
                               , boundVarColour  :: IdrisColour
                               , implicitColour  :: IdrisColour
                               , functionColour  :: IdrisColour
                               , typeColour      :: IdrisColour
                               , dataColour      :: IdrisColour
                               , promptColour    :: IdrisColour
                               , postulateColour :: IdrisColour
                               }
                   deriving (Eq, Show, Generic)

-- | Idris's default console colour theme
defaultTheme :: ColourTheme
defaultTheme = ColourTheme { keywordColour = IdrisColour Nothing True False True False
                           , boundVarColour = mkColour Magenta
                           , implicitColour = IdrisColour (Just Magenta) True True False False
                           , functionColour = mkColour Green
                           , typeColour = mkColour Blue
                           , dataColour = mkColour Red
                           , promptColour = IdrisColour Nothing True False True False
                           , postulateColour = IdrisColour (Just Green) True False True False
                           }

-- | Compute the ANSI colours corresponding to an Idris colour
mkSGR :: IdrisColour -> [SGR]
mkSGR (IdrisColour c v u b i) =
    fg c ++
    [SetUnderlining SingleUnderline | u] ++
    [SetConsoleIntensity BoldIntensity | b] ++
    [SetItalicized True | i]
  where
    fg Nothing = []
    fg (Just c) = [SetColor Foreground (if v then Vivid else Dull) c]

-- | Set the colour of a string using POSIX escape codes
colourise :: IdrisColour -> String -> String
colourise c str = setSGRCode (mkSGR c) ++ str ++ setSGRCode [Reset]

-- | Start a colour on a handle, to support colour output on Windows
hStartColourise :: Handle -> IdrisColour -> IO ()
hStartColourise h c = hSetSGR h (mkSGR c)

-- | End a colour region on a handle
hEndColourise :: Handle -> IdrisColour -> IO ()
hEndColourise h _ = hSetSGR h [Reset]

-- | Set the colour of a string using POSIX escape codes, with trailing '\STX' denoting the end
-- (required by Haskeline in the prompt string)
colouriseWithSTX :: IdrisColour -> String -> String
colouriseWithSTX (IdrisColour c v u b i) str = setSGRCode sgr ++ "\STX" ++ str ++ setSGRCode [Reset] ++ "\STX"
    where sgr = fg c ++
                [SetUnderlining SingleUnderline | u] ++
                [SetConsoleIntensity BoldIntensity | b] ++
                [SetItalicized True | i]
          fg Nothing = []
          fg (Just c) = [SetColor Foreground (if v then Vivid else Dull) c]

colouriseKwd :: ColourTheme -> String -> String
colouriseKwd t = colourise (keywordColour t)

colouriseBound :: ColourTheme -> String -> String
colouriseBound t = colourise (boundVarColour t)

colouriseImplicit :: ColourTheme -> String -> String
colouriseImplicit t = colourise (implicitColour t)

colouriseFun :: ColourTheme -> String -> String
colouriseFun t = colourise (functionColour t)

colouriseType :: ColourTheme -> String -> String
colouriseType t = colourise (typeColour t)

colouriseData :: ColourTheme -> String -> String
colouriseData t = colourise (dataColour t)

colourisePrompt :: ColourTheme -> String -> String
colourisePrompt t = colouriseWithSTX (promptColour t)

colouriseKeyword :: ColourTheme -> String -> String
colouriseKeyword t = colourise (keywordColour t)

colourisePostulate :: ColourTheme -> String -> String
colourisePostulate t = colourise (postulateColour t)


data ColourType = KeywordColour
                | BoundVarColour
                | ImplicitColour
                | FunctionColour
                | TypeColour
                | DataColour
                | PromptColour
                | PostulateColour
                  deriving (Eq, Show, Bounded, Enum)
