{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
--
-- Licensed to the Apache Software Foundation (ASF) under one
-- or more contributor license agreements. See the NOTICE file
-- distributed with this work for additional information
-- regarding copyright ownership. The ASF licenses this file
-- to you under the Apache License, Version 2.0 (the
-- "License"); you may not use this file except in compliance
-- with the License. You may obtain a copy of the License at
--
--   http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing,
-- software distributed under the License is distributed on an
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-- KIND, either express or implied. See the License for the
-- specific language governing permissions and limitations
-- under the License.
--

module Thrift
    ( module Thrift.Transport
    , module Thrift.Protocol
    , AppExnType(..)
    , AppExn(..)
    , readAppExn
    , writeAppExn
    , ThriftException(..)
    ) where

import Control.Exception

import Data.Int
import Data.Text.Lazy ( Text, pack, unpack )
import Data.Text.Lazy.Encoding
import Data.Typeable ( Typeable )
import qualified Data.HashMap.Strict as Map

import Thrift.Protocol
import Thrift.Transport
import Thrift.Types

data ThriftException = ThriftException
  deriving ( Show, Typeable )
instance Exception ThriftException

data AppExnType
    = AE_UNKNOWN
    | AE_UNKNOWN_METHOD
    | AE_INVALID_MESSAGE_TYPE
    | AE_WRONG_METHOD_NAME
    | AE_BAD_SEQUENCE_ID
    | AE_MISSING_RESULT
    | AE_INTERNAL_ERROR
    | AE_PROTOCOL_ERROR
    | AE_INVALID_TRANSFORM
    | AE_INVALID_PROTOCOL
    | AE_UNSUPPORTED_CLIENT_TYPE
      deriving ( Eq, Show, Typeable )

instance Enum AppExnType where
    toEnum 0 = AE_UNKNOWN
    toEnum 1 = AE_UNKNOWN_METHOD
    toEnum 2 = AE_INVALID_MESSAGE_TYPE
    toEnum 3 = AE_WRONG_METHOD_NAME
    toEnum 4 = AE_BAD_SEQUENCE_ID
    toEnum 5 = AE_MISSING_RESULT
    toEnum 6 = AE_INTERNAL_ERROR
    toEnum 7 = AE_PROTOCOL_ERROR
    toEnum 8 = AE_INVALID_TRANSFORM
    toEnum 9 = AE_INVALID_PROTOCOL
    toEnum 10 = AE_UNSUPPORTED_CLIENT_TYPE
    toEnum t = error $ "Invalid AppExnType " ++ show t

    fromEnum AE_UNKNOWN = 0
    fromEnum AE_UNKNOWN_METHOD = 1
    fromEnum AE_INVALID_MESSAGE_TYPE = 2
    fromEnum AE_WRONG_METHOD_NAME = 3
    fromEnum AE_BAD_SEQUENCE_ID = 4
    fromEnum AE_MISSING_RESULT = 5
    fromEnum AE_INTERNAL_ERROR = 6
    fromEnum AE_PROTOCOL_ERROR = 7
    fromEnum AE_INVALID_TRANSFORM = 8
    fromEnum AE_INVALID_PROTOCOL = 9
    fromEnum AE_UNSUPPORTED_CLIENT_TYPE = 10

data AppExn = AppExn { ae_type :: AppExnType, ae_message :: String }
  deriving ( Show, Typeable )
instance Exception AppExn

writeAppExn :: (Protocol p, Transport t) => p t -> AppExn -> IO ()
writeAppExn pt ae = writeVal pt $ TStruct $ Map.fromList
                    [ (1, ("message", TString $ encodeUtf8 $ pack $ ae_message ae))
                    , (2, ("type", TI32 $ fromIntegral $ fromEnum (ae_type ae)))
                    ]

readAppExn :: (Protocol p, Transport t) => p t -> IO AppExn
readAppExn pt = do
    let typemap = Map.fromList [("message",(1,T_STRING)),("type",(2,T_I32))]
    TStruct fields <- readVal pt $ T_STRUCT typemap
    return $ readAppExnFields fields

readAppExnFields :: Map.HashMap Int16 (Text, ThriftVal) -> AppExn
readAppExnFields fields = AppExn{
  ae_message = maybe undefined unwrapMessage $ Map.lookup 1 fields,
  ae_type    = maybe undefined unwrapType $ Map.lookup 2 fields
  }
  where
    unwrapMessage (_, TString s) = unpack $ decodeUtf8 s
    unwrapMessage _ = undefined
    unwrapType (_, TI32 i) = toEnum $ fromIntegral i
    unwrapType _ = undefined
