module Language.Haskell.HsColour.ColourHighlight
  ( Colour(..)
  , Highlight(..)
  , base256, unbase
  , rgb24bit_to_xterm256
  ,   projectToBasicColour8
  , hlProjectToBasicColour8
  ) where

import Data.Word

-- | Colours supported by ANSI codes.
data Colour = Black | Red | Green | Yellow | Blue | Magenta | Cyan | White | Rgb Word8 Word8 Word8
  deriving (Eq,Show,Read)

-- | Convert an integer in the range [0,2^24-1] to its base 256-triplet, passing the result to the given continuation (avoid unnecessary tupleism).
base256 :: Integral int => (Word8 -> Word8 -> Word8 -> r) -> int -> r
base256 kont x =
    let
        (r,gb) = divMod x 256
        (g,b)  = divMod gb 256
        fi = fromIntegral
    in 
        kont (fi r) (fi g) (fi b)

-- | Convert a three-digit numeral in the given (as arg 1) base to its integer value.
unbase :: Integral int => int -> Word8 -> Word8 -> Word8 -> int
unbase base r g b = (fi r*base+fi g)*base+fi b
    where fi = fromIntegral

-- | Approximate a 24-bit Rgb colour with a colour in the xterm256 6x6x6 colour cube, returning its index.
rgb24bit_to_xterm256 :: (Integral t) => Word8 -> Word8 -> Word8 -> t
rgb24bit_to_xterm256 r g b = let f = (`div` 43)
                          in 16 + unbase 6 (f r) (f g) (f b)


-- | Ap\"proxi\"mate a 24-bit Rgb colour with an ANSI8 colour. Will leave other colours unchanged and will never return an 'Rgb' constructor value. 
projectToBasicColour8 ::  Colour -> Colour
projectToBasicColour8 (Rgb r g b) = let f = (`div` 128)
                          in  toEnum ( unbase 2 (f r) (f g) (f b) )
projectToBasicColour8 x = x


-- | Lift 'projectToBasicColour8' to 'Highlight's
hlProjectToBasicColour8 ::  Highlight -> Highlight
hlProjectToBasicColour8 (Foreground c) = Foreground (projectToBasicColour8 c)
hlProjectToBasicColour8 (Background c) = Background (projectToBasicColour8 c)
hlProjectToBasicColour8 h = h

        

instance Enum Colour where
    toEnum 0 = Black
    toEnum 1 = Red 
    toEnum 2 = Green 
    toEnum 3 = Yellow 
    toEnum 4 = Blue 
    toEnum 5 = Magenta 
    toEnum 6 = Cyan 
    toEnum 7 = White 
    -- Arbitrary extension; maybe just 'error' out instead
    toEnum x = base256 Rgb (x-8)
    
    fromEnum Black   = 0
    fromEnum Red     = 1
    fromEnum Green   = 2
    fromEnum Yellow  = 3
    fromEnum Blue    = 4
    fromEnum Magenta = 5
    fromEnum Cyan    = 6
    fromEnum White   = 7
    -- Arbitrary extension; maybe just 'error' out instead
    fromEnum (Rgb r g b) = 8 + unbase 256 r g b
 

-- | Types of highlighting supported by ANSI codes (and some extra styles).
data Highlight =
    Normal
  | Bold
  | Dim
  | Underscore
  | Blink
  | ReverseVideo
  | Concealed
  | Foreground Colour
  | Background Colour
  -- The above styles are ANSI-supported, with the exception of the 'Rgb' constructor for 'Colour's.  Below are extra styles (e.g. for Html rendering).
  | Italic
  deriving (Eq,Show,Read)