{-# LANGUAGE FlexibleInstances, FlexibleContexts, MultiParamTypeClasses
           , UndecidableInstances, TypeOperators, TypeSynonymInstances
           , TypeFamilies
  #-}
{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}  -- Vector2
----------------------------------------------------------------------
-- |
-- Module      :  Vector2
-- Copyright   :  (c) Conal Elliott and Andy J Gill 2008
-- License     :  BSD3
-- 
-- Maintainer  :  conal@conal.net, andygill@ku.edu
-- Stability   :  experimental
-- 
-- Vectors in 2D.  Uses the OpenGL 'Vector2' type, for convenience and
-- efficiency.
----------------------------------------------------------------------

module Graphics.FieldTrip.Vector2 
  (
   Vector2(..), vector2
  , vector2x, vector2y
  , xVector2, yVector2
  , vector2Polar
  , vector2PolarCoords
  , vector2D, unvector2D
  ) where

import Control.Applicative

import Graphics.Rendering.OpenGL.GL.CoordTrans (Vector2(..))

import Data.VectorSpace
import Data.MemoTrie
import Data.Basis
import Data.Derivative
import Data.Cross

-- | Construct a 3D vector in rectangular coordinates.
vector2 :: s -> s -> Vector2 s
vector2 = Vector2

-- | The unit vector in the positive X direction.
xVector2 :: Num s => Vector2 s
xVector2 = Vector2 1 0

-- | The unit vector in the positive Y direction.
yVector2 :: Num s => Vector2 s
yVector2 = Vector2 0 1

vector2x, vector2y :: Vector2 s -> s
vector2x (Vector2 x _) = x
vector2y (Vector2 _ y) = y

-- | Vector from polar coordinates.  See also 'vector2PolarCoords'.
vector2Polar :: Floating s => s -> s -> Vector2 s
vector2Polar rho theta = Vector2 (rho * cos theta) (rho * sin theta)

-- | Polar coordinates of a vector.  See also 'vector2Polar'.
vector2PolarCoords :: (InnerSpace s, Floating s, Scalar s ~ s)
                   => Vector2 s -> (s,s)
vector2PolarCoords v@(Vector2 x y) = (rho, theta)
 where
   rho   = magnitude v
   theta = atan (y/x)  -- in case (==) is not defined, as in (a:>b)
           -- if x == 0 then 0 else atan (y/x)


instance Functor Vector2 where
  fmap f (Vector2 x y)          = Vector2 (f x) (f y)

instance Applicative Vector2 where
  pure x                        = Vector2 x x
  Vector2 f g <*> Vector2 x y   = Vector2 (f x) (g y)


-- TODO: is UndecidableInstances still necessary?

instance AdditiveGroup u => AdditiveGroup (Vector2 u) where
  zeroV                          = Vector2 zeroV zeroV
  Vector2 u v ^+^ Vector2 u' v'  = Vector2 (u^+^u') (v^+^v')
  negateV (Vector2 u v)          = Vector2 (negateV u) (negateV v)

instance (VectorSpace u) => VectorSpace (Vector2 u) where
  type Scalar (Vector2 u)        = Scalar u
  s *^ Vector2 u v               = Vector2 (s*^u) (s*^v)

instance (InnerSpace u, AdditiveGroup (Scalar u))
    => InnerSpace (Vector2 u) where
  Vector2 u v <.> Vector2 u' v'  = u<.>u' ^+^ v<.>v'

instance HasBasis u => HasBasis (Vector2 u) where
  type Basis (Vector2 u)         = Basis (u,u)
  basisValue                     = toV2 . basisValue
  decompose                      = decompose  . fromV2
  decompose'                     = decompose' . fromV2

toV2 :: (u,u) -> Vector2 u
toV2 (u,v) = Vector2 u v

fromV2 :: Vector2 u -> (u,u)
fromV2 (Vector2 u v) = (u,v)


instance Num s => HasCross2 (Vector2 s) where
  cross2 (Vector2 x y) = Vector2 (-y) x  -- or @Vector2 (-y) x@?


-- instance (Num s, LMapDom s s) => HasNormal (s :> Vector2 s) where
--   normalVec v = cross2 (derivativeAt v 1)

instance HasNormal (Float :> Vector2 Float) where
  normalVec v = cross2 (v `derivAtBasis` ())

-- instance HasNormal (One Double :> Two Double) where
--   normalVec v = cross2 (derivative v `untrie` ())



vector2D :: (HasBasis a, HasTrie (Basis a), VectorSpace s) =>
            Two (a :> s) -> a :> (Vector2 s)
vector2D (u,v) = liftD2 Vector2 u v

unvector2D :: (HasBasis a, HasTrie (Basis a), VectorSpace s) =>
              a :> (Vector2 s) -> Two (a :> s)
unvector2D d = (vector2x <$>> d, vector2y <$>> d)