{-# LANGUAGE OverloadedStrings #-}

{-| This module contains a couple functions copied from Data.Csv.Encoding
    that weren't exported. This file can be removed once they are.
-}

module Pipes.Csv.Encoding (
    encodeRecord
  , namedRecordToRecord
) where

import Blaze.ByteString.Builder (Builder, toByteString, fromByteString,
                                 fromWord8)
import Data.Monoid (mconcat, (<>), mempty)
import Data.Word (Word8)
import Data.Csv (Record, NamedRecord, Header)
import Data.ByteString (ByteString)

import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as B8
import qualified Data.HashMap.Strict as HM
import qualified Data.Vector as V

encodeRecord :: Word8 -> Record -> Builder
encodeRecord delim = mconcat . intersperse (fromWord8 delim)
                   . map fromByteString . map escape . V.toList

intersperse :: Builder -> [Builder] -> [Builder]
intersperse _   []      = []
intersperse sep (x:xs)  = x : prependToAll sep xs

prependToAll :: Builder -> [Builder] -> [Builder]
prependToAll _   []     = []
prependToAll sep (x:xs) = sep <> x : prependToAll sep xs

namedRecordToRecord :: Header -> NamedRecord -> Record
namedRecordToRecord hdr nr = V.map find hdr
  where
    find n = case HM.lookup n nr of
        Nothing -> moduleError "namedRecordToRecord" $
                   "header contains name " ++ show (B8.unpack n) ++
                   " which is not present in the named record"
        Just v  -> v

moduleError :: String -> String -> a
moduleError func msg = error $ "Pipes.Csv.Encoding." ++ func ++ ": " ++ msg
{-# NOINLINE moduleError #-}

escape :: ByteString -> ByteString
escape s
    | B.any (\ b -> b == dquote || b == comma || b == nl || b == cr || b == sp)
        s = toByteString $
            fromWord8 dquote
            <> B.foldl
                (\ acc b -> acc <> if b == dquote
                    then fromByteString "\"\""
                    else fromWord8 b)
                mempty
                s
            <> fromWord8 dquote
    | otherwise = s
  where
    dquote = 34
    comma  = 44
    nl     = 10
    cr     = 13
    sp     = 32