content: {-# LANGUAGE OverloadedStrings   #-}
  {-# LANGUAGE RecordWildCards     #-}
  {-# LANGUAGE ScopedTypeVariables #-}
  
  module Main where
  
  import Control.Applicative (optional, (<|>))
  import Control.Exception   (SomeException)
  import Data.Text           (Text)
  import Data.Version        (showVersion)
  import Dhall.CsvToDhall
  import Dhall.Pretty        (CharacterSet (..))
  import Options.Applicative (Parser, ParserInfo)
  
  import qualified Control.Exception
  import qualified Data.Text.IO        as Text.IO
  import qualified Dhall.Core
  import qualified Dhall.Csv.Util
  import qualified Dhall.Util
  import qualified GHC.IO.Encoding
  import qualified Options.Applicative as Options
  import qualified Paths_dhall_csv     as Meta
  import qualified System.Exit
  import qualified System.IO           as IO
  
  parserInfo :: ParserInfo Options
  parserInfo = Options.info
            (  Options.helper <*> parseOptions)
            (  Options.fullDesc
            <> Options.progDesc "Convert a CSV expression to a Dhall expression, given the expected Dhall type"
            )
  
  data Options
      = Default
          { schema     :: Maybe Text
          , conversion :: Conversion
          , file       :: Maybe FilePath
          , output     :: Maybe FilePath
          , ascii      :: Bool
          , plain      :: Bool
          , noHeader   :: Bool
          }
      | Type
          { file       :: Maybe FilePath
          , output     :: Maybe FilePath
          , ascii      :: Bool
          , plain      :: Bool
          , noHeader   :: Bool
          }
      | Version
      deriving Show
  
  parseOptions :: Parser Options
  parseOptions =
          typeCommand
      <|> (   Default
          <$> optional parseSchema
          <*> parseConversion
          <*> optional parseFile
          <*> optional parseOutput
          <*> parseASCII
          <*> parsePlain
          <*> parseNoHeader
          )
      <|> parseVersion
    where
      typeCommand =
          Options.hsubparser
              (Options.command "type" info <> Options.metavar "type")
        where
          info =
              Options.info parser (Options.progDesc "Output the inferred Dhall type from a CSV value (not implemented)")
  
          parser =
                  Type
              <$> optional parseFile
              <*> optional parseOutput
              <*> parseASCII
              <*> parsePlain
              <*> parseNoHeader
  
      parseSchema =
          Options.strArgument
              (  Options.metavar "SCHEMA"
              <> Options.help "Dhall type (schema).  You can omit the schema to let the executable infer the schema from the CSV value (not implemented)"
              )
  
      parseVersion =
          Options.flag'
              Version
              (  Options.long "version"
              <> Options.short 'V'
              <> Options.help "Display version"
              )
  
      parseFile =
          Options.strOption
              (   Options.long "file"
              <>  Options.help "Read CSV from a file instead of standard input"
              <>  Options.metavar "FILE"
              <>  Options.action "file"
              )
  
      parseOutput =
          Options.strOption
              (   Options.long "output"
              <>  Options.help "Write Dhall expression to a file instead of standard output"
              <>  Options.metavar "FILE"
              <>  Options.action "file"
              )
  
      parseASCII =
          Options.switch
              (   Options.long "ascii"
              <>  Options.help "Format code using only ASCII syntax"
              )
  
      parsePlain =
          Options.switch
              (   Options.long "plain"
              <>  Options.help "Disable syntax highlighting"
              )
  
      parseNoHeader =
          Options.switch
              (   Options.long "no-header"
              <>  Options.help "Convert CSV with no header"
              )
  
  
  main :: IO ()
  main = do
      GHC.IO.Encoding.setLocaleEncoding GHC.IO.Encoding.utf8
  
      options <- Options.execParser parserInfo
  
      let toCharacterSet ascii = case ascii of
              True  -> ASCII
              False -> Unicode
  
      let toCsv hasHeader file = do
              text <- case file of
                      Nothing -> Text.IO.getContents
                      Just path -> Text.IO.readFile path
  
              case Dhall.Csv.Util.decodeCsvDefault hasHeader text of
                  Left err -> fail err
                  Right csv -> pure csv
  
      let toSchema schema = do
              finalSchema <- case schema of
                  Just text -> resolveSchemaExpr text
                  Nothing   -> fail "Please specify a schema. Type inference has not been implemented"
  
              typeCheckSchemaExpr id finalSchema
  
      case options of
          Version ->
              putStrLn (showVersion Meta.version)
  
          Default{..} -> do
              handle $ do
                  let characterSet = toCharacterSet ascii
  
                  csv <- toCsv (not noHeader) file
  
                  finalSchema <- toSchema schema
  
                  expression <- Dhall.Core.throws $ dhallFromCsv conversion finalSchema csv
  
                  Dhall.Util.renderExpression characterSet plain output expression
  
          Type{} -> do
              putStrLn "type command has not been implemented yet"
  
  
  handle :: IO a -> IO a
  handle = Control.Exception.handle handler
    where
      handler :: SomeException -> IO a
      handler e = do
          IO.hPutStrLn IO.stderr ""
          IO.hPrint    IO.stderr e
          System.Exit.exitFailure
  