{-# LANGUAGE OverloadedLists #-}

module Data.JsonSchema.Utils where

import           Data.Aeson
import           Data.HashMap.Strict  (HashMap)
import           Data.JsonSchema.Core
import           Data.List
import           Data.Monoid
import           Data.Scientific
import           Data.Text            (Text)
import qualified Data.Text            as T
import           Data.Vector          (Vector)
import qualified Data.Vector          as V

isJsonType :: Value -> Vector Text -> Vector ValErr
isJsonType x xs =
  case x of
    (Null)     -> f "null"    xs ("null" :: Text)
    (Array y)  -> f "array"   xs y
    (Bool y)   -> f "boolean" xs y
    (Object y) -> f "object"  xs y
    (String y) -> f "string"  xs y
    (Number y) ->
      case toBoundedInteger y :: Maybe Int of
        Nothing -> f "number" xs y
        Just _  -> if V.elem "number" xs || V.elem "integer" xs
                     then mempty
                     else mkErr y xs
  where
    f :: (Show a) => Text -> Vector Text -> a -> Vector ValErr
    f t ts d = if V.elem t ts then mempty else mkErr d ts

    mkErr :: (Show a) => a -> Vector Text -> Vector ValErr
    mkErr y ts = V.singleton $ tshow y <> " is not one of the types " <> tshow ts

runMaybeVal :: Maybe Validator -> Value -> Vector ValErr
runMaybeVal Nothing _ = mempty
runMaybeVal (Just val) d = val d

runMaybeVal'
  :: Maybe (Value -> (Vector ValErr, Value))
  -> Value
  -> (Vector ValErr, Value)
runMaybeVal' Nothing d = (mempty, d)
runMaybeVal' (Just val) d = val d

-- TODO: optimize
-- see here: http://comments.gmane.org/gmane.comp.lang.haskell.cafe/106242
allUnique :: (Eq a) => Vector a -> Bool
allUnique bs = length (nub (V.toList bs)) == V.length bs

-- TODO: optimize
count :: (Eq a) => a -> Vector a -> Int
count b bs = V.length $ V.filter (== b) bs

toObj :: Value -> Maybe (HashMap Text Value)
toObj (Object a) = Just a
toObj _ = Nothing

fromJSONInt :: Value -> Maybe Int
fromJSONInt (Number n) = toBoundedInteger n
fromJSONInt _ = Nothing

toTxt :: Value -> Maybe Text
toTxt (String t) = Just t
toTxt _ = Nothing

greaterThanZero :: (Num a, Ord a) => a -> Maybe ()
greaterThanZero n = if n <= 0 then Nothing else Just ()

tshow :: Show a => a -> Text
tshow = T.pack . show