content: {-# LANGUAGE CPP #-}
  {-# LANGUAGE OverloadedStrings #-}
  {-# LANGUAGE ScopedTypeVariables #-}
  module Main where
  import Citeproc
  import Citeproc.CslJson
  import Control.Monad (when, unless, foldM)
  import Control.Applicative ((<|>))
  import Data.Bifunctor (second)
  import Data.Maybe (fromMaybe)
  import Data.Text (Text)
  import qualified Data.Text as T
  import qualified Data.Text.IO as TIO
  import qualified Data.ByteString.Lazy as BL
  import qualified Data.Aeson as Aeson
  import Data.Aeson.Encode.Pretty as AesonPretty
  import Data.Ord (comparing)
  import System.IO
  import System.Exit
  import System.Environment
  import System.Console.GetOpt
  
  main :: IO ()
  main = do
    rawargs <- getArgs
    let (opts, args, errs) = getOpt Permute options rawargs
    unless (null errs) $ do
      mapM_ err errs
      exitWith $ ExitFailure 1
    opt <- foldM (flip ($)) defaultOpt opts
    when (optHelp opt) $ do
      putStr $ usageInfo "citeproc [OPTIONS] [FILE]" options
      exitSuccess
    when (optVersion opt) $ do
      putStrLn $ "citeproc version " ++ VERSION_citeproc
      exitSuccess
    format <- case optFormat opt of
                Just "html" -> return Html
                Just "json" -> return Json
                Just _      -> err "--format must be html or json"
                Nothing     -> return Html
    bs <- case args of
            [] -> BL.getContents
            (f:_) -> BL.readFile f
    case Aeson.eitherDecode bs of
      Left e -> err e
      Right (inp :: Inputs (CslJson Text)) -> do
        stylesheet <- case optStyle opt of
                        Just fp -> T.dropWhile (=='\xFEFF') <$> -- drop BOM
                                         TIO.readFile fp
                        Nothing ->
                          case inputsStyle inp of
                            Just s  -> return s
                            Nothing -> err "No style specified"
        references <- case optReferences opt of
                        Just fp -> do
                          raw <- BL.readFile fp
                          case Aeson.eitherDecode raw of
                            Left e   -> err e
                            Right rs -> return rs
                        Nothing ->
                          case inputsReferences inp of
                            Just rs  -> return rs
                            Nothing  -> err "No references specified"
        abbreviations <- case optAbbreviations opt of
                            Just fp -> do
                              raw <- BL.readFile fp
                              case Aeson.eitherDecode raw of
                                Left e   -> err e
                                Right ab -> return $ Just ab
                            Nothing -> return $ inputsAbbreviations inp
        let lang = optLang opt <|> inputsLang inp
  
        parseResult <-
          parseStyle (\_ -> return mempty) stylesheet
        case parseResult of
          Left e -> err (T.unpack $ prettyCiteprocError e)
          Right parsedStyle -> do
            let style = parsedStyle{ styleAbbreviations = abbreviations }
            let result=
                  citeproc
                    defaultCiteprocOptions
                      { linkCitations = optLinkCitations opt
                      , linkBibliography = optLinkBibliography opt
                      }
                    style
                    lang
                    references
                    (fromMaybe [] (inputsCitations inp))
            let jsonResult :: Aeson.Value
                jsonResult =
                  case format of
                     Json -> Aeson.object
                            [ ("citations", Aeson.toJSON $
                                 map cslJsonToJson
                                     (resultCitations result))
                            , ("bibliography", Aeson.toJSON $
                                 map (second cslJsonToJson)
                                     (resultBibliography result))
                            , ("warnings", Aeson.toJSON $ resultWarnings result)
                            ]
                     Html -> Aeson.object
                            [ ("citations", Aeson.toJSON $
                                 map cslJsonToHtml
                                     (resultCitations result))
                            , ("bibliography", Aeson.toJSON $
                                 map (second cslJsonToHtml)
                                     (resultBibliography result))
                            , ("warnings", Aeson.toJSON $ resultWarnings result)
                            ]
                            where cslJsonToHtml el =
                                    renderCslJson True mempty el
            BL.putStr $ AesonPretty.encodePretty'
                         AesonPretty.defConfig
                           { confIndent = AesonPretty.Spaces 2
                           , confCompare = AesonPretty.keyOrder
                               ["citations","bibliography","warnings"]
                               `mappend` comparing T.length
                           , confTrailingNewline = True }
                         jsonResult
  
  data Format = Json | Html deriving (Show, Ord, Eq)
  
  data Opt =
    Opt{ optStyle            :: Maybe String
       , optReferences       :: Maybe String
       , optAbbreviations    :: Maybe String
       , optFormat           :: Maybe String
       , optLang             :: Maybe Lang
       , optLinkCitations    :: Bool
       , optLinkBibliography :: Bool
       , optHelp             :: Bool
       , optVersion          :: Bool
       } deriving Show
  
  defaultOpt :: Opt
  defaultOpt =
    Opt { optStyle = Nothing
        , optReferences = Nothing
        , optAbbreviations = Nothing
        , optFormat = Nothing
        , optLang = Nothing
        , optLinkCitations = False
        , optLinkBibliography = False
        , optHelp = False
        , optVersion = False
        }
  
  options :: [OptDescr (Opt -> IO Opt)]
  options =
    [ Option ['s'] ["style"]
       (ReqArg (\fp opt -> return opt{ optStyle = Just fp }) "FILE")
       "CSL style file"
    , Option ['r'] ["references"]
       (ReqArg (\fp opt -> return opt{ optReferences = Just fp }) "FILE")
       "CSL JSON bibliography"
    , Option ['a'] ["abbreviations"]
       (ReqArg (\fp opt -> return opt{ optAbbreviations = Just fp }) "FILE")
       "CSL abbreviations table"
    , Option ['l'] ["lang"]
       (ReqArg (\lang opt ->
                   case parseLang (T.pack lang) of
                     Right l  -> return opt{ optLang = Just l }
                     Left msg -> err $ "Could not parse language tag:\n" ++ msg)
          "BCP 47 language tag")
       "Override locale"
    , Option ['f'] ["format"]
       (ReqArg (\format opt -> return opt{ optFormat = Just format }) "html|json")
       "Controls formatting of entries in result"
    , Option [] ["link-citations"]
       (NoArg (\opt -> return opt{ optLinkCitations = True }))
       "Enable hyperlinking of citations to the bibliography"
    , Option [] ["link-bibliography"]
       (NoArg (\opt -> return opt{ optLinkBibliography = True }))
       "Enable hyperlinking of bibliography entries"
    , Option ['h'] ["help"]
       (NoArg (\opt -> return opt{ optHelp = True }))
       "Print usage information"
    , Option ['V'] ["version"]
       (NoArg (\opt -> return opt{ optVersion = True }))
       "Print version number"
    ]
  
  err :: String -> IO a
  err s = do
    hPutStrLn stderr s
    exitWith $ ExitFailure 1
  