module Text.ValveVKV.Class where

import Text.ValveVKV.Internal
import Control.Monad (forM)
import Data.Maybe (mapMaybe)
import GHC.Base (NonEmpty)
import Data.List.NonEmpty (NonEmpty, nonEmpty)

findFromName :: ValveKeyValueEntry -> String -> [ValveKeyValueEntry]
findFromName :: ValveKeyValueEntry -> String -> [ValveKeyValueEntry]
findFromName (KVObject (Pair String
_ [ValveKeyValueEntry]
stuff)) String
name =
    (ValveKeyValueEntry -> Maybe ValveKeyValueEntry)
-> [ValveKeyValueEntry] -> [ValveKeyValueEntry]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe ValveKeyValueEntry -> Maybe ValveKeyValueEntry
finder [ValveKeyValueEntry]
stuff
    where
        finder :: ValveKeyValueEntry -> Maybe ValveKeyValueEntry
        finder :: ValveKeyValueEntry -> Maybe ValveKeyValueEntry
finder this :: ValveKeyValueEntry
this@(KVObject (Pair String
thisname [ValveKeyValueEntry]
s)) = if String
thisname String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
name then ValveKeyValueEntry -> Maybe ValveKeyValueEntry
forall a. a -> Maybe a
Just ValveKeyValueEntry
this else Maybe ValveKeyValueEntry
forall a. Maybe a
Nothing
        finder this :: ValveKeyValueEntry
this@(KVString (Pair String
thisname String
s)) = if String
thisname String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
name then ValveKeyValueEntry -> Maybe ValveKeyValueEntry
forall a. a -> Maybe a
Just ValveKeyValueEntry
this else Maybe ValveKeyValueEntry
forall a. Maybe a
Nothing
        finder this :: ValveKeyValueEntry
this@(KVInt (Pair String
thisname Int
s)) = if String
thisname String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
name then ValveKeyValueEntry -> Maybe ValveKeyValueEntry
forall a. a -> Maybe a
Just ValveKeyValueEntry
this else Maybe ValveKeyValueEntry
forall a. Maybe a
Nothing
findFromName ValveKeyValueEntry
_ String
_ = []

-- | This operator receives an entry on the left side and a string on the right side. It tries to find the string subentry named the string inside the entry you gave in on the left.

(.:) :: ValveVKV a => ValveKeyValueEntry -> String -> Maybe a 
ValveKeyValueEntry
context .: :: ValveKeyValueEntry -> String -> Maybe a
.: String
name =
    let results :: [ValveKeyValueEntry]
results = ValveKeyValueEntry -> String -> [ValveKeyValueEntry]
findFromName ValveKeyValueEntry
context String
name in
    case [ValveKeyValueEntry]
results of
        [] -> Maybe a
forall a. Maybe a
Nothing
        ValveKeyValueEntry
x:[ValveKeyValueEntry]
_ -> ValveKeyValueEntry -> ValveKeyValueEntry -> Maybe a
forall a.
ValveVKV a =>
ValveKeyValueEntry -> ValveKeyValueEntry -> Maybe a
fromValveVKV ValveKeyValueEntry
x ValveKeyValueEntry
context
infixl 5 .:

-- | This operator receives an entry on the left side and a string on the right side. It tries to find the subentry named the string inside the entry you gave in on the left.

(^:) :: ValveKeyValueEntry -> String -> Maybe String
ValveKeyValueEntry
context ^: :: ValveKeyValueEntry -> String -> Maybe String
^: String
name =
    let results :: [ValveKeyValueEntry]
results = ValveKeyValueEntry -> String -> [ValveKeyValueEntry]
findFromName ValveKeyValueEntry
context String
name in
    case [ValveKeyValueEntry]
results of
        (KVString (Pair String
_ String
s)):[ValveKeyValueEntry]
_ -> String -> Maybe String
forall a. a -> Maybe a
Just String
s
        [ValveKeyValueEntry]
_ -> Maybe String
forall a. Maybe a
Nothing
infixl 5 ^:

-- | A type synonim for ValveKeyValueEntry

type Context = ValveKeyValueEntry


-- | The class that lets a value to be made from a Valve value-keyvalue format.

-- For example, if you have

-- @

-- data My = My {name :: String, count :: Int}

-- @

-- You write your instance as

-- @

-- instance ValveVKV My where

--     fromValveVKV this _ =

--         My \<$\> this ^: "name" \<*\> this .: "count"

-- @

class ValveVKV a where
    -- | The first argument is the entry that should be turned into the type. The second argument is the entry just above that.

    fromValveVKV :: ValveKeyValueEntry -> Context -> Maybe a

instance ValveVKV Int where
    fromValveVKV :: ValveKeyValueEntry -> ValveKeyValueEntry -> Maybe Int
fromValveVKV (KVInt (Pair String
_ Int
num)) ValveKeyValueEntry
_ = Int -> Maybe Int
forall a. a -> Maybe a
Just Int
num
    fromValveVKV ValveKeyValueEntry
