{- |
  Generically handle transforms, and things that are transformable.
-}

{-# LANGUAGE MultiParamTypeClasses, TypeFamilies #-}

module Data.Vector.Transform.Fancy where

import Data.Angle

import Data.Vector.Class
import Data.Vector.V1
import Data.Vector.V2
import Data.Vector.V3
import Data.Vector.V4
import Data.Vector.Axis
import Data.Vector.Transform.T1
import Data.Vector.Transform.T2
import Data.Vector.Transform.T3
import Data.Vector.Transform.T4

-- | Class for transforms.
class Transform t where
  -- | The type of vector that can be transformed. (E.g., for @Transform3@, this would be @Vector3@.)
  type Point t :: *

  -- | Transform a vector.
  transformP :: t -> Point t -> Point t

  -- | Build transform: translate by the given vector.
  translateT :: Point t -> t

  -- | Build transform: scale each coordinate axis according to the given vector.
  scaleT     :: Point t -> t

  -- | Build transform: scale all axies uniformly.
  scaleT_    :: Scalar  -> t

-- | Class for performing rotationes. (The rotations that exist vary with the number of spatial dimensions available.)
class (Transform t) => Rotate t axis1 axis2 where
  -- | Build transform: rotate in the plane defined by the two axies.
  rotateT :: (Angle a) => axis1 -> axis2 -> a Scalar -> t

-- | Class for things that can be transformed (by a specific type of transform).
class (Transform t) => Transformable t x where
  -- | Transform anything that can be transformed (for a given type of transform).
  transform :: t -> x -> x



instance Transform Transform1 where
  type Point Transform1 = Vector1
  transformP = transformP1
  translateT (Vector1 x) = Transform1  1 x
  scaleT     (Vector1 x) = Transform1  x 0
  scaleT_             k  = Transform1  k 0

instance Transform Transform2 where
  type Point Transform2 = Vector2
  transformP = transformP2
  translateT (Vector2 x y) = Transform2  1 0 x  0 1 y
  scaleT     (Vector2 x y) = Transform2  x 0 0  0 y 0
  scaleT_             k    = Transform2  k 0 0  0 k 0

instance Transform Transform3 where
  type Point Transform3 = Vector3
  transformP = transformP3
  translateT (Vector3 x y z) = Transform3  1 0 0 x  0 1 0 y  0 0 1 z
  scaleT     (Vector3 x y z) = Transform3  x 0 0 0  0 y 0 0  0 0 z 0
  scaleT_             k      = Transform3  k 0 0 0  0 k 0 0  0 0 k 0

instance Transform Transform4 where
  type Point Transform4 = Vector4
  transformP = transformP4
  translateT (Vector4 x y z w) = Transform4  1 0 0 0 x  0 1 0 0 y  0 0 1 0 z  0 0 0 1 w
  scaleT     (Vector4 x y z w) = Transform4  x 0 0 0 0  0 y 0 0 0  0 0 z 0 0  0 0 0 w 0
  scaleT_             k        = Transform4  k 0 0 0 0  0 k 0 0 0  0 0 k 0 0  0 0 0 k 0



instance Transformable Transform1 Vector1 where transform = transformP1
instance Transformable Transform2 Vector2 where transform = transformP2
instance Transformable Transform3 Vector3 where transform = transformP3
instance Transformable Transform4 Vector4 where transform = transformP4



instance Rotate Transform2 AxisX AxisY where
  rotateT _ _ a =
    let
      s  =   sine a
      c  = cosine a
      s' = negate s
    in Transform2  c s' 0  s c 0

instance Rotate Transform3 AxisX AxisY where
  rotateT _ _ a =
    let
      s  =   sine a
      c  = cosine a
      s' = negate s
    in Transform3  c s' 0 0  s c 0 0  0 0 1 0

instance Rotate Transform3 AxisX AxisZ where
  rotateT _ _ a =
    let
      s  =   sine a
      c  = cosine a
      s' = negate s
    in Transform3  c 0 s' 0  0 1 0 0  s 0 c 0

instance Rotate Transform3 AxisY AxisZ where
  rotateT _ _ a =
    let
      s  =   sine a
      c  = cosine a
      s' = negate s
    in Transform3  1 0 0 0  0 c s' 0  0 s c 0

instance Rotate Transform4 AxisX AxisY where
  rotateT _ _ a =
    let
      s  =   sine a
      c  = cosine a
      s' = negate s
    in Transform4  c s' 0 0 0  s c 0 0 0  0 0 1 0 0  0 0 0 1 0

instance Rotate Transform4 AxisX AxisZ where
  rotateT _ _ a =
    let
      s  =   sine a
      c  = cosine a
      s' = negate s
    in Transform4  c 0 s' 0 0  0 1 0 0 0  s 0 c 0 0  0 0 0 1 0

instance Rotate Transform4 AxisX AxisW where
  rotateT _ _ a =
    let
      s  =   sine a
      c  = cosine a
      s' = negate s
    in Transform4  c 0 0 s' 0  0 1 0 0 0  0 0 1 0 0  s 0 0 c 0

instance Rotate Transform4 AxisY AxisZ where
  rotateT _ _ a =
    let
      s  =   sine a
      c  = cosine a
      s' = negate s
    in Transform4  1 0 0 0 0  0 c s' 0 0  0 s c 0 0  0 0 0 1 0

instance Rotate Transform4 AxisY AxisW where
  rotateT _ _ a =
    let
      s  =   sine a
      c  = cosine a
      s' = negate s
    in Transform4  1 0 0 0 0  0 c 0 s' 0  0 0 1 0 0  0 s 0 c 0

instance Rotate Transform4 AxisZ AxisW where
  rotateT _ _ a =
    let
      s  =   sine a
      c  = cosine a
      s' = negate s
    in Transform4  1 0 0 0 0  0 1 0 0 0  0 0 c s' 0  0 0 s c 0