{-# LANGUAGE ScopedTypeVariables  #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.Geometry.Vector.VectorFixed where
import           Control.DeepSeq
import           Control.Lens hiding (element)
import           Data.Aeson
import qualified Data.Foldable as F
import           Data.Proxy
import qualified Data.Vector.Fixed as V
import           Data.Vector.Fixed (Arity)
import           Data.Vector.Fixed.Boxed
import           GHC.Generics (Generic)
import           GHC.TypeLits
import           Linear.Affine (Affine(..))
import           Linear.Metric
import qualified Linear.V2 as L2
import qualified Linear.V3 as L3
import           Linear.Vector
data C (n :: Nat) = C deriving (Show,Read,Eq,Ord)
newtype Vector (d :: Nat)  (r :: *) = Vector { _unV :: Vec d r }
                                    deriving (Generic)
unV :: Lens' (Vector d r) (Vec d r)
unV = lens _unV (const Vector)
element   :: forall proxy i d r. (Arity d, Arity i, (i + 1) <= d)
          => proxy i -> Lens' (Vector d r) r
element _ = V.elementTy (Proxy :: Proxy i)
element'   :: forall d r. Arity d => Int -> Traversal' (Vector d r) r
element' i f v
  | 0 <= i && i < fromInteger (natVal (C :: C d)) = f (v V.! i)
                                                 <&> \a -> (v&V.element i .~ a)
       
  | otherwise                                     = pure v
vectorFromList :: Arity d => [a] -> Maybe (Vector d a)
vectorFromList = fmap Vector . V.fromListM
vectorFromListUnsafe :: Arity d => [a] -> Vector d a
vectorFromListUnsafe = Vector . V.fromList
instance (Show r, Arity d) => Show (Vector d r) where
  show (Vector v) = mconcat [ "Vector", show $ V.length v , " "
                            , show $ F.toList v
                            ]
deriving instance (Eq r, Arity d)   => Eq (Vector d r)
deriving instance (Ord r, Arity d)  => Ord (Vector d r)
instance Arity d  => Functor (Vector d) where
  fmap f (Vector v) = Vector $ fmap f v
deriving instance Arity d  => Foldable (Vector d)
deriving instance Arity d  => Applicative (Vector d)
instance Arity d => Traversable (Vector d) where
  traverse f (Vector v) = Vector <$> traverse f v
deriving instance (Arity d, NFData r) => NFData (Vector d r)
instance Arity d => Additive (Vector d) where
  zero = pure 0
  (Vector u) ^+^ (Vector v) = Vector $ V.zipWith (+) u v
instance Arity d => Affine (Vector d) where
  type Diff (Vector d) = Vector d
  u .-. v = u ^-^ v
  p .+^ v = p ^+^ v
instance Arity d => Metric (Vector d)
type instance V.Dim (Vector d) = d
instance Arity d => V.Vector (Vector d) r where
  construct  = Vector <$> V.construct
  inspect    = V.inspect . _unV
  basicIndex = V.basicIndex . _unV
instance (FromJSON r, Arity d, KnownNat d)  => FromJSON (Vector d r) where
  parseJSON y = parseJSON y >>= \xs -> case vectorFromList xs of
                  Nothing -> fail . mconcat $
                    [ "FromJSON (Vector d a), wrong number of elements. Expected "
                    , show $ natVal (Proxy :: Proxy d)
                    , " elements but found "
                    , show $ length xs
                    , "."
                    ]
                  Just v -> pure v
instance (ToJSON r, Arity d) => ToJSON (Vector d r) where
  toJSON     = toJSON     . F.toList
  toEncoding = toEncoding . F.toList
destruct            :: (Arity d, Arity (d + 1), 1 <= (d + 1))
                    => Vector (d + 1) r -> (r, Vector d r)
destruct (Vector v) = (V.head v, Vector $ V.tail v)
cross       :: Num r => Vector 3 r -> Vector 3 r -> Vector 3 r
u `cross` v = fromV3 $ (toV3 u) `L3.cross` (toV3 v)
toV2                :: Vector 2 a -> L2.V2 a
toV2 ~(Vector2 a b) = L2.V2 a b
toV3                  :: Vector 3 a -> L3.V3 a
toV3 ~(Vector3 a b c) = L3.V3 a b c
fromV3               :: L3.V3 a -> Vector 3 a
fromV3 (L3.V3 a b c) = v3 a b c
snoc :: (Arity (d + 1), Arity d) => Vector d r -> r -> Vector (d + 1) r
snoc = flip V.snoc
init :: (Arity d, Arity (d + 1)) => Vector (d + 1) r -> Vector d r
init = Vector . V.reverse . V.tail . V.reverse . _unV
last :: forall d r. (Arity d, Arity (d + 1)) => Vector (d + 1) r -> r
last = view $ element (Proxy :: Proxy d)
prefix :: forall i d r. (Arity d, Arity i, i <= d)
       => Vector d r -> Vector i r
prefix = let i = fromInteger . natVal $ (Proxy :: Proxy i)
         in V.fromList . take i . V.toList
v2     :: r -> r -> Vector 2 r
v2 a b = Vector $ V.mk2 a b
v3      :: r -> r -> r -> Vector 3 r
v3 a b c = Vector $ V.mk3 a b c
_unV2 :: Vector 2 r -> (r,r)
_unV2 v = let [x,y] = V.toList v in (x,y)
_unV3 :: Vector 3 r -> (r,r,r)
_unV3 v = let [x,y,z] = V.toList v in (x,y,z)
pattern Vector2       :: r -> r -> Vector 2 r
pattern Vector2 x y   <- (_unV2 -> (x,y))
  where
    Vector2 x y = v2 x y
{-# COMPLETE Vector2 #-}
pattern Vector3       :: r -> r -> r -> Vector 3 r
pattern Vector3 x y z <- (_unV3 -> (x,y,z))
  where
    Vector3 x y z = v3 x y z
{-# COMPLETE Vector3 #-}
pattern Vector4         :: r -> r -> r -> r -> Vector 4 r
pattern Vector4 x y z a <- (V.toList -> [x,y,z,a])
  where
    Vector4 x y z a = V.mk4 x y z a
{-# COMPLETE Vector4 #-}