-- | Types and utility functions for expression text selections.
module Annotations.Bounds
  ( -- * Types
    Range
  , Bounds(..), innerRange, outerRange
    -- * Membership predicates
  , posInRange, rangeInRange, rangeInBounds, rangesInBounds, distRange
  ) where


-- | A simple textual selection: starting offset and ending offset,
-- respectively. Offsets are 0-based.
type  Range         = (Int, Int)  -- left, right

-- | A structural selection expressed as a textual selection. The margins
-- indicate the whitespace directly around the selected structure.
data  Bounds        = Bounds { leftMargin :: Range, rightMargin :: Range }
  deriving (Eq, Show)

-- | A @Bounds@' inner range does not include the whitespace around the selected structure.
innerRange :: Bounds -> Range
innerRange (Bounds (_, left) (right, _)) = (left, right)

-- | A @Bounds@' outer range includes the whitespace around the selected structure.
outerRange :: Bounds -> Range
outerRange (Bounds (left, _) (_, right)) = (left, right)


-- | Tells whether the offset falls within the given range.
posInRange :: Int -> Range -> Bool
posInRange pos (left, right) = left <= pos && pos <= right

-- | Tells whether the first range is enclosed by the second range.
rangeInRange :: Range -> Range -> Bool
rangeInRange (left, right) range = left `posInRange` range && right `posInRange` range

-- | A range is within certain bounds if its left offset is within the bounds' 
-- left margin and its right offset is within the bounds' right margin.
rangeInBounds :: Range -> Bounds -> Bool
rangeInBounds (l, r) b =
  l `posInRange` leftMargin  b &&
  r `posInRange` rightMargin b

-- | @rangesInBounds b@ yields all those ranges @r@ for which
-- @rangeInBounds r b@.
rangesInBounds :: Bounds -> [Range]
rangesInBounds (Bounds (ol, il) (ir, or)) = [ (l, r) | l <- [ol..il], r <- [ir..or] ]

-- | A measure for the dissimilarity between two ranges.
-- 
--   @distRange (l1, r1) (l2, r2) = |l1 - l2| + |r1 - r2|@
distRange :: Range -> Range -> Int
distRange (l1, r1) (l2, r2) = abs (l1 - l2) + abs (r1 - r2)