module Pretty (
      Pretty,
      PprStyle(..),
      ppNil, ppStr, ppChar, ppInt, ppInteger, ppDouble,
      ppSP, pp'SP, ppLbrack, ppRbrack, ppLparen, ppRparen,
      ppSemi, ppComma,

      ppCat, ppBeside, ppBesides, ppAbove, ppAboves,
      ppNest, ppSep, ppHang, ppInterleave,
      ppShow, ppUnformatted,
      -- abstract type, to complete the interface...
      PrettyRep
) where

import CharSeq

ppNil         :: Pretty
ppSP, pp'SP, ppLbrack, ppRbrack, ppLparen, ppRparen, ppSemi, ppComma :: Pretty
ppStr         :: [Char] -> Pretty
ppChar        :: Char -> Pretty
ppInt         :: Int -> Pretty
ppInteger     :: Integer -> Pretty
ppDouble      :: Double -> Pretty
ppBeside      :: Pretty -> Pretty -> Pretty
ppBesides     :: [Pretty] -> Pretty
ppBesideSP    :: Pretty -> Pretty -> Pretty
ppCat         :: [Pretty] -> Pretty           -- i.e., ppBesidesSP
ppAbove       :: Pretty -> Pretty -> Pretty
ppAboves      :: [Pretty] -> Pretty
ppInterleave  :: Pretty -> [Pretty] -> Pretty
ppSep         :: [Pretty] -> Pretty
ppHang        :: Pretty -> Int -> Pretty -> Pretty
ppNest        :: Int -> Pretty -> Pretty
ppShow        :: Int -> Pretty -> [Char]
ppUnformatted :: Pretty -> [Char]
type Pretty = Int             -- The width to print in
	    -> Bool           -- True => vertical context
	    -> PrettyRep
data PrettyRep
  = MkPrettyRep       CSeq    -- The text
		      Int     -- No of chars in last line
		      Bool    -- True if empty object
		      Bool    -- Fits on a single line in specified width
  deriving ()
ppShow width p
 = cShow seq
 where (MkPrettyRep seq ll emp sl) = p width False
{- !!! this seems to tickle an nhc bug (works w/ hbc)
 = case (p width False) of
      MkPrettyRep seq sl ll -> cShow seq
-}

ppUnformatted p
 = cShow seq
 where (MkPrettyRep seq ll emp sl) = p 80 False
      -- ToDo: ppUnformatted doesn't do anything yet
ppNil    width is_vert = MkPrettyRep cNil 0 True (width >= 0)
			 -- Doesn't fit if width < 0, otherwise, ppNil
			 -- will make ppBesides always return True.

ppStr  s width is_vert = MkPrettyRep (cStr s) ls False (width >= ls)
			 where ls = length s
ppChar c width is_vert = MkPrettyRep (cCh c) 1 False (width >= 1)
ppInt  n               = ppStr (show n)
ppInteger n            = ppStr (show n)
ppDouble  n            = ppStr (show n)
ppSP      = ppChar ' '
pp'SP     = ppStr ", "
ppLbrack  = ppChar '['
ppRbrack  = ppChar ']'
ppLparen  = ppChar '('
ppRparen  = ppChar ')'
ppSemi    = ppChar ';'
ppComma   = ppChar ','
ppInterleave sep ps   = ppSep (pi ps)
 where
  pi []       = []
  pi [x]      = [x]
  pi (x:xs)   = (ppBeside x sep) : pi xs
ppBeside p1 p2 width is_vert
 = MkPrettyRep (seq1 `cAppend` (cIndent ll1 seq2))
	       (ll1 + ll2)
	       (emp1 `andL` emp2)
	       ((width >= 0) `andL` (sl1 `andL` sl2))
		      -- This sequence of andL's ensures that ppBeside
		      -- returns a False for sl as soon as possible.
 where
  MkPrettyRep seq1 ll1 emp1 sl1 = p1 width       False
  MkPrettyRep seq2 ll2 emp2 sl2 = p2 (width-ll1) False
      -- ToDo: if emp{1,2} then we really
      -- should be passing on "is_vert" to p{2,1}.
