-- |
-- Module      : Cartesian.Internal.Core
-- Description : Defines basic functionality that is shared between Plane and Space
-- Copyright   : (c) Jonatan H Sundqvist, 2015
-- License     : MIT
-- Maintainer  : Jonatan H Sundqvist
-- Stability   : experimental|stable
-- Portability : POSIX (not sure)
--

-- Created October 31 2015

-- TODO | -
--        -

-- SPEC | -
--        -



------------------------------------------------------------------------------------------------------------------------------------------------------
-- GHC Pragmas
------------------------------------------------------------------------------------------------------------------------------------------------------
{-# LANGUAGE FlexibleInstances #-}



------------------------------------------------------------------------------------------------------------------------------------------------------
-- API
------------------------------------------------------------------------------------------------------------------------------------------------------
module Cartesian.Internal.Core (module Cartesian.Internal.Core,
                                module Cartesian.Internal.Types) where



------------------------------------------------------------------------------------------------------------------------------------------------------
-- We'll need these
------------------------------------------------------------------------------------------------------------------------------------------------------
import Data.List    (sort)
import Control.Lens ((^.))
import Control.Applicative

import Cartesian.Internal.Types
import Cartesian.Internal.Utils
import Cartesian.Internal.Lenses



------------------------------------------------------------------------------------------------------------------------------------------------------
-- Functions
------------------------------------------------------------------------------------------------------------------------------------------------------

-- | Finds the overlap between two ranges (lower bound, upper bound).
-- | Yields the overlap of two closed intervals (n ∈ R)
-- TODO: Normalise intervals (eg. (12, 5) -> (5, 12))
-- TODO: Type for intervals (which would also encode the 'openness' of the lower and upper limits)
overlap :: (Ord n) => (n, n) -> (n, n) -> Maybe (n, n)
overlap (a, b) (c, d)
  | min (a, b) (c, d) /= (a', b') = Just (b', c')
  | otherwise                     = Nothing
  where
    [a', b', c', d'] = sort [a, b, c, d] -- TODO: Silence the non-exhaustive warning

-- Vectors ---------------------------------------------------------------------------------------------------------------------------------

-- | Cross product
-- cross :: (Vector v, Num f) => v f -> v f -> v f
-- cross a b = _


-- | Euclidean distance between two points
-- euclidean :: (Vector v, Floating f) => v f -> v f -> f
-- euclidean a b = sqrt $ dot a b


-- -- |
-- magnitude :: (Vector v, Floating f) => v f -> f
-- magnitude v = euclidean v v


-- -- |
-- mag :: (Vector v, Floating f) => v f -> f
-- mag = magnitude

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

-- | Vector -> (magnitude, argument)
-- polar :: (Floating a, Eq a) => Vector a -> (a, a)
-- polar v@(Vector x y) = (magnitude v, argument v)

--
--
-- -- | Yields the overlap of two closed intervals (n ∈ R)
-- -- TODO: Normalise intervals (eg. (12, 5) -> (5, 12))
-- overlap :: Real a => (a, a) -> (a, a) -> Maybe (a, a)
-- overlap a b
-- 	| leftmost /= (α, β) = Just $ (β, γ) --
-- 	| otherwise                                  = Nothing --
-- 	where [α, β, γ, _] = sort [fst a, snd a, fst b, snd b] -- That's right.
-- 	      leftmost     = minimumBy (comparing fst) [a, b]  --

-- Convenience constructors ----------------------------------------------------------------------------------------------------------------

-- TODO: Generalise constructors, move to Internal.Core (✓)

-- | Creates a bounding box from two opposite corners
-- TODO: Better name (?)
-- TODO: Don't make assumptions about WHICH corners they are (✓)
-- TODO: Should we care about degenerate cases (such as 'a' and 'b' being identical)
fromCorners :: (Applicative v, Num n, Ord n) => v n -> v n -> BoundingBox (v n)
fromCorners a b = BoundingBox { cornerOf = min <$> a <*> b,
                                sizeOf   = abs <$> liftA2 (-) b a }


-- | 
fromAxes :: (Applicative v) => Axes v n -> BoundingBox (v n)
fromAxes axes' = let (begin', size') = unzipA axes' in BoundingBox { cornerOf = begin', sizeOf = size' }


-- | Top Left Bottom Right
fromExtents :: (Applicative v, Num n) => Axes v n -> BoundingBox (v n)
fromExtents extents' = let (begin', end') = unzipA extents' in BoundingBox { cornerOf = begin', sizeOf = liftA2 (-) end' begin' }

-- Booleans --------------------------------------------------------------------------------------------------------------------------------

-- | Finds the intersection (boolean AND) of two bounding boxes
intersect :: (Applicative v, Traversable v, Ord n, Num n) => BoundingBox (v n) -> BoundingBox (v n) -> Maybe (BoundingBox (v n))
intersect a b = do
  overlaps' <- traverse (uncurry overlap) (zipA (a^.extents) (b^.extents))
  return $ fromExtents overlaps'