```{-# LANGUAGE TemplateHaskell  #-}
module Data.Geometry.Box.Sides( Sides(Sides), north, east, south, west
, topSide, bottomSide, leftSide, rightSide
, sides, sides'

, sideDirections
) where

import Data.Geometry.Directions
import Data.Geometry.Box.Internal
import Data.Geometry.Box.Corners
import Data.Geometry.LineSegment
import Data.Functor.Apply
import Data.Semigroup.Foldable.Class
import Data.Semigroup.Traversable.Class
import GHC.Generics (Generic)
import Control.Lens(makeLenses, Ixed(..), Index, IxValue)

--------------------------------------------------------------------------------

-- | The four sides of a rectangle
data Sides a = Sides { _north :: !a
, _east  :: !a
, _south :: !a
, _west  :: !a
makeLenses ''Sides

instance Applicative Sides where
pure x = Sides x x x x
(Sides f g h i) <*> (Sides a b c d) = Sides (f a) (g b) (h c) (i d)

instance Foldable1 Sides
instance Traversable1 Sides where
traverse1 f (Sides a b c d) = Sides <\$> f a <.> f b <.> f c <.> f d

instance Semigroup a => Semigroup (Sides a) where
s <> s' = (<>) <\$> s <*> s'
instance Monoid a => Monoid (Sides a) where
mempty = pure mempty

type instance Index   (Sides a) = CardinalDirection
type instance IxValue (Sides a) = a

instance Ixed (Sides a) where
ix = \case
North -> north
East  -> east
South -> south
West  -> west

-- | Constructs a Sides value that indicates the appropriate
-- direction.
sideDirections :: Sides CardinalDirection
sideDirections = Sides North East South West

--------------------------------------------------------------------------------

topSide :: Num r => Rectangle p r -> LineSegment 2 p r
topSide = (\(Corners l r _ _) -> ClosedLineSegment l r) . corners

-- | Oriented from *left to right*
bottomSide :: Num r => Rectangle p r -> LineSegment 2 p r
bottomSide = (\(Corners _ _ r l) -> ClosedLineSegment l r) . corners

--
leftSide  :: Num r => Rectangle p r -> LineSegment 2 p r
leftSide = (\(Corners t _ _ b) -> ClosedLineSegment b t) . corners

-- | The right side, oriented from *bottom* to top
rightSide :: Num r => Rectangle p r -> LineSegment 2 p r
rightSide = (\(Corners _ t b _) -> ClosedLineSegment b t) . corners

-- | The sides of the rectangle, in order (Top, Right, Bottom, Left). The sides
-- themselves are also oriented in clockwise order. If, you want them in the
-- same order as the functions `topSide`, `bottomSide`, `leftSide`, and