{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
module Data.ZettelID where

import Text.Read
       (readMaybe)

import Data.Path

import Data.Text
       (Text)
import qualified Data.Text as T

import Text.Printf
       (printf)

import Data.Set
       (Set)
import qualified Data.Set as Set

import Data.Monoid
       (First(..))

import Data.Time
       (Day, defaultTimeLocale)
import Data.Time.Format
       (formatTime, parseTimeM)

-- | Uniquely identifying zettel
data ZettelID
  = ZettelID { day :: Day -- ^ The day when it was authored
             , counter :: Int -- ^ Increasing counter
             }
  deriving (Show, Eq, Ord)

-- | Render a zettel id as a text
--
-- The rendered id is "%y%m%d0000" where the latter number is the counter.
-- For example the second zettel for 11.6.2020 would be "2006110002"
render :: ZettelID -> Text
render ZettelID{..} = T.pack (dayFormat <> counterFormat)
  where
    dayFormat = formatTime defaultTimeLocale "%y%m%d" day
    counterFormat = printf "%03d" counter

-- | Parse the rendered zetteled id back into a 'ZettelID'
--
-- See 'render' for the format
parse :: Text -> Maybe ZettelID
parse path = ZettelID <$> dayParse path <*> counterParse path
  where
    dayParse = parseTimeM False defaultTimeLocale "%y%m%d" . T.unpack . T.take 6
    counterParse = readMaybe . T.unpack . T.drop 6 . T.takeWhile (/= '.')

-- | Convert a zettel id into a filename
toPath :: ZettelID -> Path Dir File
toPath zid = file (render zid <> ".md")

-- | Parse a filename into a zettel id
fromPath :: Path a File -> Maybe ZettelID
fromPath = \case
  ConsFile path _ -> parse (T.dropEnd 2 path)
  Nil -> Nothing

-- | Create a new unique zettel id
createZettelID :: Set ZettelID -> Day -> Maybe ZettelID
createZettelID history today = getFirst (foldMap firstFree zettelIDs)
  where
    firstFree :: ZettelID -> First ZettelID
    firstFree zid | zid `Set.member` history = First Nothing
                  | otherwise = First (Just zid)
    zettelIDs :: [ZettelID]
    zettelIDs = ZettelID today <$> [0..]