module Extism.JSON (
  module Extism.JSON,
  module Text.JSON
) where

import Text.JSON
import qualified Data.ByteString as B
import Data.ByteString.Internal (c2w, w2c)
import qualified Data.ByteString.Base64 as B64
import qualified Data.ByteString.Char8 as BS (unpack)

data Nullable a = Null | NotNull a

makeArray x = JSArray [showJSON a | a <- x]
isNull JSNull = True
isNull _ = False
filterNulls obj = [(a, b) | (a, b) <- obj, not (isNull b)]
object x = makeObj $ filterNulls x
objectWithNulls = makeObj
nonNull = NotNull
null' = Null
(.=) a b = (a, showJSON b)
toNullable (Just x) = NotNull x
toNullable Nothing = Null
fromNullable (NotNull x) = Just x
fromNullable Null = Nothing
fromNotNull (NotNull x) = x
fromNotNull Null = error "Value is Null"
mapNullable f Null = Null
mapNullable f (NotNull x) = NotNull (f x)

(.?) (JSObject a) k =
  case valFromObj k a of
    Ok x -> NotNull x
    Error _ -> Null
(.?) _ _ = Null
(.??) a k = toNullable $ lookup k a

find :: JSON a => String -> JSValue -> Nullable a
find k obj = obj .? k

update :: JSON a => String -> a -> JSValue -> JSValue
update k v (JSObject obj) = object $ fromJSObject obj ++ [k .= v]

instance JSON a => JSON (Nullable a) where
  showJSON (NotNull x) = showJSON x
  showJSON Null = JSNull
  readJSON JSNull = Ok Null
  readJSON x = readJSON x


newtype Base64 = Base64 B.ByteString

instance JSON Base64 where
  showJSON (Base64 bs) = showJSON (BS.unpack $ B64.encode bs)
  readJSON (JSString s) =
    let toByteString x = B.pack (Prelude.map c2w x) in
    case B64.decode (toByteString (fromJSString s)) of
    Left msg -> Error msg
    Right d -> Ok (Base64 d)
