module Data.Text.ParagraphLayout.Internal.BoxOptions
    ( BoxCollapse (..)
    , BoxOptions (..)
    , BoxSpacing (..)
    , VerticalAlignment (..)
    , activateBoxSpacing
    , defaultBoxOptions
    )
where
import Data.Int (Int32)
-- | Style options to be applied to an inline box.
--
-- This record type is likely to be extended in the future.
-- Use `defaultBoxOptions` and update it with specific record selectors
-- instead of constructing `BoxOptions` directly.
--
-- In order to get CSS defaults, use:
--
-- > defaultBoxOptions { boxVerticalAlignment = AlignBaseline 0 }
data BoxOptions = BoxOptions
    { boxSpacing :: BoxSpacing
    -- ^ Determines amount of empty space to add before the first fragment
    -- and/or after the last fragment of this box.
    --
    -- This can be used to reserve space for a left margin, border,
    -- and/or padding.
    --
    -- In order to get CSS-compliant behaviour, consider using
    -- `activateBoxSpacing` instead.
    , boxCollapse :: BoxCollapse
    -- ^ Determines how this box affects the height of a line that would
    -- otherwise be empty.
    , boxVerticalAlignment :: VerticalAlignment
    -- ^ Determines how fragments are aligned vertically within a line.
    }
    deriving (Eq)
-- | Determines the amount of empty space to add around a box.
data BoxSpacing
    -- | Specification using absolute directions, unaffected by text direction.
    --
    -- (However, text direction is still used in case of fragmentation,
    -- to determine how to map the first (last) fragment to left (right).)
    = BoxSpacingLeftRight
        Int32
        -- ^ Amount of empty space to add to the left side of the
        -- first fragment (for LTR text) or last fragment (for RTL text)
        -- created from this box.
        Int32
        -- ^ Amount of empty space to add to the right side of the
        -- first fragment (for RTL text) or last fragment (for LTR text)
        -- created from this box.
    deriving (Eq, Show, Read)
-- | Determines how an inline box affects the height of a line that would
-- otherwise be empty.
--
-- Note that for this setting to have any effect, the box must be the ancestor
-- of at least one (possibly empty) text sequence, so that it can generate
-- a `Data.Text.ParagraphLayout.Rich.Fragment`.
--
-- For CSS-compliant behaviour:
--
-- - Ensure that empty boxes contain empty text sequences as mentioned above.
--   This can be achieved using the `Data.Text.ParagraphLayout.Rich.strut`
--   function.
--
-- - Set `AllowBoxCollapse` for boxes with zero margins, padding,
--   and borders.
--
-- - Set `AvoidBoxCollapse` for boxes with non-zero margins, padding,
--   or borders.
--
-- Note that `BoxSpacing` may contain zero values even when calculated from
-- non-zero CSS values, because of rounding to integer values and/or because
-- of adding together positive and negative values that cancel out. You should
-- therefore compare the individual computed values for margins, padding, and
-- borders with zero, and set `boxCollapse` to `AllowBoxCollapse` if and only
-- if all these values are equal to zero.
data BoxCollapse
    = AllowBoxCollapse
    -- ^ The box should disappear when possible to make empty lines invisible.
    | AvoidBoxCollapse
    -- ^ The box should be preserved when possible, making lines visible
    -- even when they would otherwise be empty.
    deriving (Eq, Read, Show)
-- | Determines how the contents of a line should be aligned vertically,
-- that is, how fragments should fill (and possibly extend) the vertical space
-- inside a line.
--
-- Vertical alignment takes place after line breaking.
data VerticalAlignment
    = AlignLineTop
    -- ^ Align the top of this box with the top of the line.
    --
    -- The /top of this box/ is the topmost coordinate of any fragment
    -- descended from this box, except for fragments that are descended from
    -- another box with `AlignLineTop` or `AlignLineBottom` closer to them.
    --
    -- t`Data.Text.ParagraphLayout.Rich.LineHeight` is included
    -- when considering the /top/ coordinates of descended fragments,
    -- and thus, leading is included.
    --
    -- This behaviour is equivalent to @vertical-align: top@ in CSS.
    | AlignLineBottom
    -- ^ Align the bottom of this box with the bottom of the line.
    --
    -- The /bottom of this box/ is the bottommost coordinate of any fragment
    -- descended from this box, except for fragments that are descended from
    -- another box with `AlignLineTop` or `AlignLineBottom` closer to them.
    --
    -- t`Data.Text.ParagraphLayout.Rich.LineHeight` is included
    -- when considering the /bottom/ coordinates of descended fragments,
    -- and thus, leading is included.
    --
    -- This behaviour is equivalent to @vertical-align: bottom@ in CSS.
    | AlignBaseline Int32
    -- ^ Align the baseline of this box with the baseline of its parent box,
    -- optionally shifting it by a specified amount upwards.
    --
    -- @`AlignBaseline` 0@ is equivalent to @vertical-align: baseline@ in CSS.
    --
    -- @`AlignBaseline` x@ is equivalent to @vertical-align: \<length\>@ in CSS,
    -- after an appropriate unit conversion.
    deriving (Eq, Read, Show)
-- | `BoxOptions` with backwards-compatible values.
--
-- Note that this sets `boxVerticalAlignment` to `AlignLineTop`,
-- whereas the CSS default should be `AlignBaseline 0`.
defaultBoxOptions :: BoxOptions
defaultBoxOptions = BoxOptions
    { boxSpacing = BoxSpacingLeftRight 0 0
    , boxCollapse = AllowBoxCollapse
    , boxVerticalAlignment = AlignLineTop
    }
-- | Shorthand for updating `boxSpacing` and setting `boxCollapse`
-- to `AvoidBoxCollapse`.
--
-- Can be used to activate CSS-compliant rendering of empty boxes whose
-- spacing is calculated from non-zero values.
activateBoxSpacing :: BoxSpacing -> BoxOptions -> BoxOptions
activateBoxSpacing spacing boxOptions = boxOptions
    { boxSpacing = spacing
    , boxCollapse = AvoidBoxCollapse
    }