{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ViewPatterns #-}
module Language.Symantic.Document.Sym where

import Data.Char (Char)
import Data.Eq (Eq(..))
import Data.Foldable (Foldable(..))
import Data.Function ((.))
import Data.Functor (Functor(..))
import Data.Int (Int, Int64)
import Data.Maybe (Maybe(..))
import Data.Semigroup (Semigroup(..))
import Data.String (String, IsString)
import Data.Text (Text)
import Prelude (Integer, fromInteger, toInteger)
import qualified Data.List as L
import qualified Data.Text as T
import qualified Data.Text.Lazy as TL

-- * Class 'Doc_Text'
class (IsString d, Semigroup d) => Doc_Text d where
	charH     :: Char    -> d -- ^ XXX: MUST NOT be '\n'
	stringH   :: String  -> d -- ^ XXX: MUST NOT contain '\n'
	textH     :: Text    -> d -- ^ XXX: MUST NOT contain '\n'
	ltextH    :: TL.Text -> d -- ^ XXX: MUST NOT contain '\n'
	replicate :: Int -> d -> d
	integer   :: Integer -> d
	default charH     :: Doc_Text (ReprOf d) => Trans d => Char -> d
	default stringH   :: Doc_Text (ReprOf d) => Trans d => String -> d
	default textH     :: Doc_Text (ReprOf d) => Trans d => Text -> d
	default ltextH    :: Doc_Text (ReprOf d) => Trans d => TL.Text -> d
	default replicate :: Doc_Text (ReprOf d) => Trans d => Int -> d -> d
	default integer   :: Doc_Text (ReprOf d) => Trans d => Integer -> d
	charH     = trans . charH
	stringH   = trans . stringH
	textH     = trans . textH
	ltextH    = trans . ltextH
	replicate = trans1 . replicate
	integer   = trans . integer
	
	empty     :: d
	eol       :: d
	space     :: d
	spaces    :: Int -> d
	int       :: Int -> d
	char      :: Char    -> d
	string    :: String  -> d
	text      :: Text    -> d
	ltext     :: TL.Text -> d
	catH      :: Foldable f => f d -> d
	catV      :: Foldable f => f d -> d
	paren     :: d -> d
	brace     :: d -> d
	bracket   :: d -> d
	bquote    :: d -> d
	dquote    :: d -> d
	fquote    :: d -> d
	squote    :: d -> d
	
	empty     = ""
	eol       = "\n"
	space     = char ' '
	spaces i  = replicate i space
	int       = integer . toInteger
	char      = \case '\n' -> eol; c -> charH c
	string    = catV . fmap stringH . lines
	text      = catV . fmap textH   . lines
	ltext     = catV . fmap ltextH  . lines
	catH      = foldr (<>) empty
	catV l    = if null l then empty else foldr1 (\a acc -> a <> eol <> acc) l
	paren   d = charH '('   <> d <> charH ')'
	brace   d = charH '{'   <> d <> charH '}'
	bracket d = charH '['   <> d <> charH ']'
	bquote  d = charH '`'   <> d <> charH '`'
	dquote  d = charH '\"'  <> d <> charH '\"'
	fquote  d =       "« "  <> d <>       " »"
	squote  d = charH '\''  <> d <> charH '\''
	-- default catH :: Doc_Text (ReprOf d) => Trans d => Foldable f => Functor f => f d -> d
	-- default catV :: Doc_Text (ReprOf d) => Trans d => Foldable f => Functor f => f d -> d
	-- catH  l = trans (catH (fmap unTrans l))
	-- catV  l = trans (catV (fmap unTrans l))

-- * Class 'Doc_Color'
class Doc_Color d where
	reverse :: d -> d
	
	-- Foreground colors
	-- Dull
	black   :: d -> d
	red     :: d -> d
	green   :: d -> d
	yellow  :: d -> d
	blue    :: d -> d
	magenta :: d -> d
	cyan    :: d -> d
	white   :: d -> d
	
	-- Vivid
	blacker   :: d -> d
	redder    :: d -> d
	greener   :: d -> d
	yellower  :: d -> d
	bluer     :: d -> d
	magentaer :: d -> d
	cyaner    :: d -> d
	whiter    :: d -> d
	
	-- Background colors
	-- Dull
	onBlack   :: d -> d
	onRed     :: d -> d
	onGreen   :: d -> d
	onYellow  :: d -> d
	onBlue    :: d -> d
	onMagenta :: d -> d
	onCyan    :: d -> d
	onWhite   :: d -> d
	
	-- Vivid
	onBlacker   :: d -> d
	onRedder    :: d -> d
	onGreener   :: d -> d
	onYellower  :: d -> d
	onBluer     :: d -> d
	onMagentaer :: d -> d
	onCyaner    :: d -> d
	onWhiter    :: d -> d
	
	default reverse     :: Doc_Color (ReprOf d) => Trans d => d -> d
	default black       :: Doc_Color (ReprOf d) => Trans d => d -> d
	default red         :: Doc_Color (ReprOf d) => Trans d => d -> d
	default green       :: Doc_Color (ReprOf d) => Trans d => d -> d
	default yellow      :: Doc_Color (ReprOf d) => Trans d => d -> d
	default blue        :: Doc_Color (ReprOf d) => Trans d => d -> d
	default magenta     :: Doc_Color (ReprOf d) => Trans d => d -> d
	default cyan        :: Doc_Color (ReprOf d) => Trans d => d -> d
	default white       :: Doc_Color (ReprOf d) => Trans d => d -> d
	default blacker     :: Doc_Color (ReprOf d) => Trans d => d -> d
	default redder      :: Doc_Color (ReprOf d) => Trans d => d -> d
	default greener     :: Doc_Color (ReprOf d) => Trans d => d -> d
	default yellower    :: Doc_Color (ReprOf d) => Trans d => d -> d
	default bluer       :: Doc_Color (ReprOf d) => Trans d => d -> d
	default magentaer   :: Doc_Color (ReprOf d) => Trans d => d -> d
	default cyaner      :: Doc_Color (ReprOf d) => Trans d => d -> d
	default whiter      :: Doc_Color (ReprOf d) => Trans d => d -> d
	default onBlack     :: Doc_Color (ReprOf d) => Trans d => d -> d
	default onRed       :: Doc_Color (ReprOf d) => Trans d => d -> d
	default onGreen     :: Doc_Color (ReprOf d) => Trans d => d -> d
	default onYellow    :: Doc_Color (ReprOf d) => Trans d => d -> d
	default onBlue      :: Doc_Color (ReprOf d) => Trans d => d -> d
	default onMagenta   :: Doc_Color (ReprOf d) => Trans d => d -> d
	default onCyan      :: Doc_Color (ReprOf d) => Trans d => d -> d
	default onWhite     :: Doc_Color (ReprOf d) => Trans d => d -> d
	default onBlacker   :: Doc_Color (ReprOf d) => Trans d => d -> d
	default onRedder    :: Doc_Color (ReprOf d) => Trans d => d -> d
	default onGreener   :: Doc_Color (ReprOf d) => Trans d => d -> d
	default onYellower  :: Doc_Color (ReprOf d) => Trans d => d -> d
	default onBluer     :: Doc_Color (ReprOf d) => Trans d => d -> d
	default onMagentaer :: Doc_Color (ReprOf d) => Trans d => d -> d
	default onCyaner    :: Doc_Color (ReprOf d) => Trans d => d -> d
	default onWhiter    :: Doc_Color (ReprOf d) => Trans d => d -> d
	
	reverse     = trans1 reverse
	black       = trans1 black
	red         = trans1 red
	green       = trans1 green
	yellow      = trans1 yellow
	blue        = trans1 blue
	magenta     = trans1 magenta
	cyan        = trans1 cyan
	white       = trans1 white
	blacker     = trans1 blacker
	redder      = trans1 redder
	greener     = trans1 greener
	yellower    = trans1 yellower
	bluer       = trans1 bluer
	magentaer   = trans1 magentaer
	cyaner      = trans1 cyaner
	whiter      = trans1 whiter
	onBlack     = trans1 onBlack
	onRed       = trans1 onRed
	onGreen     = trans1 onGreen
	onYellow    = trans1 onYellow
	onBlue      = trans1 onBlue
	onMagenta   = trans1 onMagenta
	onCyan      = trans1 onCyan
	onWhite     = trans1 onWhite
	onBlacker   = trans1 onBlacker
	onRedder    = trans1 onRedder
	onGreener   = trans1 onGreener
	onYellower  = trans1 onYellower
	onBluer     = trans1 onBluer
	onMagentaer = trans1 onMagentaer
	onCyaner    = trans1 onCyaner
	onWhiter    = trans1 onWhiter

-- * Class 'Doc_Decoration'
class Doc_Decoration d where
	bold      :: d -> d
	underline :: d -> d
	italic    :: d -> d
	default bold      :: Doc_Decoration (ReprOf d) => Trans d => d -> d
	default underline :: Doc_Decoration (ReprOf d) => Trans d => d -> d
	default italic    :: Doc_Decoration (ReprOf d) => Trans d => d -> d
	bold      = trans1 bold
	underline = trans1 underline
	italic    = trans1 italic

-- * Class 'Trans'
class Trans tr where
	-- | Return the underlying @tr@ of the transformer.
	type ReprOf tr :: *
	
	-- | Lift a tr to the transformer's.
	trans :: ReprOf tr -> tr
	-- | Unlift a tr from the transformer's.
	unTrans :: tr -> ReprOf tr
	
	-- | Identity transformation for a unary symantic method.
	trans1 :: (ReprOf tr -> ReprOf tr) -> (tr -> tr)
	trans1 f = trans . f . unTrans
	
	-- | Identity transformation for a binary symantic method.
	trans2
	 :: (ReprOf tr -> ReprOf tr -> ReprOf tr)
	 -> (tr -> tr -> tr)
	trans2 f t1 t2 = trans (f (unTrans t1) (unTrans t2))
	
	-- | Identity transformation for a ternary symantic method.
	trans3
	 :: (ReprOf tr -> ReprOf tr -> ReprOf tr -> ReprOf tr)
	 -> (tr -> tr -> tr -> tr)
	trans3 f t1 t2 t3 = trans (f (unTrans t1) (unTrans t2) (unTrans t3))


-- * Class 'SplitOnCharWithEmpty'
class SplitOnCharWithEmpty t where
	splitOnCharWithEmpty :: Char -> t -> [t]
instance SplitOnCharWithEmpty Text where
	splitOnCharWithEmpty sep t =
		case T.break (== sep) t of
		 (chunk, T.uncons -> Just (_, rest)) -> chunk : splitOnCharWithEmpty sep rest
		 (chunk, _) -> [chunk]
instance SplitOnCharWithEmpty TL.Text where
	splitOnCharWithEmpty sep t =
		case TL.break (== sep) t of
		 (chunk, TL.uncons -> Just (_, rest)) -> chunk : splitOnCharWithEmpty sep rest
		 (chunk, _) -> [chunk]
instance SplitOnCharWithEmpty String where
	splitOnCharWithEmpty sep t =
		case L.break (== sep) t of
		 (chunk, _:rest) -> chunk : splitOnCharWithEmpty sep rest
		 (chunk, []) -> [chunk]

lines :: SplitOnCharWithEmpty t => t -> [t]
lines = splitOnCharWithEmpty '\n'

int64OfInt :: Int -> Int64
int64OfInt = fromInteger . toInteger


{-
-- * Class 'SplitOnChar'

class SplitOnChar t where
	splitOnChar :: Char -> t -> [t]
instance SplitOnChar Text where
	splitOnChar sep t =
		case Text.uncons t of
		 Nothing -> []
		 Just (x, xs) ->
			if x == sep
			then splitOnChar sep xs
			else
				let (chunk, rest) = Text.break (== sep) t in
				chunk:splitOnChar sep rest
instance SplitOnChar String where
	splitOnChar sep t =
		case t of
		 [] -> []
		 x:xs ->
			if x == sep
			then splitOnChar sep xs
			else
				let (chunk, rest) = List.break (== sep) t in
				chunk:splitOnChar sep rest
-}