-- | Common definitions (and support functions) for rendering a
-- 'KVITable'.

module Data.KVITable.Render
  (
    RenderConfig(..)
  , defaultRenderConfig
  , sortWithNums
  )
where

import           Data.KVITable
import qualified Data.List as L
import           Data.Text ( Text )
import qualified Data.Text as T

-- | Returns the default rendering configuration, to be used with a
-- format-specific @render@ call.

defaultRenderConfig :: RenderConfig
defaultRenderConfig :: RenderConfig
defaultRenderConfig = RenderConfig :: Bool
-> Bool
-> Bool
-> Bool
-> Maybe Key
-> Bool
-> [Key]
-> Maybe Key
-> RenderConfig
RenderConfig
  { hideBlankRows :: Bool
hideBlankRows = Bool
True
  , hideBlankCols :: Bool
hideBlankCols = Bool
True
  , equisizedCols :: Bool
equisizedCols = Bool
True
  , sortKeyVals :: Bool
sortKeyVals   = Bool
False
  , colStackAt :: Maybe Key
colStackAt    = Maybe Key
forall a. Maybe a
Nothing
  , rowRepeat :: Bool
rowRepeat     = Bool
True
  , rowGroup :: [Key]
rowGroup      = []
  , caption :: Maybe Key
caption       = Maybe Key
forall a. Maybe a
Nothing
  }

-- | The 'RenderConfig' specifies the various controls and
-- configurations used when rendering a 'KVITable' in various formats.
-- The 'RenderConfig' is global t oall formats, although some of the
-- fields in the 'RenderConfig' will be ignored as not-applicable by
-- some formats.

data RenderConfig = RenderConfig
  {
    RenderConfig -> Bool
hideBlankRows :: Bool
    -- ^ 'True' (default) removes rows for which there are no values

  , RenderConfig -> Bool
hideBlankCols :: Bool
    -- ^ 'True' (default) removes columns for which there are no values

  , RenderConfig -> Bool
equisizedCols :: Bool
    -- ^ 'True' (default) to maintain a consistent column width,
    -- otherwise the columns are shunk to the minimum size needed to
    -- display the title and values.  Not applicable for some backends
    -- (e.g. HTML) where the backend provides table rendering
    -- functionality.

  , RenderConfig -> Bool
sortKeyVals :: Bool
    -- ^ 'True' (default is False) to sort the KeyVal entries when
    -- rendering a table.

  , RenderConfig -> Maybe Key
colStackAt :: Maybe Key
    -- ^ Column key to begin stacking keys in columns and sub-columns
    -- rather than creating additional sub-rows.

  , RenderConfig -> Bool
rowRepeat :: Bool
    -- ^ 'True' (default) if an identical 'KeyVal' is to be repeated
    -- in subsequent applicable rows.

  , RenderConfig -> [Key]
rowGroup :: [Key]
    -- ^ List of Key names that should by grouped by inserting
    -- horizontal row lines between KeyVals

  , RenderConfig -> Maybe Key
caption :: Maybe Text
    -- ^ Caption to render for table for backends which support
    -- captions; otherwise ignored.
  }



-- | Sorting for KeyVals.  If the value starts or ends with a digit,
-- then this should do a rough numeric sort on the expectation that
-- the digits represent a version or some other numeric value.  As an
-- approximation of a numeric sort, sort by word size and then string
-- value.  This will result in [ "1", "2", "10", "50", "400" ], but
-- would fail with [ "v1.0", "v2.0", "v3.0", "v2.0.5", "v1.0.0.3" ],
-- but it's a reasonably fast heuristic and probably better than a
-- straight ascii sort.
--
-- This function is used by the 'KVITable' rendering functions.

sortWithNums :: [KeyVal] -> [KeyVal]
sortWithNums :: [Key] -> [Key]
sortWithNums [Key]
kvs =
  let skvs :: [(Int, Key)]
skvs = [Int] -> [Key] -> [(Int, Key)]
forall a b. [a] -> [b] -> [(a, b)]
zip (Key -> Int
rank (Key -> Int) -> [Key] -> [Int]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Key]
kvs) [Key]
kvs
      rank :: Key -> Int
rank Key
e = if (Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ Key -> Bool
T.null Key
e) Bool -> Bool -> Bool
&&
                  [Bool] -> Bool
forall (t :: * -> *). Foldable t => t Bool -> Bool
or [ Key -> Char
T.head Key
e Char -> [Char] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Char
'0'..Char
'9']
                     , Key -> Char
T.last Key
e Char -> [Char] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Char
'0'..Char
'9']
                     ]
               then Key -> Int
T.length Key
e
               else Int
0
  in (Int, Key) -> Key
forall a b. (a, b) -> b
snd ((Int, Key) -> Key) -> [(Int, Key)] -> [Key]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [(Int, Key)] -> [(Int, Key)]
forall a. Ord a => [a] -> [a]
L.sort [(Int, Key)]
skvs