{-# LANGUAGE OverloadedStrings  #-}
{-# LANGUAGE ViewPatterns       #-}
module Hakyll.Convert.OutputFormat
    (validOutputFormat, formatPath)
  where

import           Data.Default
import           Data.Time.Format             (formatTime, defaultTimeLocale)
import           System.FilePath

import           Hakyll.Convert.Common

import qualified Data.Text              as T
import qualified Data.Map.Strict        as M

validOutputFormat :: T.Text -> Bool
validOutputFormat format
  | T.null format = False
  | otherwise =
      case formatPath format def of
        Just _  -> True
        Nothing -> False

formatPath :: T.Text -> DistilledPost -> Maybe T.Text
formatPath format post = helper [] format >>= return.T.concat
  where
  helper acc input =
    case T.uncons input of
      Just ('%', rest) ->
        case T.uncons rest of
          Just (ch,  rest2) ->
            if ch `M.member` acceptableFormats
              then let formatter = acceptableFormats M.! ch
                   in helper ((formatter post):acc) rest2
              else Nothing
      Just (ch,  rest) -> helper ((T.singleton ch):acc) rest
      Nothing          -> Just $ reverse acc

-- When adding a new format, don't forget to update the help message in
-- tools/hakyll-convert.hs
acceptableFormats :: M.Map Char (DistilledPost -> T.Text)
acceptableFormats = M.fromList [
    -- this lets users put literal percent sign in the format)
    ('%', const $ T.singleton '%')

  , ('o', fmtOriginalPath) -- original filepath, like 2016/01/02/blog-post.html
  , ('s', fmtSlug)   -- original slug, i.e. "blog-post" from the example above
  , ('y', fmtYear2)  -- publication year, 2 digits
  , ('Y', fmtYear4)  -- publication year, 4 digits
  , ('m', fmtMonth)  -- publication month
  , ('d', fmtDay)    -- publication day
  , ('H', fmtHour)   -- publication hour
  , ('M', fmtMinute) -- publication minute
  , ('S', fmtSecond) -- publication second
  ]

fmtOriginalPath post =
    T.pack
  . dropTrailingSlash
  . dropExtensions
  $ chopUri (dpUri post)
  where
    dropTrailingSlash = reverse . dropWhile (== '/') . reverse
    chopUri (dropPrefix "http://" -> ("",rest)) =
       -- carelessly assumes we can treat URIs like filepaths
       joinPath $ drop 1 -- drop the domain
                $ splitPath rest
    chopUri u = error $
       "We've wrongly assumed that blog post URIs start with http://, but we got: " ++ u

    dropPrefix :: Eq a => [a] -> [a] -> ([a],[a])
    dropPrefix (x:xs) (y:ys) | x == y    = dropPrefix xs ys
    dropPrefix left right = (left,right)

fmtSlug post =
    T.reverse
  . (T.takeWhile (/= '/'))
  . T.reverse
  $ fmtOriginalPath post

fmtDate format = T.pack . (formatTime defaultTimeLocale format) . dpDate

fmtYear2  = fmtDate "%y"
fmtYear4  = fmtDate "%Y"
fmtMonth  = fmtDate "%m"
fmtDay    = fmtDate "%d"
fmtHour   = fmtDate "%H"
fmtMinute = fmtDate "%M"
fmtSecond = fmtDate "%S"