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 subentry named the string inside the entry you gave in on the left.

(.:) :: ValveVKV a => ValveKeyValueEntry -> String -> Either String a 
ValveKeyValueEntry
context .: :: ValveKeyValueEntry -> String -> Either String a
.: String
name =
    let results :: [ValveKeyValueEntry]
results = ValveKeyValueEntry -> String -> [ValveKeyValueEntry]
findFromName ValveKeyValueEntry
context String
name in
    case [ValveKeyValueEntry]
results of
        [] -> String -> Either String a
forall a b. a -> Either a b
Left (String -> Either String a) -> String -> Either String a
forall a b. (a -> b) -> a -> b
$ String
"No items with name " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
name
        ValveKeyValueEntry
x:[ValveKeyValueEntry]
_ -> ValveKeyValueEntry -> ValveKeyValueEntry -> Either String a
forall a.
ValveVKV a =>
ValveKeyValueEntry -> ValveKeyValueEntry -> Either String 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 string 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 -> Either String a

instance ValveVKV Int where
    fromValveVKV :: ValveKeyValueEntry -> ValveKeyValueEntry -> Either String Int
fromValveVKV (KVInt (Pair String
_ Int
num)) ValveKeyValueEntry
_ = Int -> Either String Int
forall a b. b -> Either a b
Right Int
num
    fromValveVKV (KVString (Pair String
name String
x)) ValveKeyValueEntry
_ = String -> Either String Int
forall a b. a -> Either a b
Left (String -> Either String Int) -> String -> Either String Int
forall a b. (a -> b) -> a -> b
$ String
"No int called " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
name String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" found. We did find a string with value " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
x
    fromValveVKV (KVObject (Pair String
name [ValveKeyValueEntry]
_)) ValveKeyValueEntry
_ = String -> Either String Int
forall a b. a -> Either a b
Left (String -> Either String Int) -> String -> Either String Int
forall a b. (a -> b) -> a -> b
$ String
"No int called " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
name String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" found. We did find an object named " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
name

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

instance ValveVKV Bool where
    fromValveVKV :: ValveKeyValueEntry -> ValveKeyValueEntry -> Either String Bool
fromValveVKV (KVInt (Pair String
_ Int
0)) ValveKeyValueEntry
_ = Bool -> Either String Bool
forall a b. b -> Either a b
Right Bool
False
    fromValveVKV (KVInt (Pair String
_ Int
1)) ValveKeyValueEntry
_ = Bool -> Either String Bool
forall a b. b -> Either a b
Right Bool
True
    fromValveVKV (KVInt (Pair String
_ Int
x)) ValveKeyValueEntry
_ = String -> Either String Bool
forall a b. a -> Either a b
Left (String -> Either String Bool) -> String -> Either String Bool
forall a b. (a -> b) -> a -> b
$ String
"Could not parse int " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show Int
x String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" as a boolean"
    fromValveVKV ValveKeyValueEntry
_ ValveKeyValueEntry
_ = String -> Either String Bool
forall a b. a -> Either a b
Left String
"Could not parse as a boolean"

instance ValveVKV a => ValveVKV [a] where
    fromValveVKV :: ValveKeyValueEntry -> ValveKeyValueEntry -> Either String [a]
fromValveVKV (KVString (Pair String
name String
_)) ValveKeyValueEntry
context =
        --Right $ map (`fromValveVKV` context) (findFromName context name)

        (ValveKeyValueEntry -> Either String a)
-> [ValveKeyValueEntry] -> Either String [a]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse (ValveKeyValueEntry -> ValveKeyValueEntry -> Either String a
forall a.
ValveVKV a =>
ValveKeyValueEntry -> ValveKeyValueEntry -> Either String a
`fromValveVKV` ValveKeyValueEntry
context) (ValveKeyValueEntry -> String -> [ValveKeyValueEntry]
findFromName ValveKeyValueEntry
context String
name)
    fromValveVKV (KVObject (Pair String
name [ValveKeyValueEntry]
_)) ValveKeyValueEntry
context =
        (ValveKeyValueEntry -> Either String a)
-> [ValveKeyValueEntry] -> Either String [a]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse (ValveKeyValueEntry -> ValveKeyValueEntry -> Either String a
forall a.
ValveVKV a =>
ValveKeyValueEntry -> ValveKeyValueEntry -> Either String a
`fromValveVKV` ValveKeyValueEntry
context) (ValveKeyValueEntry -> String -> [ValveKeyValueEntry]
findFromName ValveKeyValueEntry
context String
name)
    fromValveVKV (KVInt (Pair String
name Int
_)) ValveKeyValueEntry
context =
        (ValveKeyValueEntry -> Either String a)
-> [ValveKeyValueEntry] -> Either String [a]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse (ValveKeyValueEntry -> ValveKeyValueEntry -> Either String a
forall a.
ValveVKV a =>
ValveKeyValueEntry -> ValveKeyValueEntry -> Either String a
`fromValveVKV` ValveKeyValueEntry
context) (ValveKeyValueEntry -> String -> [ValveKeyValueEntry]
findFromName ValveKeyValueEntry
context String
name)

instance ValveVKV a => ValveVKV (NonEmpty a) where
    fromValveVKV :: ValveKeyValueEntry
-> ValveKeyValueEntry -> Either String (NonEmpty a)
fromValveVKV ValveKeyValueEntry
entry ValveKeyValueEntry
context =
        --list >>= nonEmpty

        Either String [a]
forall a. ValveVKV a => Either String [a]
list Either String [a]
-> ([a] -> Either String (NonEmpty a))
-> Either String (NonEmpty a)
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \[a]
xs ->
            case [a] -> Maybe (NonEmpty a)
forall a. [a] -> Maybe (NonEmpty a)
nonEmpty [a]
xs of
                Just NonEmpty a
x -> NonEmpty a -> Either String (NonEmpty a)
forall a b. b -> Either a b
Right NonEmpty a
x
                Maybe (NonEmpty a)
Nothing -> String -> Either String (NonEmpty a)
forall a b. a -> Either a b
Left String
"List was empty"
        where
            list :: ValveVKV a => Either String [a]
            list :: Either String [a]
list = ValveKeyValueEntry -> ValveKeyValueEntry -> Either String [a]
forall a.
ValveVKV a =>
ValveKeyValueEntry -> ValveKeyValueEntry -> Either String a
fromValveVKV ValveKeyValueEntry
entry ValveKeyValueEntry
context