{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveGeneric #-}
module Codec.Xlsx.Types.Internal.CustomProperties where

import Data.Map (Map)
import qualified Data.Map as M
import Data.Text (Text)
import GHC.Generics (Generic)
import Text.XML
import Text.XML.Cursor

import Codec.Xlsx.Parser.Internal
import Codec.Xlsx.Types.Variant
import Codec.Xlsx.Writer.Internal

newtype CustomProperties = CustomProperties (Map Text Variant)
    deriving (Eq, Show, Generic)

fromList :: [(Text, Variant)] -> CustomProperties
fromList = CustomProperties . M.fromList

empty :: CustomProperties
empty = CustomProperties M.empty

{-------------------------------------------------------------------------------
  Parsing
-------------------------------------------------------------------------------}

instance FromCursor CustomProperties where
  fromCursor cur = do
    let items = cur $/ element (cpr"property") >=> parseCustomPropertyEntry
    return (fromList items)

parseCustomPropertyEntry :: Cursor -> [(Text, Variant)]
parseCustomPropertyEntry cur = do
  name <- attribute "name" cur
  value <- cur $/ anyElement >=> fromCursor
  return (name, value)

-- | Add custom properties namespace to name
cpr :: Text -> Name
cpr x = Name
  { nameLocalName = x
  , nameNamespace = Just custPropNs
  , namePrefix = Nothing
  }

custPropNs :: Text
custPropNs = "http://schemas.openxmlformats.org/officeDocument/2006/custom-properties"

{-------------------------------------------------------------------------------
  Rendering
-------------------------------------------------------------------------------}

instance ToDocument CustomProperties where
    toDocument =
        documentFromNsElement "Custom properties generated by xlsx"
        "http://schemas.openxmlformats.org/officeDocument/2006/custom-properties"
        . toElement "Properties"

instance ToElement CustomProperties where
    toElement nm (CustomProperties m) = Element
        { elementName       = nm
        , elementAttributes = M.empty
        , elementNodes      = map (NodeElement . toElement "property" . CustomProperty)
                              . zip [2..] $ M.toList m
        }

newtype CustomProperty = CustomProperty (Int, (Text, Variant))

instance ToElement CustomProperty where
    toElement nm (CustomProperty (i, (key, val))) = Element
        { elementName       = nm
        , elementAttributes = M.fromList [ "name"  .= key
                                         , "fmtid" .= userDefinedFmtID
                                         , "pid"   .= txti i ]
        , elementNodes      = [ NodeElement $ variantToElement val ]
        }

-- | FMTID_UserDefinedProperties
userDefinedFmtID :: Text
userDefinedFmtID = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"
