{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ViewPatterns #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
-- | Render a TCT source file in HTML5.
module Language.TCT.HTML5.Source where

import Control.Applicative (Applicative(..))
import Control.Monad (Monad(..), forM_, mapM)
import Data.Bool
import Data.Eq (Eq(..))
import Data.Foldable (Foldable(..))
import Data.Function (($), (.))
import Data.Int (Int)
import Data.Maybe (Maybe(..))
import Data.Monoid (Monoid(..))
import Data.Ord (Ord(..))
import Data.Semigroup (Semigroup(..))
import Data.Sequence (ViewL(..))
import Data.String (IsString(..))
import Data.Text (Text)
import Prelude (Num(..), undefined)
import Text.Blaze ((!))
import Text.Blaze.Html (Html)
import Text.Show (Show(..))
import qualified Control.Monad.Trans.State as S
import qualified Data.List as L
import qualified Data.Sequence as Seq
import qualified Data.Text as Text
import qualified Text.Blaze.Html5 as H
import qualified Text.Blaze.Html5.Attributes  as HA

import Language.TCT.Tree
import Language.TCT.Token
import Language.TCT.Elem

{-
class HTML5able a where
	html5Of :: a -> Html

class Textable a where
	textOf :: a -> Html
instance HTML5able TCT where
-}
instance Semigroup H.AttributeValue where
	(<>) = mappend

-- * Class 'Attributable'
class AttrValue a where
	attrValue :: a -> H.AttributeValue
instance AttrValue Text where
	attrValue = fromString . Text.unpack
instance AttrValue Int where
	attrValue = fromString . show
instance AttrValue Group where
	attrValue = fromString . show

whenJust :: Applicative m => Maybe a -> (a -> m ()) -> m ()
whenJust Nothing _f = pure ()
whenJust (Just a) f = f a

html5 :: Trees (Cell Key) (Cell Token) -> Html
html5 tct = do
	H.docType
	H.html $ do
		H.head $ do
			H.meta ! HA.httpEquiv "Content-Type"
			       ! HA.content "text/html; charset=UTF-8"
			whenJust (titleTCT tct) $ \(unCell -> t) ->
				H.title $ H.toMarkup $ L.head $ Text.lines (t_Token t) <> [""]
			-- link ! rel "Chapter" ! title "SomeTitle">
			H.link ! HA.rel "stylesheet"
			       ! HA.type_ "text/css"
			       ! HA.href "style/tct-html5-source.css"
		H.body $ do
			H.a ! HA.id ("line-1") $ return ()
			forM_ (treePosLastCell tct) $ h_TreeCell

titleTCT :: Trees (Cell Key) (Cell a) -> Maybe (Cell a)
titleTCT tct =
	L.find (\case
	 TreeN (unCell -> KeySection{}) _ts -> True
	 _ -> False) tct >>=
	\case
	 TreeN (unCell -> KeySection _lvl) (Seq.viewl -> Tree0 title:<_) -> Just title
	 _ -> Nothing

h_Text :: Text -> Html
h_Text = H.toMarkup

h_Spaces :: Int -> Html
h_Spaces 0  = return ()
h_Spaces sp = H.span $ h_Text $ Text.replicate sp " "

h_TreeCell :: Tree (Pos,Cell Key) (Pos,Cell Token) -> Html
h_TreeCell (TreeN c@(_, unCell -> KeySection lvl) ts) = do
	h_IndentCell c
	H.section $ do
		H.span ! HA.class_ "section-title" $ do
			H.span $ h_Text $ Text.replicate lvl "#" <> " "
			case Seq.viewl ts of
			 Tree0 (_,Cell posTitle _ title) :< _ -> h lvl $ h_IndentToken posTitle title
			 _ -> return ()
		forM_ (case Seq.viewl ts of {Tree0{} :< ts' -> ts'; _ -> ts}) $ h_TreeCell
	where
	h 1 = H.h1
	h 2 = H.h2
	h 3 = H.h3
	h 4 = H.h4
	h 5 = H.h5
	h 6 = H.h6
	h n | n > 0 = H.span ! HA.class_ ("h h"<>attrValue n)
	h _ = undefined
h_TreeCell (Tree0 c@(_,cell)) = do
	h_IndentCell c
	h_CellToken cell
h_TreeCell (TreeN c@(_,cell) cs) = do
	h_IndentCell c
	h_CellKey cell cs

h_IndentCell :: (Pos,Cell a) -> Html
h_IndentCell ((lineLast,colLast),posCell -> (line,col))
 | lineLast < line = do
	forM_ [lineLast+1..line] $ \lnum -> do
		H.toMarkup '\n'
		H.a ! HA.id ("line-"<>attrValue lnum) $ return ()
	H.toMarkup $ Text.replicate (col - 1) " "
 | lineLast == line
 && colLast <= col = H.toMarkup $ Text.replicate (col - colLast) " "
 | otherwise = undefined

h_CellKey :: Cell Key -> Trees (Pos,Cell Key) (Pos,Cell Token) -> Html
h_CellKey (Cell _pos _posEnd key) cells = do
	case key of
	 KeyColon n wh -> h_Key n wh ":" "colon"
	 KeyGreat n wh -> h_Key n wh ">" "great"
	 KeyEqual n wh -> h_Key n wh "=" "equal"
	 KeyBar   n wh -> h_Key n wh "|" "bar"
	 KeyDash -> do
		H.toMarkup ("- "::Text)
		forM_ cells h_TreeCell
	 KeyLower name attrs -> do
		H.span ! HA.class_ (mconcat ["key key-lower"," key-name-",attrValue name]) $ do
			H.span ! HA.class_ "key-mark" $ H.toMarkup '<'
			H.span ! HA.class_ "key-name" $ H.toMarkup name
			h_Attrs attrs
			forM_ cells h_TreeCell
	where
	h_Key :: Text -> White -> Text -> H.AttributeValue -> Html
	h_Key name wh mark cl = do
		-- h_Spaces $ colPos posEnd - (colPos pos + Text.length name + 1)
		H.span ! HA.class_ (mconcat ["key key-",cl," key-name-",attrValue name]) $ do
			H.span ! HA.class_ "key-name" $ H.toMarkup name
			H.toMarkup wh
			H.span ! HA.class_ "key-mark" $ H.toMarkup mark
			forM_ cells h_TreeCell

h_CellToken :: Cell Token -> Html
h_CellToken (Cell pos _posEnd mrk) =
	h_IndentToken pos mrk

h_IndentToken :: Pos -> Token -> Html
h_IndentToken pos mrk = go mrk `S.evalState` linePos pos
	where
	indent = Text.replicate (columnPos pos - 1) " "
	go :: Token -> S.State Int Html
	go (TokenPlain txt) = do
		lin <- S.get
		let lines = Text.splitOn "\n" txt
		let lnums = H.toMarkup :
			 [ \line -> do
				H.toMarkup '\n'
				H.a ! HA.id ("line-"<>attrValue lnum) $ return ()
				H.toMarkup indent
				H.toMarkup line
			 | lnum <- [lin+1..]
			 ]
		S.put (lin - 1 + L.length lines)
		return $ mconcat $ L.zipWith ($) lnums lines
	go (TokenTag v) = do
		return $
			H.span ! HA.class_ "tag" $ do
				H.span ! HA.class_ "tag-open" $ H.toMarkup '#'
				H.toMarkup v
	go (TokenEscape c) = return $ H.toMarkup ['\\',c]
	go (TokenLink lnk) = do
		return $
			H.a ! HA.href (attrValue lnk) $
				H.toMarkup lnk
	go (TokenGroup (GroupElem name attrs) t) = do
		h <- go t
		return $ do
			let cl = mconcat ["group-GroupElem", " group-elem-", attrValue name]
			H.span ! HA.class_ cl $ do
				H.span ! HA.class_ "group-open"    $ H.toMarkup o
				H.span ! HA.class_ "group-content" $ h
				H.span ! HA.class_ "group-close"   $ H.toMarkup c
		where
		h_name = H.span ! HA.class_ "elem-name" $ H.toMarkup name
		o,c :: Html
		(o,c) =
			case t of
			 Tokens ts | Seq.null ts ->
				( "<"<>h_name<>h_Attrs attrs<>"/>"
				, "" )
			 _ ->
				( "<"<>h_name<>h_Attrs attrs<>">"
				, "</"<>h_name<>">" )
	go (TokenGroup grp t) = do
		h <- go t
		return $ do
			let (o,c) = groupBorders grp t
			H.span ! HA.class_ (mconcat ["group-", attrValue grp]) $ do
				H.span ! HA.class_ "group-open"    $ H.toMarkup o
				H.span ! HA.class_ "group-content" $ h
				H.span ! HA.class_ "group-close"   $ H.toMarkup c
	go (Tokens ts) = do
		ts' <- go`mapM`ts
		return $ foldr (<>) mempty ts'

h_Attrs :: Attrs -> Html
h_Attrs = foldMap h_Attr

h_Attr :: (Text,Attr) -> Html
h_Attr (attr_white,Attr{..}) = do
	H.toMarkup attr_white
	H.span ! HA.class_ "attr-name" $
		H.toMarkup attr_name
	H.toMarkup attr_open
	H.span ! HA.class_ "attr-value" $
		H.toMarkup attr_value
	H.toMarkup attr_close

t_Token :: Token -> Text
t_Token (TokenPlain t) = t
t_Token (TokenTag v) = "#"<>v
t_Token (TokenEscape c) = Text.pack ['\\',c]
t_Token (TokenLink lnk) = lnk
t_Token (TokenGroup grp t) = o<>t_Token t<>c
	where (o,c) = groupBorders grp t
t_Token (Tokens ts) = foldMap t_Token ts

t_Value :: Text -> Text
t_Value v = v

treePosLastCell :: Trees (Cell k) (Cell a) -> Trees (Pos,Cell k) (Pos,Cell a)
treePosLastCell t = S.evalState (go`mapM`t) (1,1)
	where
	go :: Tree (Cell k) (Cell a) ->
	      S.State (Int, Int) (Tree (Pos, Cell k) (Pos, Cell a))
	go (Tree0 cell) = do
		lastPos <- S.get
		S.put $ posEndCell cell
		return $ Tree0 (lastPos,cell)
	go (TreeN cell ts) = do
		lastPos <- S.get
		S.put $ posEndCell cell
		ts' <- go`mapM`ts
		return $ TreeN (lastPos,cell) ts'