{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FunctionalDependencies, FlexibleContexts #-}

-- | The base functions for accessing JSON object fields.
module Text.JSON.JSONField
(
  JSONField(..)
) where

import Data.ByteString
import qualified Data.Trie as T
import Text.JSONb
import qualified Text.JSON as J
import qualified Text.HJson as H
import qualified Data.Aeson.Types as A
import Text.JSON.Types
import Text.JSON.Failure
import Control.Failure
import qualified Data.Map as M
import Data.Text

-- | Accessing JSON object fields.
class JSONField j f | j -> f where
  -- | Access the field of a JSON object with potential failure.
  field ::
    Failure (NoSuchFieldOrExpectedObject f j) m =>
    f
    -> j
    -> m j
  -- | Access all fields of a JSON object with potential failure.
  fields ::
    Failure (ExpectedObject j) m =>
    j
    -> m [f]
  -- | Returns all object field values of a JSON value.
  values ::
    Failure (ExpectedObject j) m =>
    j
    -> m [j]

instance JSONField JSON ByteString where
  field f (Object o) =
    case T.lookup f o of
      Nothing -> failure (NoSuchFieldOrExpectedObject_NoSuchField f :: NoSuchFieldOrExpectedObject ByteString JSON)
      Just x  -> return x
  field _ j =
    failure (NoSuchFieldOrExpectedObject_ExpectedObject j :: NoSuchFieldOrExpectedObject ByteString JSON)
  fields (Object o) =
    return (T.keys o)
  fields j =
    failure (ExpectedObject j)
  values (Object o) =
    return . fmap snd . T.toList $ o
  values j =
    failure (ExpectedObject j)

instance JSONField J.JSValue [Char] where
  field f (J.JSObject (JSONObject o)) =
    case lookup f o of
      Nothing -> failure (NoSuchFieldOrExpectedObject_NoSuchField f :: NoSuchFieldOrExpectedObject String J.JSValue)
      Just x  -> return x
  field _ j =
    failure (NoSuchFieldOrExpectedObject_ExpectedObject j :: NoSuchFieldOrExpectedObject String J.JSValue)
  fields (J.JSObject (JSONObject o)) =
    return (fmap fst o)
  fields j =
    failure (ExpectedObject j)
  values (J.JSObject (JSONObject o)) =
    return (fmap snd o)
  values j =
    failure (ExpectedObject j)

instance JSONField H.Json [Char] where
  field f (H.JObject o) =
    case M.lookup f o of
      Nothing -> failure (NoSuchFieldOrExpectedObject_NoSuchField f :: NoSuchFieldOrExpectedObject String H.Json)
      Just x  -> return x
  field _ j =
    failure (NoSuchFieldOrExpectedObject_ExpectedObject j :: NoSuchFieldOrExpectedObject String H.Json)
  fields (H.JObject o) =
    return (M.keys o)
  fields j =
    failure (ExpectedObject j)
  values (H.JObject o) =
    return (fmap snd (M.toList o))
  values j =
    failure (ExpectedObject j)

instance JSONField A.Value Text where
  field f (A.Object o) =
    case M.lookup f o of
      Nothing -> failure (NoSuchFieldOrExpectedObject_NoSuchField f :: NoSuchFieldOrExpectedObject Text A.Value)
      Just x  -> return x
  field _ j =
    failure (NoSuchFieldOrExpectedObject_ExpectedObject j :: NoSuchFieldOrExpectedObject Text A.Value)
  fields (A.Object o) =
    return (M.keys o)
  fields j =
    failure (ExpectedObject j)
  values (A.Object o) =
    return (fmap snd (M.toList o))
  values j =
    failure (ExpectedObject j)