{-# LANGUAGE PostfixOperators #-}

module Operations.Finder where


import Parser (runParser)
import ParserCombinators (IsMatch(..), (|*), (<|>))
import Parsers.String (withinSquareBrackets)
import Parsers.Number (unsignedInt)
import Parsers.Char (dot)
import SyntaxTrees.Json(JsExpression(..))
import SyntaxTrees.Yaml (YamlExpression(..))
import SyntaxTrees.Toml (TomlExpression(..))


import qualified Data.Map as Map
import Data.Maybe (listToMaybe)
import Data.Either (fromRight)



class Finder a where

  toList :: a -> [(String, a)]
  findAll :: ((String, a) -> Bool) -> a -> [a]
  find :: ((String, a) -> Bool) -> a -> Maybe a
  findByKeys :: [String] -> a -> Maybe a
  findByPath :: String -> a -> Maybe a


  findAll (String, a) -> Bool
f = ((String, a) -> a) -> [(String, a)] -> [a]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (String, a) -> a
forall a b. (a, b) -> b
snd ([(String, a)] -> [a]) -> (a -> [(String, a)]) -> a -> [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((String, a) -> Bool) -> [(String, a)] -> [(String, a)]
forall a. (a -> Bool) -> [a] -> [a]
filter (String, a) -> Bool
f ([(String, a)] -> [(String, a)])
-> (a -> [(String, a)]) -> a -> [(String, a)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> [(String, a)]
forall a. Finder a => a -> [(String, a)]
toList
  find (String, a) -> Bool
f = [a] -> Maybe a
forall a. [a] -> Maybe a
listToMaybe ([a] -> Maybe a) -> (a -> [a]) -> a -> Maybe a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((String, a) -> Bool) -> a -> [a]
forall a. Finder a => ((String, a) -> Bool) -> a -> [a]
findAll (String, a) -> Bool
f

  findByKeys []       a
expr = a -> Maybe a
forall a. a -> Maybe a
Just a
expr
  findByKeys (String
x : [String]
xs) a
expr = String -> a -> Maybe a
forall a. Finder a => String -> a -> Maybe a
findByKey String
x a
expr Maybe a -> (a -> Maybe a) -> Maybe a
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= [String] -> a -> Maybe a
forall a. Finder a => [String] -> a -> Maybe a
findByKeys [String]
xs  where

    findByKey :: String -> a -> Maybe a
findByKey String
key = ((String, a) -> Bool) -> a -> Maybe a
forall a. Finder a => ((String, a) -> Bool) -> a -> Maybe a
find (\(String
str, a
_) -> String
str String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
key)

  findByPath String
path = [String] -> a -> Maybe a
forall a. Finder a => [String] -> a -> Maybe a
findByKeys [String]
pathSeq where

    pathSeq :: [String]
pathSeq = [String] -> Either ParseError [String] -> [String]
forall b a. b -> Either a b -> b
fromRight [] (Either ParseError [String] -> [String])
-> Either ParseError [String] -> [String]
forall a b. (a -> b) -> a -> b
$ Parser [String] -> String -> Either ParseError [String]
forall a. Parser a -> String -> Either ParseError a
runParser Parser [String]
parsePath String
path
    parsePath :: Parser [String]
parsePath = Char -> Parser Char
forall a. IsMatch a => a -> Parser a
is Char
'$' Parser Char -> Parser [String] -> Parser [String]
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> (Parser String
index Parser String -> Parser String -> Parser String
forall a. Parser a -> Parser a -> Parser a
<|> Parser String
key Parser String -> Parser [String]
forall a. Parser a -> Parser [a]
|*)

    index :: Parser String
index = Integer -> String
forall a. Show a => a -> String
show (Integer -> String) -> Parser Integer -> Parser String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser Integer -> Parser Integer
forall b. Parser b -> Parser b
withinSquareBrackets Parser Integer
unsignedInt
    key :: Parser String
key   = Parser Char
dot Parser Char -> Parser String -> Parser String
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> Parser String
word
    word :: Parser String
word  = (String -> Parser Char
forall a. IsMatch a => [a] -> Parser a
noneOf [Char
'.', Char
'['] Parser Char -> Parser String
forall a. Parser a -> Parser [a]
|*)



instance Finder JsExpression where
  toList :: JsExpression -> [(String, JsExpression)]
toList JsExpression
expr = case JsExpression
expr of
    null :: JsExpression
null @ JsExpression
JsNull      -> [(String
"", JsExpression
null)]
    n :: JsExpression
n @ (JsNumber Double
_)   -> [(String
"", JsExpression
n)]
    bool :: JsExpression
bool @ (JsBool Bool
_)  -> [(String
"", JsExpression
bool)]
    str :: JsExpression
str @ (JsString String
_) -> [(String
"", JsExpression
str)]
    JsArray [JsExpression]
arr        -> [String] -> [JsExpression] -> [(String, JsExpression)]
forall a b. [a] -> [b] -> [(a, b)]
zip (Int -> String
forall a. Show a => a -> String
show (Int -> String) -> [Int] -> [String]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Int
0 .. [JsExpression] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [JsExpression]
arr Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1]) [JsExpression]
arr
    JsObject Map String JsExpression
obj       -> Map String JsExpression -> [(String, JsExpression)]
forall k a. Map k a -> [(k, a)]
Map.toList Map String JsExpression
obj


instance Finder YamlExpression where
  toList :: YamlExpression -> [(String, YamlExpression)]
toList YamlExpression
expr = case YamlExpression
expr of
    null :: YamlExpression
null @ YamlExpression
YamlNull             -> [(String
"", YamlExpression
null)]
    n :: YamlExpression
n @ (YamlInteger Integer
_)         -> [(String
"", YamlExpression
n)]
    n :: YamlExpression
n @ (YamlFloat Double
_)           -> [(String
"", YamlExpression
n)]
    bool :: YamlExpression
bool @ (YamlBool Bool
_)         -> [(String
"", YamlExpression
bool)]
    str :: YamlExpression
str @ (YamlString String
_)        -> [(String
"", YamlExpression
str)]
    date :: YamlExpression
date @ (YamlDate Day
_)         -> [(String
"", YamlExpression
date)]
    time :: YamlExpression
time @ (YamlTime TimeOfDay
_)         -> [(String
"", YamlExpression
time)]
    dateTime :: YamlExpression
dateTime @ (YamlDateTime ZonedTime
_) -> [(String
"", YamlExpression
dateTime)]
    YamlList CollectionType
_ [YamlExpression]
arr              -> [String] -> [YamlExpression] -> [(String, YamlExpression)]
forall a b. [a] -> [b] -> [(a, b)]
zip (Int -> String
forall a. Show a => a -> String
show (Int -> String) -> [Int] -> [String]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Int
0 .. [YamlExpression] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [YamlExpression]
arr Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1]) [YamlExpression]
arr
    YamlMap CollectionType
_ Map String YamlExpression
obj               -> Map String YamlExpression -> [(String, YamlExpression)]
forall k a. Map k a -> [(k, a)]
Map.toList Map String YamlExpression
obj


instance Finder TomlExpression where
  toList :: TomlExpression -> [(String, TomlExpression)]
toList TomlExpression
expr = case TomlExpression
expr of
    null :: TomlExpression
null @ TomlExpression
TomlNull             -> [(String
"", TomlExpression
null)]
    n :: TomlExpression
n @ (TomlInteger Integer
_)         -> [(String
"", TomlExpression
n)]
    n :: TomlExpression
n @ (TomlFloat Double
_)           -> [(String
"", TomlExpression
n)]
    bool :: TomlExpression
bool @ (TomlBool Bool
_)         -> [(String
"", TomlExpression
bool)]
    str :: TomlExpression
str @ (TomlString String
_)        -> [(String
"", TomlExpression
str)]
    date :: TomlExpression
date @ (TomlDate Day
_)         -> [(String
"", TomlExpression
date)]
    time :: TomlExpression
time @ (TomlTime TimeOfDay
_)         -> [(String
"", TomlExpression
time)]
    dateTime :: TomlExpression
dateTime @ (TomlDateTime ZonedTime
_) -> [(String
"", TomlExpression
dateTime)]
    TomlArray [TomlExpression]
arr               -> [String] -> [TomlExpression] -> [(String, TomlExpression)]
forall a b. [a] -> [b] -> [(a, b)]
zip (Int -> String
forall a. Show a => a -> String
show (Int -> String) -> [Int] -> [String]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Int
0 .. [TomlExpression] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [TomlExpression]
arr Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1]) [TomlExpression]
arr
    TomlTable TableType
_ Map String TomlExpression
obj             -> Map String TomlExpression -> [(String, TomlExpression)]
forall k a. Map k a -> [(k, a)]
Map.toList Map String TomlExpression
obj