{-# LANGUAGE Safe #-}

-- | The Data module for common data types within the code.
module Data.Range.Data where

data OverlapType = Separate | Overlap | Adjoin
   deriving (Eq, Show)

-- | Represents a type of boundary.
data BoundType
   = Inclusive -- ^ The value at the boundary should be included in the bound.
   | Exclusive -- ^ The value at the boundary should be excluded in the bound.
   deriving (Eq, Show)

-- | Represents a bound at a particular value with a 'BoundType'. 
-- There is no implicit understanding if this is a lower or upper bound, it could be either.
data Bound a = Bound
   { boundValue :: a          -- ^ The value at the edge of this bound.
   , boundType :: BoundType   -- ^ The type of bound. Should be 'Inclusive' or 'Exclusive'.
   } deriving (Eq, Show)

instance Functor Bound where
   fmap f (Bound v vType) = Bound (f v) vType

-- TODO can we implement Monoid for Range a with the addition of an empty?
-- Or maybe we can implement Monoid for a list of ranges...

-- | The Range Data structure; it is capable of representing any type of range. This is
-- the primary data structure in this library. Everything should be possible to convert
-- back into this datatype. All ranges in this structure are inclusively bound.
data Range a
   = SingletonRange a               -- ^ Represents a single element as a range. @SingletonRange a@ is equivalent to @SpanRange (Bound a Inclusive) (Bound a Inclusive)@.
   | SpanRange (Bound a) (Bound a)  -- ^ Represents a bounded span of elements. The first argument is expected to be less than or equal to the second argument.
   | LowerBoundRange (Bound a)      -- ^ Represents a range with a finite lower bound and an infinite upper bound.
   | UpperBoundRange (Bound a)      -- ^ Represents a range with an infinite lower bound and a finite upper bound.
   | InfiniteRange                  -- ^ Represents an infinite range over all values.
   deriving(Eq)

instance Functor Range where
   fmap f (SingletonRange x) = SingletonRange . f $ x
   fmap f (SpanRange x y) = SpanRange (fmap f x) (fmap f y)
   fmap f (LowerBoundRange x) = LowerBoundRange (fmap f x)
   fmap f (UpperBoundRange x) = UpperBoundRange (fmap f x)
   fmap _ InfiniteRange = InfiniteRange

instance Show a => Show (Range a) where
   showsPrec i (SingletonRange a) = ((++) "SingletonRange ") . showsPrec i a
   showsPrec i (SpanRange (Bound l lType) (Bound r rType)) =
      showsPrec i l . showSymbol lType rType . showsPrec i r
      where
         showSymbol Inclusive Inclusive = (++) " +=+ "
         showSymbol Inclusive Exclusive = (++) " +=* "
         showSymbol Exclusive Inclusive = (++) " *=+ "
         showSymbol Exclusive Exclusive = (++) " *=* "
   showsPrec i (LowerBoundRange (Bound a Inclusive)) = ((++) "lbi ") . (showsPrec i a)
   showsPrec i (LowerBoundRange (Bound a Exclusive)) = ((++) "lbe ") . (showsPrec i a)
   showsPrec i (UpperBoundRange (Bound a Inclusive)) = ((++) "ubi ") . (showsPrec i a)
   showsPrec i (UpperBoundRange (Bound a Exclusive)) = ((++) "ube ") . (showsPrec i a)
   showsPrec _ (InfiniteRange) = (++) "inf"