_ ValveKeyValueEntry
_ = Maybe Int
forall a. Maybe a
Nothing

instance ValveVKV a => ValveVKV (Maybe a) where
    fromValveVKV :: ValveKeyValueEntry -> ValveKeyValueEntry -> Maybe (Maybe a)
fromValveVKV ValveKeyValueEntry
entry ValveKeyValueEntry
con = Maybe a -> Maybe (Maybe a)
forall a. a -> Maybe a
Just (ValveKeyValueEntry -> ValveKeyValueEntry -> Maybe a
forall a.
ValveVKV a =>
ValveKeyValueEntry -> ValveKeyValueEntry -> Maybe a
fromValveVKV ValveKeyValueEntry
entry ValveKeyValueEntry
con)

instance ValveVKV Bool where
    fromValveVKV :: ValveKeyValueEntry -> ValveKeyValueEntry -> Maybe Bool
fromValveVKV (KVInt (Pair String
_ Int
0)) ValveKeyValueEntry
_ = Bool -> Maybe Bool
forall a. a -> Maybe a
Just Bool
False
    fromValveVKV (KVInt (Pair String
_ Int
1)) ValveKeyValueEntry
_ = Bool -> Maybe Bool
forall a. a -> Maybe a
Just Bool
True
    fromValveVKV ValveKeyValueEntry
_ ValveKeyValueEntry
_ = Maybe Bool
forall a. Maybe a
Nothing

instance ValveVKV a => ValveVKV [a] where
    fromValveVKV :: ValveKeyValueEntry -> ValveKeyValueEntry -> Maybe [a]
fromValveVKV (KVString (Pair String
name String
_)) ValveKeyValueEntry
context =
        let results :: [a]
results = (ValveKeyValueEntry -> Maybe a) -> [ValveKeyValueEntry] -> [a]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (ValveKeyValueEntry -> ValveKeyValueEntry -> Maybe a
forall a.
ValveVKV a =>
ValveKeyValueEntry -> ValveKeyValueEntry -> Maybe a
`fromValveVKV` ValveKeyValueEntry
context) (ValveKeyValueEntry -> String -> [ValveKeyValueEntry]
findFromName ValveKeyValueEntry
context String
name) in
        case [a]
results of
            [] -> Maybe [a]
forall a. Maybe a
Nothing
            [a]
_ -> [a] -> Maybe [a]
forall a. a -> Maybe a
Just [a]
results
    fromValveVKV (KVObject (Pair String
name [ValveKeyValueEntry]
_)) ValveKeyValueEntry
context =
        let results :: [a]
results = (ValveKeyValueEntry -> Maybe a) -> [ValveKeyValueEntry] -> [a]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (ValveKeyValueEntry -> ValveKeyValueEntry -> Maybe a
forall a.
ValveVKV a =>
ValveKeyValueEntry -> ValveKeyValueEntry -> Maybe a
`fromValveVKV` ValveKeyValueEntry
context) (ValveKeyValueEntry -> String -> [ValveKeyValueEntry]
findFromName ValveKeyValueEntry
context String
name) in
        case [a]
results of
            [] -> Maybe [a]
forall a. Maybe a
Nothing
            [a]
_ -> [a] -> Maybe [a]
forall a. a -> Maybe a
Just [a]
results
    fromValveVKV (KVInt (Pair String
name Int
_)) ValveKeyValueEntry
context =
        let results :: [a]
results = (ValveKeyValueEntry -> Maybe a) -> [ValveKeyValueEntry] -> [a]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (ValveKeyValueEntry -> ValveKeyValueEntry -> Maybe a
forall a.
ValveVKV a =>
ValveKeyValueEntry -> ValveKeyValueEntry -> Maybe a
`fromValveVKV` ValveKeyValueEntry
context) (ValveKeyValueEntry -> String -> [ValveKeyValueEntry]
findFromName ValveKeyValueEntry
context String
name) in
        case [a]
results of
            [] -> Maybe [a]
forall a. Maybe a
Nothing
            [a]
_ -> [a] -> Maybe [a]
forall a. a -> Maybe a
Just [a]
results

instance ValveVKV a => ValveVKV (NonEmpty a) where
    fromValveVKV :: ValveKeyValueEntry -> ValveKeyValueEntry -> Maybe (NonEmpty a)
fromValveVKV ValveKeyValueEntry
entry ValveKeyValueEntry
context =
        Maybe [a]
forall a. ValveVKV a => Maybe [a]
list Maybe [a] -> ([a] -> Maybe (NonEmpty a)) -> Maybe (NonEmpty a)
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= [a] -> Maybe (NonEmpty a)
forall a. [a] -> Maybe (NonEmpty a)
nonEmpty
        where
            list :: ValveVKV a => Maybe [a]
            list :: Maybe [a]
list = ValveKeyValueEntry -> ValveKeyValueEntry -> Maybe [a]
forall a.
ValveVKV a =>
ValveKeyValueEntry -> ValveKeyValueEntry -> Maybe a
fromValveVKV ValveKeyValueEntry
entry ValveKeyValueEntry
context