-- | This module contains primitive modifiers for lists and 'String's to be
-- filled or fitted to a specific length.
module Text.Layout.Table.Primitives.Basic
    ( -- * String-related tools
      spaces

      -- ** Filling
    , fillLeft'
    , fillLeft
    , fillRight
    , fillCenter'
    , fillCenter
      -- ** Fitting
    , fitRightWith
    , fitLeftWith
    , fitCenterWith
      -- ** Applying cut marks
    , applyMarkLeftWith
    , applyMarkRightWith

      -- * List-related tools
      -- ** Filling
    , fillStart'
    , fillStart
    , fillEnd
    , fillBoth'
    , fillBoth
    ) where

import Text.Layout.Table.Spec.CutMark

import Data.List

spaces :: Int -> String
spaces :: Int -> String
spaces = (Int -> Char -> String) -> Char -> Int -> String
forall a b c. (a -> b -> c) -> b -> a -> c
flip Int -> Char -> String
forall a. Int -> a -> [a]
replicate Char
' '

fillStart' :: a -> Int -> Int -> [a] -> [a]
fillStart' :: a -> Int -> Int -> [a] -> [a]
fillStart' a
x Int
i Int
lenL [a]
l = Int -> a -> [a]
forall a. Int -> a -> [a]
replicate (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
lenL) a
x [a] -> [a] -> [a]
forall a. [a] -> [a] -> [a]
++ [a]
l

