{-# LANGUAGE NoMonomorphismRestriction #-}

module Text.HTML.Moe2.Renderer
(
    render
  , render'
)
where

import Text.HTML.Moe2.Element
import Text.HTML.Moe2.Attribute
import Text.HTML.Moe2.Utils
import Text.HTML.Moe2.Type hiding (name, value)
import qualified Text.HTML.Moe2.Type as T

import Prelude hiding ((/), (-), head, (>), (.), concat, concatMap, (+))
import qualified Prelude as P

import MPS.Env ((>), (.), times, belongs_to, reject)

import Control.Monad.Writer (execWriter)

import qualified Data.ByteString.Char8 as B
import Data.DList (toList)
import Data.List (intersperse)


render :: MoeUnit -> String
render = render' > B.unpack

render' :: MoeUnit -> B.ByteString
render' = execWriter > toList > map render_element > intercalate new_line > to_bs

_indent_space :: Int
_indent_space = 2

new_line :: Internal
new_line = pack "\n"

space :: Internal
space = pack " "

_indent :: Int -> Internal
_indent n = (n * _indent_space).times space.concat

render_element' :: Int -> Element -> Internal
render_element' _ (Raw x)   = x
render_element' _ (Pre x)   = x
render_element' n (Prim x)  = _indent n + x
render_element' n (Data x)  = _indent n + x
render_element' n (Attributes xs e) = 
  let xs' = e.attributes.reject (key > belongs_to (xs.map key)) ++ xs
  in
  render_element' n e {attributes = xs'}
  
render_element' n x 
  | x.self_close =
    [ _indent n
    , "<".pack
    , x.T.name
    , x.attributes.map render_attribute. join_attribute
    , " />".pack
    ]
    .concat
    
  | otherwise = 
    [ self_indent
    , "<".pack
    , x.T.name
    , x.attributes.map render_attribute. join_attribute
    , ">".pack
    , inner_elements
    , "</".pack
    , x.T.name
    , ">".pack
    ]
    .concat
    
  where
    self_indent = if x.indent then _indent n else "".pack
    join_attribute xs = xs.map (pack " " `append`) .concat
    inner_elements = 
      if x.elements.null
        then "".pack
        else 
          [ new_line
          , x.elements.map (render_element' (n P.+ 1)) .intersperse new_line .concat
          , new_line
          , self_indent
          ]
          .concat

      



render_element :: Element -> Internal
render_element = render_element' 0

render_attribute :: Attribute -> Internal
render_attribute x = 
  [x.key, pack "=", pack "\"", x.T.value, pack "\""].concat