-- |Type for individual market information returned from API.
module Cryptsy.API.Public.Types.Market
	( module Cryptsy.API.Public.Types.Market
	, module Cryptsy.API.Public.Types.Num
	, module Cryptsy.API.Public.Types.Order
	, module Cryptsy.API.Public.Types.Time
	, module Cryptsy.API.Public.Types.Trade
	)
where

-- aeson
import Data.Aeson (FromJSON(..), Value, withObject, (.:))
import Data.Aeson.Types (Parser)

-- base
import Control.Applicative ((<$>), (<*>))
import Data.Traversable (mapM)
import Prelude hiding (mapM)

-- text
import Data.Text (Text, pack)

-- vector
import Data.Vector (Vector)

-- this package
import Cryptsy.API.Public.Types.Internal
import Cryptsy.API.Public.Types.Num
import Cryptsy.API.Public.Types.Order
import Cryptsy.API.Public.Types.Time
import Cryptsy.API.Public.Types.Trade

-- |generalized market
data GMarket p q dt t = Market
	{ mktMarketid :: Text
	, mktLabel :: Text
	, mktLastTradePrice :: p
	, mktVolume :: q
	, mktLastTradeTime :: dt
	, mktPrimaryName :: Text
	, mktPrimaryCode :: Text
	, mktSecondaryName :: Text
	, mktSecondaryCode :: Text
	, mktRecentTrades :: Vector (GTrade dt p q t)
	, mktSellOrders :: Vector (GOrder p q t)
	, mktBuyOrders :: Vector (GOrder p q t)
	} deriving Show

-- |default market
type Market = GMarket CryptsyNum CryptsyNum CryptsyTime CryptsyNum

instance (FromJSON p, FromJSON q, FromJSON dt, FromJSON t) =>
	FromJSON (GMarket p q dt t)
 where
	parseJSON = parseMarket parseJSON parseJSON parseJSON parseJSON

-- |Combine component parsers into JSON Object parser.
parseMarket :: (Value -> Parser p)  -- ^ price parser
            -> (Value -> Parser q)  -- ^ quantity parser
            -> (Value -> Parser dt) -- ^ date/time parser
            -> (Value -> Parser t)  -- ^ total parser
            -> Value -> Parser (GMarket p q dt t)
parseMarket parsePrice parseQuantity parseDatetime parseTotal =
	withObject "Market" $ \o -> Market                 <$>
	 o .: pack "marketid"                              <*>
	 o .: pack "label"                                 <*>
	(o .: pack "lasttradeprice" >>= parsePrice       ) <*>
	(o .: pack "volume"         >>= parseQuantity    ) <*>
	(o .: pack "lasttradetime"  >>= parseDatetime    ) <*>
	 o .: pack "primaryname"                           <*>
	 o .: pack "primarycode"                           <*>
	 o .: pack "secondaryname"                         <*>
	 o .: pack "secondarycode"                         <*>
	(o .: pack "recenttrades"   >>= parseRecentTrades) <*>
	(o .: pack "sellorders"     >>= parseSellOrders  ) <*>
	(o .: pack "buyorders"      >>= parseBuyOrders   )
 where
	parseRecentTrades =
		withNullableArray "recenttrades" . mapM $
		parseTrade parseDatetime parsePrice parseQuantity parseTotal
	parseOrders = mapM $ parseOrder parsePrice parseQuantity parseTotal
	parseSellOrders = withNullableArray "sellorders" parseOrders
	parseBuyOrders  = withNullableArray "buyorders"  parseOrders
{-# INLINABLE parseMarket #-}