main :: IO ()
main = hakyllWith hakyllConfig $ do
  faIcons <- fold <$> preprocess loadFontAwesomeIcons

  -- "entry/year/month/day/title/index.md"
  let entryPattern      = "entry/*/*/*/*/index.md"
      entryFilesPattern = "entry/*/*/*/*/**"

  let tagPagesPath tag = "entry" </> "tags" </> sanitizeTagName tag </> "index.html"
  tags <- buildTags entryPattern $ fromFilePath . tagPagesPath

  let appendFooter locale zone item = do
        utc <- fmap Just (getItemUTC locale (itemIdentifier item))
                 `catchError` const (return Nothing)
        let y = fmap (formatTime locale "%Y" . utcToLocalTime zone) utc
        appendFooterWith y item
      appendFooterWith y item = do
        footer <- loadBody $ setVersion y "footer.html"
        appendItemBody footer item

  match entryPattern $ do
    route $ setExtension "html"
    compile $
      pandocCompilerWith readerOptions writerOptions
        >>= absolutizeUrls
        >>= saveSnapshot "feed-content"
        >>= renderKaTeX
        >>= saveSnapshot "content"
        >>= loadAndApplyTemplate "templates/entry.html" (postContext tags)
        >>= appendFooter defaultTimeLocale' timeZoneJST
        >>= loadAndApplyTemplate "templates/default.html" (postContext tags)
        >>= modifyExternalLinkAttributes
        >>= cleanIndexHtmls
        >>= renderFontAwesome faIcons

  match entryFilesPattern $ do
    route idRoute
    compile copyFileCompiler

  let listPageRules title footerVer pages = paginateRules pages $ \num pat -> do
        route idRoute
        compile $ do
          posts  <- recentFirst =<< loadAllSnapshots pat "content"
          let listContext' = listField "posts" postContext' (return posts)
                          <> boolField "noindex" (const (num /= 1))
                          <> paginateContext pages num
                          <> maybe missingField (constField "title") title
                          <> listContext
              postContext' = teaserField "teaser" "content" <> postContext tags
          makeEmptyItem'
            >>= loadAndApplyTemplate "templates/entry_list.html" listContext'
            >>= appendFooterWith footerVer
            >>= loadAndApplyTemplate "templates/default.html" listContext'
            >>= modifyExternalLinkAttributes
            >>= cleanIndexHtmls
            >>= renderFontAwesome faIcons

  tagsRules tags $ \tag pat ->
    let grouper = fmap (paginateEvery 5) . sortRecentFirst
        makeId  = makePageIdentifier $ tagPagesPath tag
        title   = "Tag archives: " ++ tag
    in  buildPaginateWith grouper pat makeId >>= listPageRules (Just title) Nothing

  let yearlyPagePath year = "entry" </> year </> "index.html"
  yearlyArchives <- buildYearlyArchives defaultTimeLocale' timeZoneJST entryPattern $
                      fromFilePath . yearlyPagePath
  archivesRules yearlyArchives $ \year pat ->
    let grouper = fmap (paginateEvery 5) . sortRecentFirst
        makeId  = makePageIdentifier $ yearlyPagePath year
        title   = "Yearly archives: " <> year
    in  buildPaginateWith grouper pat makeId >>= listPageRules (Just title) (Just year)

  let monthlyPagePath (year, month) = "entry" </> year </> month </> "index.html"
  monthlyArchives <- buildMonthlyArchives defaultTimeLocale' timeZoneJST entryPattern $
                       fromFilePath . monthlyPagePath
  archivesRules monthlyArchives $ \key@(year, month) pat ->
    let grouper = fmap (paginateEvery 5) . sortRecentFirst
        makeId  = makePageIdentifier $ monthlyPagePath key
        title   = "Monthly archives: " <> year <> "/" <> month
    in  buildPaginateWith grouper pat makeId >>= listPageRules (Just title) (Just year)

  listPageRules Nothing Nothing =<<
    let grouper = fmap (paginateEvery 5) . sortRecentFirst
        makeId  = makePageIdentifier "index.html"
    in  buildPaginateWith grouper entryPattern makeId

  let years = map fst $ archivesMap yearlyArchives
      version' = maybe id version
  forM_ (Nothing:map Just years) $ \year -> version' year $
    create ["footer.html"] $
      compile $ do
        recent <- fmap (take 5) . recentFirst =<< loadAllSnapshots entryPattern "content"
        let ctx = listField "recent-posts" (postContext tags) (return recent)
               <> tagCloudField' "tag-cloud" tags
               <> yearMonthArchiveField "archives" yearlyArchives monthlyArchives year
               <> siteContext
        makeEmptyItem' >>= loadAndApplyTemplate "templates/footer.html" ctx

  create ["feed.xml"] $ do
    route idRoute
    compile $ do
      let ctx = bodyField "description" <> postContext tags
      loadAllSnapshots entryPattern "feed-content"
        >>= fmap (take 20) . recentFirst
        >>= mapM (prependBaseUrl (feedRoot atomFeedConfig))
        >>= renderAtom atomFeedConfig ctx

  match "images/**/*.svg" $ do
    route idRoute
    compile $ optimizeSVGCompiler ["-p", "4"]

  match "images/icon/cocoa.ico" $ do
    route $ constRoute "favicon.ico"
    compile copyFileCompiler

  match ("CNAME" .||. "images/**") $ do
    route idRoute
    compile copyFileCompiler

  scssDependencies <- makePatternDependency "stylesheets/*/**.scss"
  match "stylesheets/*/**.scss" $ compile getResourceBody
  rulesExtraDependencies [scssDependencies] $
    match "stylesheets/*.scss" $ do
      route $ setExtension "css"
      compile $ fmap compressCss <$> sassCompiler

  match "stylesheets/*.css" $ do
    route   idRoute
    compile compressCssCompiler

  create ["stylesheets/highlight.css"] $ do
    route   idRoute
    compile $ makeItem $ compressCss $ styleToCss pygments

  match "node_modules/@fortawesome/fontawesome-svg-core/styles.css" $ do
    route $ constRoute "vendor/fontawesome/style.css"
    compile compressCssCompiler

  match ("node_modules/katex/dist/katex.min.css" .||. "node_modules/katex/dist/fonts/**") $ do
    route $ gsubRoute "node_modules/katex/dist/" (const "vendor/katex/")
    compile copyFileCompiler

  match "node_modules/normalize.css/normalize.css" $ do
    route $ gsubRoute "node_modules/" (const "vendor/")
    compile copyFileCompiler

  match "templates/*" $ compile templateBodyCompiler