ppBesides [] = ppNil
ppBesides ps = foldr1 ppBeside ps
ppBesideSP p1 p2 width is_vert
 = MkPrettyRep (seq1 `cAppend` (sp `cAppend` (cIndent li seq2)))
	       (li + ll2)
	       (emp1 `andL` emp2)
	       ((width >= wi) `andL` (sl1 `andL` sl2))
 where
  MkPrettyRep seq1 ll1 emp1 sl1 = p1 width      False
  MkPrettyRep seq2 ll2 emp2 sl2 = p2 (width-li) False
  li, wi :: Int
  li = if emp1 then 0 else ll1+1
  wi = if emp1 then 0 else 1
  sp = if emp1 `orL` emp2 then cNil else (cCh ' ')
ppCat []  = ppNil
ppCat ps  = foldr1 ppBesideSP ps
ppAbove p1 p2 width is_vert
 = MkPrettyRep (seq1 `cAppend` (nl `cAppend` seq2))
	       ll2
	      -- ToDo: make ll depend on empties?
	       (emp1 `andL` emp2)
	       False
 where
  nl = if emp1 `orL` emp2 then cNil else cNL
  MkPrettyRep seq1 ll1 emp1 sl1 = p1 width True
  MkPrettyRep seq2 ll2 emp2 sl2 = p2 width True
      -- ToDo: ditto about passing is_vert if empties
ppAboves [] = ppNil
ppAboves ps = foldr1 ppAbove ps
ppNest n p width False = p width False
ppNest n p width True
 = MkPrettyRep (cIndent n seq) (ll+n) emp sl
 where
  MkPrettyRep seq ll emp sl = p (width-n) True
ppHang p1 n p2 width is_vert  -- This is a little bit stricter than it could
			      -- be made with a little more effort.
			      -- Eg the output always starts with seq1
 = if emp1 then
      p2 width is_vert
   else
   if (ll1 <= n) `orL` sl2 then       -- very ppBesideSP'ish
      -- Hang it if p1 shorter than indent or if it doesn't fit
      MkPrettyRep (seq1 `cAppend` (cCh ' ') `cAppend` (cIndent (ll1+1) seq2))
		(ll1 + 1 + ll2)
		False
		(sl1 `andL` sl2)
  else
      -- Nest it (pretty ppAbove-ish)
      MkPrettyRep (seq1 `cAppend` (cNL `cAppend` (cIndent n seq2')))
		ll2' -- ToDo: depend on empties
		False
		False
 where
  MkPrettyRep seq1 ll1 emp1 sl1 = p1 width      False
  MkPrettyRep seq2 ll2 emp2 sl2 = p2 (width-(ll1+1)) False
      -- ToDo: more "is_vert if empty" stuff

  MkPrettyRep seq2' ll2' emp2' sl2' = p2 (width-n) False      -- ToDo: True?
ppSep []  width is_vert = ppNil width is_vert
ppSep [p] width is_vert = p     width is_vert
ppSep ps  width is_vert
 = if sl then
      pr                              -- Fits on one line
   else
      ppAboves ps width is_vert       -- Takes several lines
 where
  pr@(MkPrettyRep seq ll emp sl) = ppCat ps width is_vert

{- !!! suspected on same nhc-bug grounds
 = case (ppBesides ps width is_vert) of
      pr1@(MkPrettyRep seq1 sl1 ll1) ->
	  if (sl1 && ll1 <= width) then
	      pr1                             -- Fits on one line
	  else
	      ppAboves ps width is_vert       -- Takes several lines
-}
andL :: Bool -> Bool -> Bool
andL False x = False
andL True  x = x
orL :: Bool -> Bool -> Bool
orL True  x = True
orL False x = x
data PprStyle
  = PprForUser -- Pretty-print in a way that will make sense
	       -- to the ordinary user; must be very close to Haskell
	       -- syntax, etc.
	       -- ToDo: how diff is this from what pprInterface must do?
  | PprDebug   -- Standard debugging output
  | PprShowAll -- Debugging output which leaves nothing to the imagination
  | PprInterface -- Interface generation
