{-# LANGUAGE OverloadedStrings, CPP #-}

module Text.LaTeX.Guide.Backend.HTML (
  backend
  ) where

import Text.LaTeX.Guide.Syntax
import Text.LaTeX.Guide.Info hiding (LaTeX)
import Text.LaTeX.Guide.Auto
--
import Text.Blaze.Html5 (Html,toHtml,preEscapedToHtml,preEscapedToValue,(!))
import qualified Text.Blaze.Html5 as H
import qualified Text.Blaze.Html5.Attributes as A
import Text.Blaze.Html.Renderer.Text (renderHtml)
import Data.Text.Lazy.IO
--
import Data.Monoid
import Control.Monad.Trans.State
import Data.List (intersperse)
import Data.Version (showVersion)
-- Time
import Data.Time

#if !MIN_VERSION_base(4,8,0)
import Control.Applicative ((<$>))
#endif

resURL :: Text -> Text
resURL t = "https://raw.github.com/Daniel-Diaz/hatex-guide/master/res/" <> t

sectFromInt :: Int -> Html -> Html
sectFromInt 1 = H.h1
sectFromInt 2 = H.h2
sectFromInt 3 = H.h3
sectFromInt 4 = H.h4
sectFromInt 5 = H.h5
sectFromInt 6 = H.h6
sectFromInt n = error $ "Subsection with hierarchy of " ++ show n ++ " is not available in the HTML backend."

type SectionNumber = [Int]

showSN :: SectionNumber -> String
showSN = mconcat . intersperse "." . fmap show

mapLast :: (a -> a) -> [a] -> [a]
mapLast _ [] = []
mapLast f [x] = [f x]
mapLast f (x:xs) = x : mapLast f xs

upgradeSN :: Int -> SectionNumber -> SectionNumber
upgradeSN n [] = replicate (n-1) 0 ++ [1]
upgradeSN n sn = mapLast (+1) $ take n $ sn ++ repeat 0

sectiondots :: Int -> Html
sectiondots 1 = mempty
sectiondots n = toHtml $ (mconcat $ replicate (n-1) str) ++ " "
  where
    str :: String
    str = ".........."

data HtmlState =
  HState { fnIndex :: Int -- Footnote index
         , sectionNumber :: SectionNumber -- Section numbering
         , htmlBody :: Html -- HTML body
         , fnHtml :: Html -- Footnotes html
         , tocHtml :: Html -- Table Of Contents
           }

defaultState :: HtmlState
defaultState = HState 1 [] mempty mempty mempty

type HtmlM = State HtmlState

ihtml :: Html -> HtmlM ()
ihtml h = modify $ \s -> s { htmlBody = htmlBody s <> h }

ihtmlf :: (Html -> Html) -> HtmlM () -> HtmlM ()
ihtmlf f hm = do
  s0 <- get
  let s1 = execState hm $ s0 { htmlBody = mempty }
  put $ s1 { htmlBody = htmlBody s0 <> f (htmlBody s1) }

execHtmlM :: HtmlM a -> Html
execHtmlM hm =
  do let s = execState hm defaultState
     H.h1 "Table of contents"
     tocHtml s
     H.br
     H.a ! A.href "#footnotes" $ "Footnotes"
     H.hr
     htmlBody s
     H.hr
     H.a ! A.id "footnotes" $ H.h1 "Footnotes"
     fnHtml s

htmlSyntax :: Syntax -> HtmlM ()
htmlSyntax (Raw t) = ihtml $ toHtml t
htmlSyntax (Section n s) = do
  t <- sectionNumber <$> get
  let t' = upgradeSN n t
      a  = "s" ++ showSN t'
  ihtmlf (sectFromInt n . (H.a ! A.id (preEscapedToValue a))) $ do
    ihtml $ toHtml $ (++". ") $ showSN t'
    htmlSyntax s
  let fortoc = do
        sectiondots n
        H.a ! A.href (preEscapedToValue $ '#' : a) $ do
          toHtml $ showSN t'
          toHtml (". " :: String)
          htmlBody $ execState (htmlSyntax s) defaultState
  modify $ \st -> st { sectionNumber = t'
                     , tocHtml = tocHtml st <> H.br <> fortoc
                       }
htmlSyntax (Bold s) = ihtmlf H.b $ htmlSyntax s
htmlSyntax (Italic s) = ihtmlf H.i $ htmlSyntax s
htmlSyntax (Code b t) =
  let f = if b then H.code else H.pre
  in  ihtml $ f $ preEscapedToHtml t
htmlSyntax (URL t) = ihtml $ H.a ! A.href (preEscapedToValue t) $ toHtml t
htmlSyntax (IMG t) = ihtml $ H.img ! A.src (preEscapedToValue $ resURL t) ! A.width "50%"
htmlSyntax LaTeX = ihtml $ H.i "LaTeX"
htmlSyntax HaTeX = ihtml $ H.i "HaTeX"
htmlSyntax (Math t) = ihtml $ H.i $ toHtml t
htmlSyntax (Append s1 s2) = htmlSyntax s1 >> htmlSyntax s2
htmlSyntax Empty = return ()
htmlSyntax (Footnote x) = do
  s0 <- get
  let i = fnIndex s0
      str = '[' : (show i ++ "]")
      s1  = execState (htmlSyntax x) $ s0 { htmlBody = mempty }
      fn = H.p $ do H.a ! A.id (preEscapedToValue $ 'f' : show i) $ toHtml str
                    toHtml (" - " :: String)
                    htmlBody s1
      h  = H.a ! A.href (preEscapedToValue $ "#f" ++ show i) $ toHtml str
  modify $ \s -> s { fnIndex = fnIndex s + 1
                   , fnHtml = fnHtml s <> fn
                   , htmlBody = htmlBody s <> h
                     }
htmlSyntax (Paragraph s) = do
  ihtmlf H.p $ htmlSyntax s
  ihtml $ toHtml ("\n\n" :: String)

htmlTitle :: Day -> Html
htmlTitle d = do
  H.h1 ! A.class_ "title" $ "The HaTeX User's Guide"
  let vstr = "Version " ++ showVersion guideVersion
  H.p  ! A.class_ "centered" $ H.i $ do
    toHtml vstr
    toHtml (" using " :: String)
    H.a ! A.href "http://hackage.haskell.org/package/blaze-html" $ "blaze-html"
    toHtml ("." :: String)
  H.p  ! A.class_ "centered" $ H.i $ toHtml $ "Generated on " ++ showGregorian d ++ "."
  H.p  ! A.class_ "centered" $ toHtml $ mconcat $ intersperse ", " $ "Daniel Díaz" : contributors

htmlConfig :: Html -> IO Html
htmlConfig h = do
  d <- utctDay <$> getCurrentTime
  return $ H.docTypeHtml $ do 
    H.head $ do
      H.title "The HaTeX User's Guide"
      H.link ! A.rel "stylesheet" ! A.href "https://rawgithub.com/Daniel-Diaz/hatex-guide/master/hatex.css"
    H.body $ htmlTitle d <> H.hr <> h

createManual :: IO Html
createManual = fmap (execHtmlM . sequence_ . fmap htmlSyntax) parseSections >>= htmlConfig

backend :: IO ()
backend = do
  Prelude.putStrLn "Creating guide..."
  m <- createManual
  Prelude.putStrLn "Writing guide file..."
  let fp = outputName ".html"
  Data.Text.Lazy.IO.writeFile fp $ renderHtml m
  Prelude.putStrLn $ "Guide written in " <> fp <> "."