{-# LANGUAGE FlexibleInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}

module Data.Configurator.Instances () where

import Control.Applicative
import Data.Configurator.Types.Internal
import Data.Complex (Complex)
import Data.Fixed (Fixed, HasResolution)
import Data.Int (Int8, Int16, Int32, Int64)
import Data.Text.Encoding (encodeUtf8)
import Data.Ratio (Ratio, denominator, numerator)
import Data.Word (Word, Word8, Word16, Word32, Word64)
import Foreign.C.Types (CDouble, CFloat)
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as LB
import qualified Data.Text as T
import qualified Data.Text.Lazy as L

instance Configured Value where
    convert = Just

instance Configured Bool where
    convert (Bool v) = Just v
    convert _        = Nothing

convertNumberToNum :: (Num a) => Value -> Maybe a
convertNumberToNum (Number r)
    | denominator r == 1 = Just $ fromInteger $ numerator r
convertNumberToNum _ = Nothing

instance Configured Int where
    convert = convertNumberToNum

instance Configured Integer where
    convert = convertNumberToNum

instance Configured Int8 where
    convert = convertNumberToNum

instance Configured Int16 where
    convert = convertNumberToNum

instance Configured Int32 where
    convert = convertNumberToNum

instance Configured Int64 where
    convert = convertNumberToNum

instance Configured Word where
    convert = convertNumberToNum

instance Configured Word8 where
    convert = convertNumberToNum

instance Configured Word16 where
    convert = convertNumberToNum

instance Configured Word32 where
    convert = convertNumberToNum

instance Configured Word64 where
    convert = convertNumberToNum

convertNumberToFractional :: (Fractional a) => Value -> Maybe a
convertNumberToFractional (Number r) = Just $ fromRational r
convertNumberToFractional _ = Nothing

instance Configured Double where
    convert = convertNumberToFractional

instance Configured Float where
    convert = convertNumberToFractional

instance Configured CDouble where
    convert = convertNumberToFractional

instance Configured CFloat where
    convert = convertNumberToFractional

instance Integral a => Configured (Ratio a) where
    convert = convertNumberToFractional

instance RealFloat a => Configured (Complex a) where
    convert = convertNumberToFractional

instance HasResolution a => Configured (Fixed a) where
    convert = convertNumberToFractional

instance Configured T.Text where
    convert (String v) = Just v
    convert _          = Nothing

instance Configured Char where
    convert (String txt) | T.length txt == 1 = Just $ T.head txt
    convert _ = Nothing

    convertList = fmap T.unpack . convert

instance Configured L.Text where
    convert = fmap L.fromStrict . convert

instance Configured B.ByteString where
    convert = fmap encodeUtf8 . convert

instance Configured LB.ByteString where
    convert = fmap (LB.fromChunks . (:[])) . convert

instance (Configured a, Configured b) => Configured (a,b) where
    convert (List [a,b]) = (,) <$> convert a <*> convert b
    convert _            = Nothing

instance (Configured a, Configured b, Configured c) => Configured (a,b,c) where
    convert (List [a,b,c]) = (,,) <$> convert a <*> convert b <*> convert c
    convert _              = Nothing

instance (Configured a, Configured b, Configured c, Configured d)
    => Configured (a,b,c,d) where
    convert (List [a,b,c,d]) = (,,,) <$> convert a <*> convert b <*> convert c
                                     <*> convert d
    convert _                = Nothing
