-- | This module provides functions for vertical alginment of columns.
module Text.Layout.Table.Vertical
    ( -- * Vertical padding of columns
      vPad
    , vPadAll
    , -- * Converting columns to rows with positioning
      colsAsRowsAll
    , colsAsRows
    ) where

import Data.List

import Text.Layout.Table.Spec.Position
import Text.Layout.Table.Spec.Util
import Text.Layout.Table.Primitives.Basic

{- | Merges multiple columns together to a valid grid without holes. For example:

>>> colsAsRowsAll top [justifyText 10 "This text will not fit on one line.", ["42", "23"]]
[[Just "This  text",Just "42"],[Just "will   not",Just "23"],[Just "fit on one",Nothing],[Just "line.",Nothing]]

The result is intended to be used with a grid layout function like 'Text.Layout.Table.grid'.
-}
colsAsRowsAll :: Position V -> [Col a] -> [Row (Maybe a)]
colsAsRowsAll :: forall a. Position V -> [Col a] -> [Row (Maybe a)]
colsAsRowsAll Position V
p = [[Maybe a]] -> [[Maybe a]]
forall a. [[a]] -> [[a]]
transpose ([[Maybe a]] -> [[Maybe a]])
-> ([[a]] -> [[Maybe a]]) -> [[a]] -> [[Maybe a]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe a -> Position V -> [[Maybe a]] -> [[Maybe a]]
forall a. a -> Position V -> [Col a] -> [Col a]
vPadAll Maybe a
forall a. Maybe a
Nothing Position V
p ([[Maybe a]] -> [[Maybe a]])
-> ([[a]] -> [[Maybe a]]) -> [[a]] -> [[Maybe a]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([a] -> [Maybe a]) -> [[a]] -> [[Maybe a]]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((a -> Maybe a) -> [a] -> [Maybe a]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> Maybe a
forall a. a -> Maybe a
Just)

{- | Works like 'colsAsRowsAll' but every position can be specified on its
   own:

>>> colsAsRows [top, center, bottom] [["a1"], ["b1", "b2", "b3"], ["c3"]]
[[Just "a1",Just "b1",Nothing],[Nothing,Just "b2",Nothing],[Nothing,Just "b3",Just "c3"]]
-}
colsAsRows :: [Position V] -> [Col a] -> [Row (Maybe a)]
colsAsRows :: forall a. [Position V] -> [Col a] -> [Row (Maybe a)]
colsAsRows [Position V]
ps = [[Maybe a]] -> [[Maybe a]]
forall a. [[a]] -> [[a]]
transpose ([[Maybe a]] -> [[Maybe a]])
-> ([[a]] -> [[Maybe a]]) -> [[a]] -> [[Maybe a]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe a -> [Position V] -> [[Maybe a]] -> [[Maybe a]]
forall a. a -> [Position V] -> [Col a] -> [Col a]
vPad Maybe a
forall a. Maybe a
Nothing [Position V]
ps ([[Maybe a]] -> [[Maybe a]])
-> ([[a]] -> [[Maybe a]]) -> [[a]] -> [[Maybe a]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([a] -> [Maybe a]) -> [[a]] -> [[Maybe a]]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((a -> Maybe a) -> [a] -> [Maybe a]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> Maybe a
forall a. a -> Maybe a
Just)

-- | Fill all columns to the same length by aligning at the given position.
vPadAll :: a -> Position V -> [Col a] -> [Col a]
vPadAll :: forall a. a -> Position V -> [Col a] -> [Col a]
vPadAll a
x = a -> [Position V] -> [Col a] -> [Col a]
forall a. a -> [Position V] -> [Col a] -> [Col a]
vPad a
x ([Position V] -> [Col a] -> [Col a])
-> (Position V -> [Position V]) -> Position V -> [Col a] -> [Col a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Position V -> [Position V]
forall a. a -> [a]
repeat

-- | Fill all columns to the same length by aligning at the given positions.
vPad :: a -> [Position V] -> [Col a] -> [Col a]
vPad :: forall a. a -> [Position V] -> [Col a] -> [Col a]
vPad a
x [Position V]
vs [Col a]
l = (Position V -> Col a -> Col a)
-> [Position V] -> [Col a] -> [Col a]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith Position V -> Col a -> Col a
forall {orientation}. Position orientation -> Col a -> Col a
fillToMax [Position V]
vs [Col a]
l
  where
    fillToMax :: Position orientation -> Col a -> Col a
fillToMax Position orientation
vPos = Position orientation -> Int -> Col a -> Col a
forall {orientation}. Position orientation -> Int -> Col a -> Col a
fillTo Position orientation
vPos (Int -> Col a -> Col a) -> Int -> Col a -> Col a
forall a b. (a -> b) -> a -> b
$ [Int] -> Int
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
maximum ([Int] -> Int) -> [Int] -> Int
forall a b. (a -> b) -> a -> b
$ Int
0 Int -> [Int] -> [Int]
forall a. a -> [a] -> [a]
: (Col a -> Int) -> [Col a] -> [Int]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Col a -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Col a]
l
    fillTo :: Position orientation -> Int -> Col a -> Col a
fillTo Position orientation
vPos    = let f :: a -> Int -> [a] -> [a]
f = case Position orientation
vPos of
                            Position orientation
Start  -> a -> Int -> [a] -> [a]
forall a. a -> Int -> [a] -> [a]
fillEnd
                            Position orientation
Center -> a -> Int -> [a] -> [a]
forall a. a -> Int -> [a] -> [a]
fillBoth
                            Position orientation
End    -> a -> Int -> [a] -> [a]
forall a. a -> Int -> [a] -> [a]
fillStart
                     in a -> Int -> Col a -> Col a
forall a. a -> Int -> [a] -> [a]
f a
x