module FP.API.Convert
  (encodeFpco
  ,decodeFpco
  ,validFileName
  ,validateFilePath)
  where
import           Control.Monad
import           Data.Aeson
import           Data.Aeson.Types (parseEither)
import           Data.ByteString        (ByteString)
import qualified Data.ByteString.Char8  as BS
import           Data.Data
import           Data.Generics.Aliases
import qualified Data.Text              as Text
import           Fay.Convert (encodeFay, decodeFay)
import qualified FP.API                 as API
import           FP.API.ModuleName
import           Prelude
encodeFpco :: GenericQ Value
encodeFpco = encodeFay $ \f -> (f
    `extQ` (int :: Integer -> Value))
  where
    int = Number . fromIntegral
decodeFpco :: Data a => Value -> Either String a
decodeFpco = decodeFay $ \value r -> r
   `extR` parseInteger value
   `extR` (validateEncFileName =<< normalDecode value)
   `extR` (either (Left . Text.unpack) Right . checkModuleName =<< normalDecode value)
normalDecode :: Data a => Value -> Either String a
normalDecode = decodeFay (\_ r' -> r')
validateEncFileName :: API.EncFileName -> Either String API.EncFileName
validateEncFileName
    = fmap API.encFileNameFromByteString
    . validateFilePath
    . API.unFileName
    . API.unEncFileName
validateFilePath :: ByteString -> Either String ByteString
validateFilePath path =
    case filter (not . BS.null) $ BS.splitWith (`elem` ['\\', '/']) $ BS.filter (/= '\NUL') path of
        [] -> Left "Invalid filepath: Empty"
        components -> Right $ BS.intercalate "/" components
validFileName :: ByteString -> Either String API.FileName
validFileName = fmap API.FileName . validateFilePath
parseInteger :: Value -> Either String Integer
parseInteger = parseEither parseJSON