{-|
Module      : Data.Sv.Encode.Options
Copyright   : (C) CSIRO 2017-2019
License     : BSD3
Maintainer  : George Wilson <george.wilson@data61.csiro.au>
Stability   : experimental
Portability : non-portable

Options to configure encoding
-}

module Data.Sv.Encode.Options (
  EncodeOptions (EncodeOptions, _encodeSeparator, _quoting, _newline, _terminalNewline)
, HasEncodeOptions (encodeOptions, quoting, newline, terminalNewline)
, HasSeparator (separator)
, Quoting (..)
, defaultEncodeOptions
) where

import Control.Lens (Lens')

import Data.Sv.Structure.Newline (Newline, lf)
import Data.Sv.Structure.Separator (Separator, HasSeparator (separator), comma)

-- | Should the output file have quotes around every value, or only when they
-- are required?
--
-- Beware the 'Never' constructor. It can construct malformed CSV files if
-- there are fields containing quotes, newlines, or separators. It is the
-- fastest option though, so you might like to use it if you're sure none
-- of your encoded data will include those characters.
data Quoting
  = Always
  | AsNeeded
  | Never

-- | These are options to configure encoding. A default is provided as
-- 'defaultEncodeOptions'.
data EncodeOptions =
  EncodeOptions {
    -- | Are your values separated by commas, tabs, or something else? Default: comma
    _encodeSeparator :: Separator
    -- | Would you like quotes around your values? If so, double quotes or single? Deafult: Double quotes
  , _quoting :: Quoting
    -- | What kind of newline would you like? Default: LF
  , _newline :: Newline
    -- | Should the file be terminated with a newline? Default: No
  , _terminalNewline :: Bool
  }

-- | Classy lenses for 'EncodeOptions'
--
-- @
-- import Control.Lens
--
-- defaultEncodeOptions & newline .~ crlf & quoting .~ Always
-- @
class HasSeparator c => HasEncodeOptions c where
  encodeOptions :: Lens' c EncodeOptions
  quoting :: Lens' c Quoting
  {-# INLINE quoting #-}
  newline :: Lens' c Newline
  {-# INLINE newline #-}
  terminalNewline :: Lens' c Bool
  {-# INLINE terminalNewline #-}
  newline = encodeOptions . newline
  quoting = encodeOptions . quoting
  terminalNewline = encodeOptions . terminalNewline

instance HasSeparator EncodeOptions where
  separator f (EncodeOptions x1 x2 x3 x4) =
    fmap (\ y -> EncodeOptions y x2 x3 x4) (f x1)
  {-# INLINE separator #-}

instance HasEncodeOptions EncodeOptions where
  encodeOptions = id
  {-# INLINE encodeOptions #-}
  quoting f (EncodeOptions x1 x2 x3 x4) =
    fmap (\ y -> EncodeOptions x1 y x3 x4) (f x2)
  {-# INLINE quoting #-}
  newline f (EncodeOptions x1 x2 x3 x4) =
    fmap (\ y -> EncodeOptions x1 x2 y x4) (f x3)
  {-# INLINE newline #-}
  terminalNewline f (EncodeOptions x1 x2 x3 x4) =
    fmap (EncodeOptions x1 x2 x3) (f x4)
  {-# INLINE terminalNewline #-}

-- | The default options for encoding.
--
-- The default is a CSV file with quotes when necessary, LF lines,
-- and no terminating newline.
defaultEncodeOptions :: EncodeOptions
defaultEncodeOptions = EncodeOptions comma AsNeeded lf False