{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, ExistentialQuantification, GADTs #-}

module Network.IRC.Configuration
  ( Name
  , Value (..)
  , Configuration
  , Configurable (..)
  , fromMap
  , lookup
  , require
  , lookupDefault
  ) where

import qualified ClassyPrelude as P

import ClassyPrelude hiding (lookup)
import Data.Maybe           (fromJust)

type Name = Text

class Configurable a where
  fromValue :: Value -> Maybe a

  valueToList :: Value -> Maybe [a]
  valueToList (List xs) = mapM fromValue xs
  valueToList _         = Nothing

  toValue :: a -> Value

  listToValue :: [a] -> Value
  listToValue = List . map toValue

valueToNum :: (Num a) => Value -> Maybe a
valueToNum (Number n) = Just . fromInteger $ n
valueToNum _          = Nothing

instance Configurable Integer where
  fromValue   = valueToNum
  toValue = Number

instance Configurable Int where
  fromValue   = valueToNum
  toValue = Number . toInteger

instance Configurable Text where
  fromValue (String s) = Just s
  fromValue _          = Nothing

  toValue = String

instance Configurable Bool where
  fromValue (Boolean b) = Just b
  fromValue _           = Nothing

  toValue = Boolean

instance Configurable a => Configurable [a] where
  fromValue   = valueToList
  toValue = listToValue

data Value = String Text
           | Number Integer
           | Boolean Bool
           | List [Value]
           deriving (Eq, Show)

newtype Configuration = Configuration { configMap :: Map Name Value } deriving (Show)

fromMap :: Map Name Value -> Configuration
fromMap = Configuration

lookup :: (Configurable a) => Name -> Configuration -> Maybe a
lookup name Configuration {..} = join . map fromValue $ P.lookup name configMap

require :: (Configurable a) => Name -> Configuration -> a
require n = fromJust . lookup n

lookupDefault :: (Configurable a) => Name -> Configuration -> a -> a
lookupDefault n c v = fromMaybe v $ lookup n c