-- |
-- Helper methods used to construct requests.
--

module Network.TableStorage.Request where

import Data.Time
import System.Locale
import Data.Maybe (fromMaybe)
import Data.List (intercalate)
import Text.XML.Light.Types (elAttribs)
import Text.XML.Light
import Network.TableStorage.Types
import Network.TableStorage.XML
import Network.TableStorage.Atom
import Network.TableStorage.Format
import Network.HTTP.Base
import Text.Printf (printf)

-- |
-- Formats a list of entity properties for inclusion in an Atom entry. 
--
propertyList :: [(String, EntityColumn)] -> Element
propertyList props = 
  blank_element { elName = qualifyMetadata "properties",
                  elContent = map property props } where
  property (key, value) =
    let stringValue = printEntityColumn value in
    Elem blank_element { elName = qualifyDataServices key,
                         elAttribs = [ Attr (qualifyMetadata "type") $ columnToTypeString value, 
                                       Attr (qualifyMetadata "null") $ maybe "true" (const "false") stringValue ],
                         elContent = cDataText $ fromMaybe "" stringValue }

-- |
-- Constructs relative URIs which refer to the entity with the specified table name
-- and entity key. 
--
entityKeyResource :: Account -> String -> EntityKey -> String
entityKeyResource acc tableName key = printf "/%s/%s(PartitionKey='%s',RowKey='%s')" 
  (accountName acc) 
  tableName 
  (ekPartitionKey key) 
  (ekRowKey key)

-- |
-- Converts an entity column into its type name
--
columnToTypeString :: EntityColumn -> String
columnToTypeString (EdmBinary _)        = "Edm.Binary"
columnToTypeString (EdmBoolean _)       = "Edm.Boolean"
columnToTypeString (EdmDateTime _)      = "Edm.DateTime"
columnToTypeString (EdmDouble _)        = "Edm.Double"
columnToTypeString (EdmGuid _)          = "Edm.EdmGuid"
columnToTypeString (EdmInt32 _)         = "Edm.Int32"
columnToTypeString (EdmInt64 _)         = "Edm.Int64"
columnToTypeString (EdmString _)        = "Edm.String"

-- |
-- Formats a column value to appear in the body of an Atom entry
--
printEntityColumn :: EntityColumn -> Maybe String
printEntityColumn (EdmBinary (Just val))       = Just val
printEntityColumn (EdmBoolean (Just True))     = Just "true"
printEntityColumn (EdmBoolean (Just False))    = Just "false"
printEntityColumn (EdmDateTime (Just val))     = Just $ formatTime defaultTimeLocale atomDateFormat val
printEntityColumn (EdmDouble (Just val))       = Just $ printf "%f" val
printEntityColumn (EdmGuid (Just val))         = Just val
printEntityColumn (EdmInt32 (Just val))        = Just $ printf "%d" val
printEntityColumn (EdmInt64 (Just val))        = Just $ printf "%d" val
printEntityColumn (EdmString (Just val))       = Just val
printEntityColumn _                            = Nothing

printComparisonType :: ComparisonType -> String
printComparisonType Equal               = "eq"
printComparisonType GreaterThan         = "gt"
printComparisonType GreaterThanOrEqual  = "ge" 
printComparisonType LessThan            = "lt"
printComparisonType LessThanOrEqual     = "le" 
printComparisonType NotEqual            = "ne"

-- |
-- Converts entity filter values into strings to appear in the filter
-- portion of the Query Entities URI. 
--
buildFilterString :: EntityFilter -> String
buildFilterString (And fs) = printf "(%s)" $ intercalate "%%20and%%20" $ map buildFilterString fs
buildFilterString (Or fs) = printf "(%s)" $ intercalate "%%20or%%20" $ map buildFilterString fs
buildFilterString (Not f) = "(not%%20" ++ buildFilterString f ++ ")"
buildFilterString (CompareBoolean prop val) = printf "%s%%20eq%%20%s" (urlEncode prop) (if val then "true" else "false")
buildFilterString (CompareDateTime prop cmp val) = printf "%s%%20%s%%20datetime'%s'" (urlEncode prop) (printComparisonType cmp) (formatTime defaultTimeLocale atomDateFormat val)
buildFilterString (CompareDouble prop cmp val) = printf "%s%%20%s%%20%f" (urlEncode prop) (printComparisonType cmp) val
buildFilterString (CompareGuid prop val) = printf "%s%%20eq%%20guid'%s'" (urlEncode prop) val
buildFilterString (CompareInt32 prop cmp val) = printf "%s%%20%s%%20%d" (urlEncode prop) (printComparisonType cmp) val
buildFilterString (CompareInt64 prop cmp val) = printf "%s%%20%s%%20%d" (urlEncode prop) (printComparisonType cmp) val
buildFilterString (CompareString prop cmp val) = printf "%s%%20%s%%20'%s'" (urlEncode prop) (printComparisonType cmp) (urlEncode val)

-- |
-- Constructs the full query string for the Query Entities web method. 
--
buildQueryString :: EntityQuery -> String
buildQueryString query = printf "$filter=%s&$top=%s"
  (maybe "" buildFilterString $ eqFilter query)
  (maybe "" show $ eqPageSize query)