--- Contexts
main :: IO ()
main = hakyllWith hakyllConfig $ do
  faIcons <- fold <$> preprocess loadFontAwesomeIcons

  -- "entry/year/month/day/title/index.md"
  let entryPattern      = "entry/*/*/*/*/index.md"
      entryFilesPattern = "entry/*/*/*/*/**"

  let tagPagesPath tag = "entry" </> "tags" </> sanitizeTagName tag </> "index.html"
  tags <- buildTags entryPattern $ fromFilePath . tagPagesPath

  let appendFooter locale zone item = do
        utc <- fmap Just (getItemUTC locale (itemIdentifier item))
                 `catchError` const (return Nothing)
        let y = fmap (formatTime locale "%Y" . utcToLocalTime zone) utc
        appendFooterWith y item
      appendFooterWith y item = do
        footer <- loadBody $ setVersion y "footer.html"
        appendItemBody footer item

  match entryPattern $ do
    route $ setExtension "html"
    compile $
      pandocCompilerWith readerOptions writerOptions
        >>= absolutizeUrls
        >>= saveSnapshot "feed-content"
        >>= renderKaTeX
        >>= saveSnapshot "content"
        >>= loadAndApplyTemplate "templates/entry.html" (postContext tags)
        >>= appendFooter defaultTimeLocale' timeZoneJST
        >>= loadAndApplyTemplate "templates/default.html" (postContext tags)
        >>= modifyExternalLinkAttributes
        >>= cleanIndexHtmls
        >>= renderFontAwesome faIcons

  match entryFilesPattern $ do
    route idRoute
    compile copyFileCompiler

  let listPageRules title footerVer pages = paginateRules pages $ \num pat -> do
        route idRoute
        compile $ do
          posts  <- recentFirst =<< loadAllSnapshots pat "content"
          let listContext' = listField "posts" postContext' (return posts)
                          <> boolField "noindex" (const (num /= 1))
                          <> paginateContext pages num
                          <> maybe missingField (constField "title") title
                          <> listContext
              postContext' = teaserField "teaser" "content" <> postContext tags
          makeEmptyItem'
            >>= loadAndApplyTemplate "templates/entry_list.html" listContext'
            >>= appendFooterWith footerVer
            >>= loadAndApplyTemplate "templates/default.html" listContext'
            >>= modifyExternalLinkAttributes
            >>= cleanIndexHtmls
            >>= renderFontAwesome faIcons

  tagsRules tags $ \tag pat ->
    let grouper = fmap (paginateEvery 5) . sortRecentFirst
        makeId  = makePageIdentifier $ tagPagesPath tag
        title   = "Tag archives: " ++ tag
    in  buildPaginateWith grouper pat makeId >>= listPageRules (Just title) Nothing

  let yearlyPagePath year = "entry" </> year </> "index.html"
  yearlyArchives <- buildYearlyArchives defaultTimeLocale' timeZoneJST entryPattern $
                      fromFilePath . yearlyPagePath
  archivesRules yearlyArchives $ \year pat ->
    let grouper = fmap (paginateEvery 5) . sortRecentFirst
        makeId  = makePageIdentifier $ yearlyPagePath year
        title   = "Yearly archives: " <> year
    in  buildPaginateWith grouper pat makeId >>= listPageRules (Just title) (Just year)

  let monthlyPagePath (year, month) = "entry" </> year </> month </> "index.html"
  monthlyArchives <- buildMonthlyArchives defaultTimeLocale' timeZoneJST entryPattern $
                       fromFilePath . monthlyPagePath
  archivesRules monthlyArchives $ \key@(year, month) pat ->
    let grouper = fmap (paginateEvery 5) . sortRecentFirst
        makeId  = makePageIdentifier $ monthlyPagePath key
        title   = "Monthly archives: " <> year <> "/" <> month
    in  buildPaginateWith grouper pat makeId >>= listPageRules (Just title) (Just year)

  listPageRules Nothing Nothing =<<
    let grouper = fmap (paginateEvery 5) . sortRecentFirst
        makeId  = makePageIdentifier "index.html"
    in  buildPaginateWith grouper entryPattern makeId

  let years = map fst $ archivesMap yearlyArchives
      version' = maybe id version
  forM_ (Nothing:map Just years) $ \year -> version' year $
    create ["footer.html"] $
      compile $ do
        recent <- fmap (take 5) . recentFirst =<< loadAllSnapshots entryPattern "content"
        let ctx = listField "recent-posts" (postContext tags) (return recent)
               <> tagCloudField' "tag-cloud" tags
               <> yearMonthArchiveField "archives" yearlyArchives monthlyArchives year
               <> siteContext
        makeEmptyItem' >>= loadAndApplyTemplate "templates/footer.html" ctx

  create ["feed.xml"] $ do
    route idRoute
    compile $ do
      let ctx = bodyField "description" <> postContext tags
      loadAllSnapshots entryPattern "feed-content"
        >>= fmap (take 20) . recentFirst
        >>= mapM (prependBaseUrl (feedRoot atomFeedConfig))
        >>= renderAtom atomFeedConfig ctx

  match "images/**/*.svg" $ do
    route idRoute
    compile $ optimizeSVGCompiler ["-p", "4"]

  match "images/icon/cocoa.ico" $ do
    route $ constRoute "favicon.ico"
    compile copyFileCompiler

  match ("CNAME" .||. "images/**") $ do
    route idRoute
    compile copyFileCompiler

  scssDependencies <- makePatternDependency "stylesheets/*/**.scss"
  match "stylesheets/*/**.scss" $ compile getResourceBody
  rulesExtraDependencies [scssDependencies] $
    match "stylesheets/*.scss" $ do
      route $ setExtension "css"
      compile $ fmap compressCss <$> sassCompiler

  match "stylesheets/*.css" $ do
    route   idRoute
    compile compressCssCompiler

  create ["stylesheets/highlight.css"] $ do
    route   idRoute
    compile $ makeItem $ compressCss $ styleToCss pygments

  match "node_modules/@fortawesome/fontawesome-svg-core/styles.css" $ do
    route $ constRoute "vendor/fontawesome/style.css"
    compile compressCssCompiler

  match ("node_modules/katex/dist/katex.min.css" .||. "node_modules/katex/dist/fonts/**") $ do
    route $ gsubRoute "node_modules/katex/dist/" (const "vendor/katex/")
    compile copyFileCompiler

  match "node_modules/normalize.css/normalize.css" $ do
    route $ gsubRoute "node_modules/" (const "vendor/")
    compile copyFileCompiler

  match "templates/*" $ compile templateBodyCompiler

