{-# LANGUAGE RecordWildCards #-}
module GalleryPage (make) where

import BuilderQQ
import Date
import Info
import qualified NsfwWarning
import TagTransforms

import Control.Monad
import Data.Foldable
import Data.Function ((&))
import qualified Data.HashMap.Strict as HashMap
import qualified Data.HashSet as HashSet
import Data.List (intersperse, sort, sortOn)
import Data.Maybe
import Data.Ord (Down (..))
import qualified Data.Text.Lazy as Lazy
import System.FilePath (takeDirectory, joinPath, splitPath)

make :: Text -> IndexInfo -> GalleryInfo -> [(FilePath, Info)] -> Lazy.Text
make root iinfo ginfo infos =
  toLazyText $ make' root ginfo $
  map (fmap $ transformInfoTags iinfo.tags) infos


make' :: Text -> GalleryInfo -> [(FilePath, Info)] -> Builder
make' root (GalleryInfo {title, desc, prefix, filters, hidden}) infos =
  [b|
    <!DOCTYPE html>
    <html lang=en>
    <meta charset=utf-8>
    <link rel=stylesheet href=/style/shiny/gallery.css>
    <link rel=icon href=/style/niss.svg>
    <link rel=alternate href=rss.xml type=application/rss+xml>

    <meta property=og:type content=og:website>
    <meta property=og:title content="$title">
    <meta property=og:site_name content="$title">
    <meta property=og:description content="$desc">
    <meta property=og:image content="$url/style/card.png">
    <meta property=og:url content="$url">
    <meta name=twitter:site content=@2_gecs>
    <meta name=twitter:card content=summary>

    <meta name=robots content='noai,noimageai'>

    <script src=/script/gallery.js type=module></script>
    $nsfwScript

    <title>$title</title>

    $nsfwDialog

    <div class=page>
      <header>
        <h1>$title</h1>
        <a class="right corner" href=rss.xml>rss</a>
        <a class="left corner" href=$undir>back</a>
      </header>

      <nav id=filters>
        <details id=filters-details>
          <summary><h2>filters</h2></summary>
          <div>
            <h3>show only</h3>
            <ul id=require class=filterlist>
              $requireFilters
            </ul>

            <h3>exclude</h3>
            <ul id=exclude class=filterlist>
              $excludeFilters
            </ul>

            <ul id=filterstuff>
              <li><a href=# id=clear>clear</a>
              <li><a href=# id=sortalpha>sort by name</a>
              <li><a href=# id=sortuses>sort by uses</a>
              <li><a href=# id=singles>toggle single-use tags</a>
            </ul>
          </div>
        </details>
      </nav>

      <main>
        $items
      </main>
    </div>
  |]
 where
  items = map (uncurry $ makeYearItems nsfw) infosByYear

  infosByYear :: [(Int, [(FilePath, Info)])]
  infosByYear = infos &
    filter (not . (.unlisted) . snd) &
    sortOn (Down . compareKeyFor nsfw . snd) &
    groupOnKey (\(_, i) -> latestYearFor nsfw i)

  undir = joinPath (replicate (length (splitPath prefix)) "..")

  allTags = infos
    & concatMap (map (,1) . HashSet.toList . tagsFor nsfw . snd)
    & HashMap.fromListWith (+) & HashMap.toList
    & sort

  requireFilters = map (uncurry $ makeFilter "require" mempty) allTags
  excludeFilters = map (uncurry $ makeFilter "exclude" hidden) allTags

  nsfw = filters.nsfw /= NoNsfw

  url = [b|$root/$prefix|]

  nsfw'      = NsfwWarning.Gallery <$ guard nsfw
  nsfwScript = NsfwWarning.script nsfw'
  nsfwDialog = NsfwWarning.dialog nsfw'

-- from @extra@
groupOnKey :: Eq k => (a -> k) -> [a] -> [(k, [a])]
groupOnKey _ []     = []
groupOnKey f (x:xs) = (fx, x:yes) : groupOnKey f no where
  fx        = f x
  (yes, no) = span (\y -> fx == f y) xs

makeFilter :: Text -> HashSet Text -> Text -> Int -> Builder
makeFilter prefix initial tag count = [b|
    <li$hidden>
      <input type=checkbox id="$id'" value="$tag"$checked>
      <label for="$id'" data-count=$count>$tag</label>
  |]
 where
  id' = [b|$prefix$&_$tag'|]
  tag' = escId tag
  checked = if HashSet.member tag initial then [b|$& checked|] else ""
  hidden = if count <= 1 then [b|$& hidden|] else ""

makeYearItems :: Bool -- ^ nsfw
              -> Int -- ^ year
              -> [(FilePath, Info)]
              -> Builder
makeYearItems nsfw year infos = [b|
    <h2 class="item year-marker" id="marker-$year" aria-label=$year>
      $year'
    </h2>
    $items
  |]
 where
  items = map (uncurry $ makeItem nsfw) infos
  year' = show year & foldMap \c -> [b|<span class=y>$c</span>|]

makeItem :: Bool -> FilePath -> Info -> Builder
makeItem nsfw file info@(Info {bg}) = [b|
    <figure class="item post$nsfw'" data-year=$year' data-updated="$updated'"
        data-tags="$tags'">
      <a href="$dir">
        <img src="$thumbnail" loading=lazy$bgStyle
          width=200 height=200
          alt="$title" title="$tooltip">
      </a>
    </figure>
  |]
 where
  title     = escAttr $ fromMaybe info.title info.galleryTitle
  dir       = takeDirectory file
  thumbnail = getThumb dir info
  nsfw'     = if nsfw && anyNsfw info then [b|$& nsfw|] else ""
  tags'     = fold $ intersperse ";" $ map fromText $
              sort $ HashSet.toList $ tagsFor nsfw info
  date      = latestDateFor nsfw info
  date'     = formatTooltip date
  year'     = date.year
  updated'  = if hasUpdatesFor nsfw info then [b|true|] else [b|false|]
  bgStyle   = case bg of Other col -> [b|$& style="background: $col"|]; _ -> ""
  tooltip   = [b|$title ($upd$date')|]
    where upd = if hasUpdatesFor nsfw info then "updated " else "" :: Builder