{-# LANGUAGE LambdaCase           #-}
{-# LANGUAGE OverloadedStrings    #-}
{-# LANGUAGE ScopedTypeVariables  #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
{- |
   Module      : Text.Pandoc.Lua.Marshaling.ReaderOptions
   Copyright   : © 2012-2024 John MacFarlane
                 © 2017-2024 Albert Krewinkel
   License     : GNU GPL, version 2 or above

   Maintainer  : Albert Krewinkel <albert+pandoc@tarleb.com>
   Stability   : alpha

Marshal citeproc 'Reference' values.
-}
module Text.Pandoc.Lua.Marshal.Reference
  ( pushReference
  ) where

import Citeproc.Types
  ( Date (..), DateParts (..), ItemId (..), Name (..), Reference (..)
  , Val (..), Variable, fromVariable
  )
import Control.Monad (forM_)
import HsLua hiding (Name, Reference, pushName, peekName)
import Text.Pandoc.Builder (Inlines, toList)
import Text.Pandoc.Lua.Marshal.Inline (pushInlines)
import Text.Pandoc.Lua.Marshal.List (pushPandocList)

import qualified Data.Map as Map

-- | Pushes a ReaderOptions value as userdata object.
pushReference :: LuaError e => Pusher e (Reference Inlines)
pushReference reference = do
  pushAsTable [ ("id", pushItemId . referenceId)
              , ("type", pushText . referenceType)
              ]
              reference
  forM_ (Map.toList $ referenceVariables reference) $ \(var, val) -> do
    pushVariable var
    pushVal val
    rawset (nth 3)

-- | Pushes an 'ItemId' as a string.
pushItemId :: Pusher e ItemId
pushItemId = pushText . unItemId

-- | Pushes a person's 'Name' as a table.
pushName :: LuaError e => Pusher e Name
pushName = pushAsTable
  [ ("family"                , pushTextOrNil . nameFamily)
  , ("given"                 , pushTextOrNil . nameGiven)
  , ("dropping-particle"     , pushTextOrNil . nameDroppingParticle)
  , ("non-dropping-particle" , pushTextOrNil . nameNonDroppingParticle)
  , ("suffix"                , pushTextOrNil . nameSuffix)
  , ("literal"               , pushTextOrNil . nameLiteral)
  , ("comma-suffix"          , pushBoolOrNil . nameCommaSuffix)
  , ("static-ordering"       , pushBoolOrNil . nameStaticOrdering)
  ]
  where
    pushTextOrNil = \case
      Nothing -> pushnil
      Just xs -> pushText xs

-- | Pushes a boolean, but uses @nil@ instead of @false@; table fields
-- are not set unless the value is true.
pushBoolOrNil :: Pusher e Bool
pushBoolOrNil = \case
  False -> pushnil
  True  -> pushBool True

-- | Pushes a 'Variable' as string.
pushVariable :: Pusher e Variable
pushVariable = pushText . fromVariable

-- | Pushes a 'Val', i.e., a variable value.
pushVal :: LuaError e => Pusher e (Val Inlines)
pushVal = \case
  TextVal t -> pushText t
  FancyVal inlns -> pushInlines $ toList inlns
  NumVal i       -> pushIntegral i
  NamesVal names -> pushPandocList pushName names
  DateVal date   -> pushDate date
  _              -> pushText mempty

-- | Pushes a 'Date' as table.
pushDate :: LuaError e => Pusher e Date
pushDate = pushAsTable
  [ ("date-parts", pushPandocList pushDateParts . dateParts)
  , ("circa", pushBoolOrNil . dateCirca)
  , ("season", maybe pushnil pushIntegral . dateSeason)
  , ("literal", maybe pushnil pushText . dateLiteral)
  ]
 where
   -- date parts are lists of Int values
   pushDateParts (DateParts dp) = pushPandocList pushIntegral dp