--- Contexts
main = hakyllWith hakyllConfig $ do
  faIcons <- fold <$> preprocess loadFontAwesomeIcons

  -- "entry/year/month/day/title/index.md"
  let entryPattern      = "entry/*/*/*/*/index.md"
      entryFilesPattern = "entry/*/*/*/*/**"

  let tagPagesPath tag = "entry" </> "tags" </> sanitizeTagName tag </> "index.html"
  tags <- buildTags entryPattern $ fromFilePath . tagPagesPath

  let appendFooter locale zone item = do
        utc <- fmap Just (getItemUTC locale (itemIdentifier item))
                 `catchError` const (return Nothing)
        let y = fmap (formatTime locale "%Y" . utcToLocalTime zone) utc
        appendFooterWith y item
      appendFooterWith y item = do
        footer <- loadBody $ setVersion y "footer.html"
        appendItemBody footer item

  match entryPattern $ do
    route $ setExtension "html"
    compile $
      pandocCompilerWith readerOptions writerOptions
        >>= absolutizeUrls
        >>= saveSnapshot "feed-content"
        >>= renderKaTeX
        >>= saveSnapshot "content"
        >>= loadAndApplyTemplate "templates/entry.html" (postContext tags)
        >>= appendFooter defaultTimeLocale' timeZoneJST
        >>= loadAndApplyTemplate "templates/default.html" (postContext tags)
        >>= modifyExternalLinkAttributes
        >>= cleanIndexHtmls
        >>= renderFontAwesome faIcons

  match entryFilesPattern $ do
    route idRoute
    compile copyFileCompiler

  let listPageRules title footerVer pages = paginateRules pages $ \num pat -> do
        route idRoute
        compile $ do
          posts  <- recentFirst =<< loadAllSnapshots pat "content"
          let listContext' = listField "posts" postContext' (return posts)
                          <> boolField "noindex" (const (num /= 1))
                          <> paginateContext pages num
                          <> maybe missingField (constField "title") title
                          <> listContext
              postContext' = teaserField "teaser" "content" <> postContext tags
          makeEmptyItem'
            >>= loadAndApplyTemplate "templates/entry_list.html" listContext'
            >>= appendFooterWith footerVer
            >>= loadAndApplyTemplate "templates/default.html" listContext'
            >>= modifyExternalLinkAttributes
            >>= cleanIndexHtmls
            >>= renderFontAwesome faIcons

  tagsRules tags $ \tag pat ->
    let grouper = fmap (paginateEvery 5) . sortRecentFirst
        makeId  = makePageIdentifier $ tagPagesPath tag
        title   = "Tag archives: " ++ tag
    in  buildPaginateWith grouper pat makeId >>= listPageRules (Just title) Nothing

  let yearlyPagePath year = "entry" </> year </> "index.html"
  yearlyArchives <- buildYearlyArchives defaultTimeLocale' timeZoneJST entryPattern $
                      fromFilePath . yearlyPagePath
  archivesRules yearlyArchives $ \year pat ->
    let grouper = fmap (paginateEvery 5) . sortRecentFirst
        makeId  = makePageIdentifier $ yearlyPagePath year
        title   = "Yearly archives: " <> year
    in  buildPaginateWith grouper pat makeId >>= listPageRules (Just title) (Just year)

  let monthlyPagePath (year, month) = "entry" </> year </> month </> "index.html"
  monthlyArchives <- buildMonthlyArchives defaultTimeLocale' timeZoneJST entryPattern $
                       fromFilePath . monthlyPagePath
  archivesRules monthlyArchives $ \key@(year, month) pat ->
    let grouper = fmap (paginateEvery 5) . sortRecentFirst
        makeId  = makePageIdentifier $ monthlyPagePath key
        title   = "Monthly archives: " <> year <> "/" <> month
    in  buildPaginateWith grouper pat makeId >>= listPageRules (Just title) (Just year)

  listPageRules Nothing Nothing =<<
    let grouper = fmap (paginateEvery 5) . sortRecentFirst
        makeId  = makePageIdentifier "index.html"
    in  buildPaginateWith grouper entryPattern makeId

  let years = map fst $ archivesMap yearlyArchives
      version' = maybe id version
  forM_ (Nothing:map Just years) $ \year -> version' year $
    create ["footer.html"] $
      compile $ do
        recent <- fmap (take 5) . recentFirst =<< loadAllSnapshots entryPattern "content"
        let ctx = listField "recent-posts" (postContext tags) (return recent)
               <> tagCloudField' "tag-cloud" tags
               <> yearMonthArchiveField "archives" yearlyArchives monthlyArchives year
               <> siteContext
        makeEmptyItem' >>= loadAndApplyTemplate "templates/footer.html" ctx

  create ["feed.xml"] $ do
    route idRoute
    compile $ do
      let ctx = bodyField "description" <> postContext tags
      loadAllSnapshots entryPattern "feed-content"
        >>= fmap (take 20) . recentFirst
        >>= mapM (prependBaseUrl (feedRoot atomFeedConfig))
        >>= renderAtom atomFeedConfig ctx

  match "images/**/*.svg" $ do
    route idRoute
    compile $ optimizeSVGCompiler ["-p", "4"]

  match "images/icon/cocoa.ico" $ do
    route $ constRoute "favicon.ico"
    compile copyFileCompiler

  match ("CNAME" .||. "images/**") $ do
    route idRoute
    compile copyFileCompiler

  scssDependencies <- makePatternDependency "stylesheets/*/**.scss"
  match "stylesheets/*/**.scss" $ compile getResourceBody
  rulesExtraDependencies [scssDependencies] $
    match "stylesheets/*.scss" $ do
      route $ setExtension "css"
      compile $ fmap compressCss <$> sassCompiler

  match "stylesheets/*.css" $ do
    route   idRoute
    compile compressCssCompiler

  create ["stylesheets/highlight.css"] $ do
    route   idRoute
    compile $ makeItem $ compressCss $ styleToCss pygments

  match "node_modules/@fortawesome/fontawesome-svg-core/styles.css" $ do
    route $ constRoute "vendor/fontawesome/style.css"
    compile compressCssCompiler

  match ("node_modules/katex/dist/katex.min.css" .||. "node_modules/katex/dist/fonts/**") $ do
    route $ gsubRoute "node_modules/katex/dist/" (const "vendor/katex/")
    compile copyFileCompiler

  match "node_modules/normalize.css/normalize.css" $ do
    route $ gsubRoute "node_modules/" (const "vendor/")
    compile copyFileCompiler

  match "templates/*" $ compile templateBodyCompiler

--- Contexts