{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
module Textphile.TCT.Cell
 ( module Textphile.TCT.Cell
 , XML.FileRange(..)
 , XML.NoSource(..)
 , XML.Sourced(..)
 ) where

import Control.Applicative (Applicative(..))
import Data.Bool
import Data.Default.Class (Default(..))
import Data.Eq (Eq(..))
import Data.Function (($), (.))
import Data.List.NonEmpty (NonEmpty(..))
import Data.Ord (Ord(..))
import Data.Semigroup (Semigroup(..))
import Symantic.XML (Sourced(..))
import Prelude (Bounded(..), (-), Int, fromIntegral, Integral, Num)
import Text.Show (Show(..), showChar, showString)
import qualified Data.Text as T
import qualified Data.Text.Lazy as TL
import qualified Symantic.XML as XML
import qualified Text.Megaparsec as P

import Textphile.TCT.Debug

{-
-- * Type 'Pos'
-- | Absolute text file position.
type Pos = XML.FileRange XML.LineColumn
instance Semigroup Pos where
	Pos lx cx <> Pos ly cy =
		Pos (lx+ly) (cx+cy)
instance Monoid Pos where
	mempty  = Pos 0 0
	mappend = (<>)

pos1 :: Pos
pos1 = def
-}

-- * Type 'Cell'
type Cell = XML.Sourced Location
{-
instance (FromPad a, Semigroup a, Monoid a) => Monoid (Cell a) where
	mempty  = cell0 mempty
	mappend = (<>)
-}

cell0 :: a -> Cell a
cell0 = XML.Sourced (XML.FileRange "" pos0 pos0 :| [])
	where
	pos0 = LineColumn num0 num0
	num0 = numMax<>numMax<>pos1<>pos1
	numMax = P.mkPos maxBound

-- ** Type 'Span'
type Span = XML.FileRange LineColumn

-- ** Type 'Location'
type Location = XML.FileSource LineColumn

instance (FromPad a, Semigroup a) => Semigroup (Sourced (XML.FileSource LineColumn) a) where
	(<>)
	 (Sourced rx@(XML.FileRange xf xb xe :|  xs) x)
	 (Sourced    (XML.FileRange yf yb ye :| _ys) y)
	 | xf == yf  = Sourced (XML.FileRange xf xb ye :| xs) $
	                       x<>fromPad (l,c)<>y
	 | otherwise = Sourced rx (x<>y)
		where
		l = lineInt yb - lineInt xe
		c = colInt  yb - colInt (if l <= 0 then xe else xb)

-- ** Class 'FromPad'
class FromPad a where
	fromPad :: (Int, Int) -> a
instance FromPad T.Text where
	fromPad (lineNum, colNum) =
		T.replicate lineNum "\n" <>
		T.replicate colNum " "
instance FromPad TL.Text where
	fromPad (lineNum, colNum) =
		TL.replicate (fromIntegral lineNum) "\n" <>
		TL.replicate (fromIntegral colNum) " "
instance FromPad XML.EscapedText where
	fromPad = XML.EscapedText . pure . fromPad
instance FromPad XML.Escaped where
	fromPad = XML.EscapedPlain . fromPad
{-
instance (FromPad a, Semigroup a, Monoid a) => Monoid (Sourced a) where
	mempty  = sourced0 mempty
	mappend = (<>)
-}

-- ** Type 'LineColumn'
-- | Absolute text file position.
data LineColumn = LineColumn
 { lineNum :: {-# UNPACK #-} !P.Pos
 , colNum  :: {-# UNPACK #-} !P.Pos
 } deriving (Eq, Ord)
num :: (Integral a, Num a) => a -> P.Pos
num = P.mkPos . fromIntegral
pos1 :: P.Pos
pos1 = P.pos1
instance Default LineColumn where
	def = LineColumn P.pos1 P.pos1
instance Show LineColumn where
	showsPrec _p lc =
		showsPrec 11 (lineInt lc) .
		showChar ':' .
		showsPrec 11 (colInt lc)
instance Pretty LineColumn

-- ** Type 'LineInt'
type LineInt = Int
lineInt :: LineColumn -> Int
lineInt = P.unPos . lineNum

-- ** Type 'ColInt'
type ColInt = Int
colInt :: LineColumn -> Int
colInt = P.unPos . colNum
instance Default (XML.FileRange LineColumn) where
	def = XML.FileRange "" def def
instance Show (XML.FileRange LineColumn) where
	showsPrec _p XML.FileRange{..} =
		showString fileRange_file .
		showChar '#' . showsPrec 10 fileRange_begin .
		showChar '-' . showsPrec 10 fileRange_end