fillStart :: a -> Int -> [a] -> [a]
fillStart :: a -> Int -> [a] -> [a]
fillStart a
x Int
i [a]
l = a -> Int -> Int -> [a] -> [a]
forall a. a -> Int -> Int -> [a] -> [a]
fillStart' a
x Int
i ([a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [a]
l) [a]
l

fillEnd :: a -> Int -> [a] -> [a]
fillEnd :: a -> Int -> [a] -> [a]
fillEnd a
x Int
i [a]
l = Int -> [a] -> [a]
forall a. Int -> [a] -> [a]
take Int
i ([a] -> [a]) -> [a] -> [a]
forall a b. (a -> b) -> a -> b
$ [a]
l [a] -> [a] -> [a]
forall a. [a] -> [a] -> [a]
++ a -> [a]
forall a. a -> [a]
repeat a
x

fillBoth' :: a -> Int -> Int -> [a] -> [a]
fillBoth' :: a -> Int -> Int -> [a] -> [a]
fillBoth' a
x Int
i Int
lenL [a]
l = 
    -- Puts more on the beginning if odd.
    Int -> [a]
filler Int
q [a] -> [a] -> [a]
forall a. [a] -> [a] -> [a]
++ [a]
l [a] -> [a] -> [a]
forall a. [a] -> [a] -> [a]
++ Int -> [a]
filler (Int
q Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
r)
  where
    filler :: Int -> [a]
filler  = (Int -> a -> [a]) -> a -> Int -> [a]
forall a b c. (a -> b -> c) -> b -> a -> c
flip Int -> a -> [a]
forall a. Int -> a -> [a]
replicate a
x
    missing :: Int
missing = Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
lenL
    (Int
q, Int
r)  = Int
missing Int -> Int -> (Int, Int)
forall a. Integral a => a -> a -> (a, a)
`divMod` Int
2

fillBoth :: a -> Int -> [a] -> [a]
fillBoth :: a -> Int -> [a] -> [a]
fillBoth a
x Int
i [a]
l = a -> Int -> Int -> [a] -> [a]
forall a. a -> Int -> Int -> [a] -> [a]
fillBoth' a
x Int
i ([a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [a]
l) [a]
l

fillLeft' :: Int -> Int -> String -> String
fillLeft' :: Int -> Int -> String -> String
fillLeft' = Char -> Int -> Int -> String -> String
forall a. a -> Int -> Int -> [a] -> [a]
fillStart' Char
' '

-- | Fill on the left until the 'String' has the desired length.
fillLeft :: Int -> String -> String
fillLeft :: Int -> String -> String
fillLeft = Char -> Int -> String -> String
forall a. a -> Int -> [a] -> [a]
fillStart Char
' '

-- | Fill on the right until the 'String' has the desired length.
fillRight :: Int -> String -> String
fillRight :: Int -> String -> String
fillRight = Char -> Int -> String -> String
forall a. a -> Int -> [a] -> [a]
fillEnd Char
' '

fillCenter' :: Int -> Int -> String -> String
fillCenter' :: Int -> Int -> String -> String
fillCenter' = Char -> Int -> Int -> String -> String
forall a. a -> Int -> Int -> [a] -> [a]
fillBoth' Char
' '

-- | Fill on both sides equally until the 'String' has the desired length.
fillCenter :: Int -> String -> String
fillCenter :: Int -> String -> String
fillCenter = Char -> Int -> String -> String
forall a. a -> Int -> [a] -> [a]
fillBoth Char
' '

-- | Fits to the given length by either trimming or filling it to the right.
fitRightWith :: CutMark -> Int -> String -> String
fitRightWith :: CutMark -> Int -> String -> String
fitRightWith CutMark
cms Int
i String
s =
    if String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
s Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
i
    then Int -> String -> String
fillRight Int
i String
s
    else CutMark -> String -> String
applyMarkRightWith CutMark
cms (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ Int -> String -> String
forall a. Int -> [a] -> [a]
take Int
i String
s
         --take i $ take (i - mLen) s ++ take mLen m

-- | Fits to the given length by either trimming or filling it to the right.
fitLeftWith :: CutMark -> Int -> String -> String
fitLeftWith :: CutMark -> Int -> String -> String
fitLeftWith CutMark
cms Int
i String
s =
    if Int
lenS Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
i
    then Int -> Int -> String -> String
fillLeft' Int
i Int
lenS String
s
    else CutMark -> String -> String
applyMarkLeftWith CutMark
cms (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ Int -> String -> String
forall a. Int -> [a] -> [a]
drop (Int
lenS Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
i) String
s
  where
    lenS :: Int
lenS = String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
s

-- | Fits to the given length by either trimming or filling it on both sides,
-- but when only 1 character should be trimmed it will trim left.
fitCenterWith :: CutMark -> Int -> String -> String
fitCenterWith :: CutMark -> Int -> String -> String
fitCenterWith CutMark
cms Int
i String
s             = 
    if Int
diff Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
0
    then Int -> Int -> String -> String
fillCenter' Int
i Int
lenS String
s
    else case Int -> String -> (String, String)
forall a. Int -> [a] -> ([a], [a])
splitAt Int
halfLenS String
s of
        (String
ls, String
rs) -> String -> String
addMarks (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ Int -> String -> String
forall a. Int -> [a] -> [a]
drop (Int
halfLenS Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
halfI) String
ls String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String -> String
forall a. Int -> [a] -> [a]
take (Int
halfI Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
r) String
rs
  where
    addMarks :: String -> String
addMarks   = CutMark -> String -> String
applyMarkLeftWith CutMark
cms (String -> String) -> (String -> String) -> String -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. if Int
diff Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== (-Int
1) then String -> String
forall a. a -> a
id else CutMark -> String -> String
applyMarkRightWith CutMark
cms
    diff :: Int
diff       = Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
lenS
    lenS :: Int
lenS       = String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
s
    halfLenS :: Int
halfLenS   = Int
lenS Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
2
    (Int
halfI, Int
r) = Int
i Int -> Int -> (Int, Int)
forall a. Integral a => a -> a -> (a, a)
`divMod` Int
2

-- | Applies a 'CutMark' to the left of a 'String', while preserving the length.
applyMarkLeftWith :: CutMark -> String -> String
applyMarkLeftWith :: CutMark -> String -> String
applyMarkLeftWith = (CutMark -> String) -> CutMark -> String -> String
forall a. (a -> String) -> a -> String -> String
applyMarkLeftBy CutMark -> String
leftMark

-- | Applies a 'CutMark' to the right of a 'String', while preserving the length.
applyMarkRightWith :: CutMark -> String -> String
applyMarkRightWith :: CutMark -> String -> String
applyMarkRightWith CutMark
cms = String -> String
forall a. [a] -> [a]
reverse (String -> String) -> (String -> String) -> String -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (CutMark -> String) -> CutMark -> String -> String
forall a. (a -> String) -> a -> String -> String
applyMarkLeftBy (String -> String
forall a. [a] -> [a]
reverse (String -> String) -> (CutMark -> String) -> CutMark -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CutMark -> String
rightMark) CutMark
cms (String -> String) -> (String -> String) -> String -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String
forall a. [a] -> [a]
reverse

applyMarkLeftBy :: (a -> String) -> a -> String -> String
applyMarkLeftBy :: (a -> String) -> a -> String -> String
applyMarkLeftBy a -> String
f a
v = ((Char -> Char) -> Char -> Char)
-> [Char -> Char] -> String -> String
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith (Char -> Char) -> Char -> Char
forall a b. (a -> b) -> a -> b
($) ([Char -> Char] -> String -> String)
-> [Char -> Char] -> String -> String
forall a b. (a -> b) -> a -> b
$ (Char -> Char -> Char) -> String -> [Char -> Char]
forall a b. (a -> b) -> [a] -> [b]
map Char -> Char -> Char
forall a b. a -> b -> a
const (a -> String
f a
v) [Char -> Char] -> [Char -> Char] -> [Char -> Char]
forall a. [a] -> [a] -> [a]
++ (Char -> Char) -> [Char -> Char]
forall a. a -> [a]
repeat Char -> Char
forall a. a -> a
id