{-# LANGUAGE OverloadedStrings #-}
module Database.Schema.Migrations.Filesystem.Serialize
    ( serializeMigration
    )
where

import Data.ByteString ( ByteString )
import qualified Data.ByteString as BS
import Data.Text ( Text )
import qualified Data.Text as T
import Data.String.Conversions ( cs )
import Data.Time () -- for UTCTime Show instance
import Data.Maybe ( catMaybes )
import Data.Monoid ( (<>) )

import Database.Schema.Migrations.Migration
    ( Migration(..)
    )

type FieldSerializer = Migration -> Maybe ByteString

fieldSerializers :: [FieldSerializer]
fieldSerializers = [ serializeDesc
                   , serializeTimestamp
                   , serializeDepends
                   , serializeApply
                   , serializeRevert
                   ]

serializeDesc :: FieldSerializer
serializeDesc m =
    case mDesc m of
      Nothing -> Nothing
      Just desc -> Just . cs $ "Description: " <> desc

serializeTimestamp :: FieldSerializer
serializeTimestamp m =
    case mTimestamp m of
        Nothing -> Nothing
        Just ts -> Just $ "Created: " <> (cs . show $ ts)

serializeDepends :: FieldSerializer
serializeDepends m = Just . cs $ "Depends: " <> (T.intercalate " " $ mDeps m)

serializeRevert :: FieldSerializer
serializeRevert m =
    case mRevert m of
      Nothing -> Nothing
      Just revert -> Just $ "Revert: |\n" <>
                     (serializeMultiline revert)

serializeApply :: FieldSerializer
serializeApply m = Just $ "Apply: |\n" <> (serializeMultiline $ mApply m)

commonPrefix :: Text -> Text -> Text
commonPrefix a b = cs . map fst $ takeWhile (uncurry (==)) (T.zip a b)

commonPrefixLines :: [Text] -> Text
commonPrefixLines [] = ""
commonPrefixLines theLines = foldl1 commonPrefix theLines

serializeMultiline :: Text -> ByteString
serializeMultiline s =
    let sLines = T.lines s
        prefix = case T.head $ commonPrefixLines sLines of
                   -- If the lines already have a common prefix that
                   -- begins with whitespace, no new prefix is
                   -- necessary.
                   ' ' -> ""
                   -- Otherwise, use a new prefix of two spaces.
                   _ -> "  "

    in cs . T.unlines $ map (prefix <>) sLines

serializeMigration :: Migration -> ByteString
serializeMigration m = BS.intercalate "\n" fields
    where
      fields = catMaybes [ f m | f <- fieldSerializers ]
