{-# LANGUAGE ScopedTypeVariables, TypeSynonymInstances, FlexibleContexts, FlexibleInstances, GeneralizedNewtypeDeriving, ViewPatterns #-}
module Text.PrettyPrint.Compact.Core(Layout(..),Document(..),Doc) where

import Data.List (intercalate,sort,groupBy)
import Data.Function (on)
import Data.Monoid
import Data.Sequence (singleton, Seq, viewl, viewr, ViewL(..), ViewR(..))
import qualified Data.Sequence as S



newtype L = L (Seq String) -- non-empty sequence
  deriving (Eq,Ord)

instance Monoid L where
   mempty = L (singleton "")
   L (viewr -> xs :> x) `mappend` L (viewl -> y :< ys) = L (xs <> singleton (x ++ y) <> fmap (indent ++) ys)
      where n = length x
            indent = Prelude.replicate n ' '

newtype N = N {fromN :: String}
instance Monoid N where
  mempty = N ""
  mappend (N x) (N y) = N (x ++ "\n" ++ y)
instance Layout L where
   render (L xs) = fromN $ foldMap N xs
   text = L . singleton
   flush (L xs) = L (xs <> singleton "")


class Monoid d => Layout d where
  text :: String -> d
  flush :: d -> d
  render :: d -> String

class Layout d => Document d where
  (<|>) :: d -> d -> d

data M = M {height    :: Int,
            lastWidth :: Int,
            maxWidth  :: Int
            }
  deriving (Show,Eq,Ord)

instance Monoid M where
  mempty = text ""
  a `mappend` b =
    M {maxWidth = max (maxWidth a) (maxWidth b + lastWidth a),
       height = height a + height b,
       lastWidth = lastWidth a + lastWidth b}

instance Layout M where
  text s = M {height = 0, maxWidth = length s, lastWidth = length s}
  flush a = M {maxWidth = maxWidth a,
               height = height a + 1,
               lastWidth = 0}
  render = error "don't use this render"

fits :: M -> Bool
fits x = maxWidth x <= 80

class Poset a where
  () :: a -> a -> Bool


instance Poset M where
  M c1 l1 s1  M c2 l2 s2 = c1 <= c2 && l1 <= l2 && s1 <= s2



merge :: Ord a => [a] -> [a] -> [a]
merge [] xs = xs
merge xs [] = xs
merge (x:xs) (y:ys)
  | x <= y = x:merge xs (y:ys)
  | otherwise = y:merge (x:xs) ys


mergeAll :: Ord a => [[a]] -> [a]
mergeAll [] = []
mergeAll (x:xs) = merge x (mergeAll xs)

bests :: forall a. (Ord a, Poset a) => [[a]] -> [a]
bests = pareto' [] . mergeAll

pareto' :: Poset a => [a] -> [a] -> [a]
pareto' acc [] = Prelude.reverse acc
pareto' acc (x:xs) = if any ( x) acc
                       then pareto' acc xs
                       else pareto' (x:acc) xs


newtype Doc = MkDoc [(M,L)]

quasifilter :: (a -> Bool) -> [a] -> [a]
quasifilter p xs = let fxs = filter p xs in if null fxs then take 1 xs else fxs

instance Monoid Doc where
  mempty = text ""
  MkDoc xs `mappend` MkDoc ys = MkDoc $ bests [ quasifilter (fits . fst) [x <> y | y <- ys] | x <- xs]

instance Layout Doc where
  flush (MkDoc xs) = MkDoc $ bests $ map sort $ groupBy ((==) `on` (height . fst)) $ (map flush xs)
  -- flush xs = pareto' [] $ sort $ (map flush xs)
  text s = MkDoc [text s]
  render (MkDoc []) = error "No suitable layout found."
  render (MkDoc (x:_)) = render x

instance Document Doc where
  MkDoc m1 <|> MkDoc m2 = MkDoc (bests [m1,m2])


--  (a,b) `mappend` (c,d) = (a<>c ,b<>d)

instance (Layout a, Layout b) => Layout (a,b) where
  text s = (text s, text s)
  flush (a,b) = (flush a, flush b)
  render = render . snd

instance (Document a, Document b) => Document (a,b) where
  (a,b) <|> (c,d) = (a<|>c,b<|>d)

instance (Poset a) => Poset (a,b) where
  (a,_)  (b,_) = a  b