{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module System.Hardware.Blink1.Types
  ( Word8
  , RGB(..)
  , black
  , Delay(..)
  , Pos(..)
  , EEPROMAddr(..)
  , serialNumLen
  ) where

import Data.Word (Word8)
import Data.Fixed (Centi)
import Numeric (showHex)

data RGB = RGB { red, green, blue :: !Word8 }

black :: RGB
black = RGB 0 0 0

showHex2 :: Word8 -> ShowS
showHex2 x
  | x < 16 = showChar '0' . showHex x
  | otherwise = showHex x

instance Show RGB where
  showsPrec _ (RGB r g b) = showChar '#' . showHex2 r . showHex2 g . showHex2 b

-- | time is counted in centiseconds
newtype Delay = Delay { delayCentiseconds :: Centi } deriving (Eq, Ord, Num, Real, Fractional, RealFrac)

instance Bounded Delay where
  minBound = Delay 0
  maxBound = Delay 655.36

instance Show Delay where
  showsPrec p (Delay s) = showsPrec p s . showChar 's'

-- | positions are counted 0-11
newtype Pos = Pos Word8 deriving (Eq, Ord, Enum, Num)

instance Bounded Pos where
  minBound = Pos 0
  maxBound = Pos 11

data EEPROMAddr
  = EEOSCCAL
  | EEBootMode
  | EESerialNum Word8
  | EEPatternStart
  deriving (Eq, Ord)

serialNumLen :: Word8
serialNumLen = 4

instance Enum EEPROMAddr where
  fromEnum EEOSCCAL = 0
  fromEnum EEBootMode = 1
  fromEnum (EESerialNum i) 
    | i < serialNumLen = 2 + fromIntegral i
    | otherwise = error "EEPROMAddr.fromEnum: invalid EESerialNum"
  fromEnum EEPatternStart = 6
  toEnum 0 = EEOSCCAL
  toEnum 1 = EEBootMode
  toEnum 6 = EEPatternStart
  toEnum x 
    | x >= 2 && x < 6 = EESerialNum (fromIntegral x-2)
    | otherwise = error "EEPROMAddr.toEnum: invalid address"

instance Bounded EEPROMAddr where
  minBound = EEOSCCAL
  maxBound = EEPatternStart

data BootMode
  = BootNormal
  | BootNightLight
  deriving (Eq, Ord, Enum, Bounded)