    {-# LANGUAGE DeriveDataTypeable #-}
    -- #hide
    --------------------------------------------------------------------------------
    -- |
    -- Module      :  Graphics.Rendering.OpenGL.GL.VertexAttributes
    -- Copyright   :  (c) Sven Panne 2002-2009
    -- License     :  BSD-style (see the file libraries/OpenGL/LICENSE)
    --
    -- Maintainer  :  sven.panne@aedion.de
    -- Stability   :  stable
    -- Portability :  portable
    --
    -- This is a purely internal module for auxiliary vertex attributes.
    --
    --------------------------------------------------------------------------------

    module Graphics.Rendering.OpenGL.GL.VertexAttributes (
       TexCoord1(..), TexCoord2(..), TexCoord3(..), TexCoord4(..),
       Normal3(..),
       FogCoord1(..),
       Color3(..), Color4(..),
       Index1(..)
    ) where

    import Control.Applicative
    import Control.Monad
    import Data.Foldable
    import Data.Ix
    import Data.Traversable
    import Data.Typeable
    import Foreign.Marshal.Array
    import Foreign.Ptr
    import Foreign.Storable

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

    -- | Texture coordinates with /t/=0, /r/=0, and /q/=1.
    newtype TexCoord1 a = TexCoord1 a
       deriving (Eq, Ord, Ix, Bounded, Show, Read, Typeable)

    instance Functor TexCoord1 where
       fmap f (TexCoord1 x) = TexCoord1 (f x)

    instance Applicative TexCoord1 where
       pure a = TexCoord1 a
       TexCoord1 f <*> TexCoord1 x = TexCoord1 (f x)

    instance Foldable TexCoord1 where
       foldr f a (TexCoord1 x) = x `f ` a
       foldl f a (TexCoord1 x) = a `f` x
       foldr1 _ (TexCoord1 x) = x
       foldl1 _ (TexCoord1 x) = x

    instance Traversable TexCoord1 where
       traverse f (TexCoord1 x) = pure TexCoord1 <*> f x
       sequenceA (TexCoord1 x) =  pure TexCoord1 <*> x
       mapM f (TexCoord1 x) = return TexCoord1 `ap` f x
       sequence (TexCoord1 x) = return TexCoord1 `ap` x

    instance Storable a => Storable (TexCoord1 a) where
       sizeOf    ~(TexCoord1 s) = sizeOf s
       alignment ~(TexCoord1 s) = alignment s
       peek = peekApplicativeTraversable
       poke = pokeFoldable

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

    -- | Texture coordinates with /r/=0 and /q/=1.
    data TexCoord2 a = TexCoord2 !a !a
       deriving (Eq, Ord, Ix, Bounded, Show, Read, Typeable)

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

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

    instance Foldable TexCoord2 where
       foldr f a (TexCoord2 x y) = x `f ` (y `f` a)
       foldl f a (TexCoord2 x y) = (a `f` x) `f` y
       foldr1 f (TexCoord2 x y) = x `f` y
       foldl1 f (TexCoord2 x y) = x `f` y

    instance Traversable TexCoord2 where
       traverse f (TexCoord2 x y) = pure TexCoord2 <*> f x <*> f y
       sequenceA (TexCoord2 x y) =  pure TexCoord2 <*> x <*> y
       mapM f (TexCoord2 x y) = return TexCoord2 `ap` f x `ap` f y
       sequence (TexCoord2 x y) = return TexCoord2 `ap` x `ap` y

    instance Storable a => Storable (TexCoord2 a) where
       sizeOf ~(TexCoord2 x _) = 2 * sizeOf x
       alignment ~(TexCoord2 x _) = alignment x
       peek = peekApplicativeTraversable
       poke = pokeFoldable

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

    -- | Texture coordinates with /q/=1.
    data TexCoord3 a = TexCoord3 !a !a !a
       deriving (Eq, Ord, Ix, Bounded, Show, Read, Typeable)

    instance Functor TexCoord3 where
       fmap f (TexCoord3 x y z) = TexCoord3 (f x) (f y) (f z)

    instance Applicative TexCoord3 where
       pure a = TexCoord3 a a a
       TexCoord3 f g h <*> TexCoord3 x y z = TexCoord3 (f x) (g y) (h z)

    instance Foldable TexCoord3 where
       foldr f a (TexCoord3 x y z) = x `f ` (y `f` (z `f` a))
       foldl f a (TexCoord3 x y z) = ((a `f` x) `f` y) `f` z
       foldr1 f (TexCoord3 x y z) = x `f` (y `f` z)
       foldl1 f (TexCoord3 x y z) = (x `f` y) `f` z

    instance Traversable TexCoord3 where
       traverse f (TexCoord3 x y z) = pure TexCoord3 <*> f x <*> f y <*> f z
       sequenceA (TexCoord3 x y z) =  pure TexCoord3 <*> x <*> y <*> z
       mapM f (TexCoord3 x y z) = return TexCoord3 `ap` f x `ap` f y `ap` f z
       sequence (TexCoord3 x y z) = return TexCoord3 `ap` x `ap` y `ap` z

    instance Storable a => Storable (TexCoord3 a) where
       sizeOf ~(TexCoord3 x _ _) = 3 * sizeOf x
       alignment ~(TexCoord3 x _ _) = alignment x
       peek = peekApplicativeTraversable
       poke = pokeFoldable

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

    -- | Fully-fledged four-dimensional texture coordinates.
    data TexCoord4 a = TexCoord4 !a !a !a !a
       deriving (Eq, Ord, Ix, Bounded, Show, Read, Typeable)

    instance Functor TexCoord4 where
       fmap f (TexCoord4 x y z w) = TexCoord4 (f x) (f y) (f z) (f w)

    instance Applicative TexCoord4 where
       pure a = TexCoord4 a a a a
       TexCoord4 f g h i <*> TexCoord4 x y z w = TexCoord4 (f x) (g y) (h z) (i w)

    instance Foldable TexCoord4 where
       foldr f a (TexCoord4 x y z w) = x `f ` (y `f` (z `f` (w `f` a)))
       foldl f a (TexCoord4 x y z w) = (((a `f` x) `f` y) `f` z) `f` w
       foldr1 f (TexCoord4 x y z w) = x `f` (y `f` (z `f` w))
       foldl1 f (TexCoord4 x y z w) = ((x `f` y) `f` z) `f` w

    instance Traversable TexCoord4 where
       traverse f (TexCoord4 x y z w) = pure TexCoord4 <*> f x <*> f y <*> f z <*> f w
       sequenceA (TexCoord4 x y z w) =  pure TexCoord4 <*> x <*> y <*> z <*> w
       mapM f (TexCoord4 x y z w) = return TexCoord4 `ap` f x `ap` f y `ap` f z `ap` f w
       sequence (TexCoord4 x y z w) = return TexCoord4 `ap` x `ap` y `ap` z `ap` w

    instance Storable a => Storable (TexCoord4 a) where
       sizeOf ~(TexCoord4 x _ _ _) = 4 * sizeOf x
       alignment ~(TexCoord4 x _ _ _) = alignment x
       peek = peekApplicativeTraversable
       poke = pokeFoldable

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

    -- A three-dimensional normal.
    data Normal3 a = Normal3 !a !a !a
       deriving (Eq, Ord, Ix, Bounded, Show, Read, Typeable)
    instance Functor Normal3 where
       fmap f (Normal3 x y z) = Normal3 (f x) (f y) (f z)

    instance Applicative Normal3 where
       pure a = Normal3 a a a
       Normal3 f g h <*> Normal3 x y z = Normal3 (f x) (g y) (h z)

    instance Foldable Normal3 where
       foldr f a (Normal3 x y z) = x `f ` (y `f` (z `f` a))
       foldl f a (Normal3 x y z) = ((a `f` x) `f` y) `f` z
       foldr1 f (Normal3 x y z) = x `f` (y `f` z)
       foldl1 f (Normal3 x y z) = (x `f` y) `f` z

    instance Traversable Normal3 where
       traverse f (Normal3 x y z) = pure Normal3 <*> f x <*> f y <*> f z
       sequenceA (Normal3 x y z) =  pure Normal3 <*> x <*> y <*> z
       mapM f (Normal3 x y z) = return Normal3 `ap` f x `ap` f y `ap` f z
       sequence (Normal3 x y z) = return Normal3 `ap` x `ap` y `ap` z

    instance Storable a => Storable (Normal3 a) where
       sizeOf ~(Normal3 x _ _) = 3 * sizeOf x
       alignment ~(Normal3 x _ _) = alignment x
       peek = peekApplicativeTraversable
       poke = pokeFoldable

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

    -- | A fog coordinate.
    newtype FogCoord1 a = FogCoord1 a
       deriving (Eq, Ord, Ix, Bounded, Show, Read, Typeable)

    instance Functor FogCoord1 where
       fmap f (FogCoord1 x) = FogCoord1 (f x)

    instance Applicative FogCoord1 where
       pure a = FogCoord1 a
       FogCoord1 f <*> FogCoord1 x = FogCoord1 (f x)

    instance Foldable FogCoord1 where
       foldr f a (FogCoord1 x) = x `f ` a
       foldl f a (FogCoord1 x)  = a `f` x
       foldr1 _ (FogCoord1 x) = x
       foldl1 _ (FogCoord1 x) = x

    instance Traversable FogCoord1 where
       traverse f (FogCoord1 x) = pure FogCoord1 <*> f x
       sequenceA (FogCoord1 x) =  pure FogCoord1 <*> x
       mapM f (FogCoord1 x) = return FogCoord1 `ap` f x
       sequence (FogCoord1 x) = return FogCoord1 `ap` x

    instance Storable a => Storable (FogCoord1 a) where
       sizeOf    ~(FogCoord1 s) = sizeOf s
       alignment ~(FogCoord1 s) = alignment s
       peek = peekApplicativeTraversable
       poke = pokeFoldable

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

    -- An RGBA color with /A/=1.
    data Color3 a = Color3 !a !a !a
       deriving (Eq, Ord, Ix, Bounded, Show, Read, Typeable)

    instance Functor Color3 where
       fmap f (Color3 x y z) = Color3 (f x) (f y) (f z)

    instance Applicative Color3 where
       pure a = Color3 a a a
       Color3 f g h <*> Color3 x y z = Color3 (f x) (g y) (h z)

    instance Foldable Color3 where
       foldr f a (Color3 x y z) = x `f ` (y `f` (z `f` a))
       foldl f a (Color3 x y z) = ((a `f` x) `f` y) `f` z
       foldr1 f (Color3 x y z) = x `f` (y `f` z)
       foldl1 f (Color3 x y z) = (x `f` y) `f` z

    instance Traversable Color3 where
       traverse f (Color3 x y z) = pure Color3 <*> f x <*> f y <*> f z
       sequenceA (Color3 x y z) =  pure Color3 <*> x <*> y <*> z
       mapM f (Color3 x y z) = return Color3 `ap` f x `ap` f y `ap` f z
       sequence (Color3 x y z) = return Color3 `ap` x `ap` y `ap` z

    instance Storable a => Storable (Color3 a) where
       sizeOf ~(Color3 x _ _) = 3 * sizeOf x
       alignment ~(Color3 x _ _) = alignment x
       peek = peekApplicativeTraversable
       poke = pokeFoldable

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

    -- | A fully-fledged RGBA color.
    data Color4 a = Color4 !a !a !a !a
       deriving (Eq, Ord, Ix, Bounded, Show, Read, Typeable)

    instance Functor Color4 where
       fmap f (Color4 x y z w) = Color4 (f x) (f y) (f z) (f w)

    instance Applicative Color4 where
       pure a = Color4 a a a a
       Color4 f g h i <*> Color4 x y z w = Color4 (f x) (g y) (h z) (i w)

    instance Foldable Color4 where
       foldr f a (Color4 x y z w) = x `f ` (y `f` (z `f` (w `f` a)))
       foldl f a (Color4 x y z w) = (((a `f` x) `f` y) `f` z) `f` w
       foldr1 f (Color4 x y z w) = x `f` (y `f` (z `f` w))
       foldl1 f (Color4 x y z w) = ((x `f` y) `f` z) `f` w

    instance Traversable Color4 where
       traverse f (Color4 x y z w) = pure Color4 <*> f x <*> f y <*> f z <*> f w
       sequenceA (Color4 x y z w) =  pure Color4 <*> x <*> y <*> z <*> w
       mapM f (Color4 x y z w) = return Color4 `ap` f x `ap` f y `ap` f z `ap` f w
       sequence (Color4 x y z w) = return Color4 `ap` x `ap` y `ap` z `ap` w

    instance Storable a => Storable (Color4 a) where
       sizeOf ~(Color4 x _ _ _) = 4 * sizeOf x
       alignment ~(Color4 x _ _ _) = alignment x
       peek = peekApplicativeTraversable
       poke = pokeFoldable

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

    -- | A color index.
    newtype Index1 a = Index1 a
       deriving (Eq, Ord, Ix, Bounded, Show, Read, Typeable)

    instance Functor Index1 where
       fmap f (Index1 x) = Index1 (f x)

    instance Applicative Index1 where
       pure a = Index1 a
       Index1 f <*> Index1 x = Index1 (f x)

    instance Foldable Index1 where
       foldr f a (Index1 x) = x `f ` a
       foldl f a (Index1 x)  = a `f` x
       foldr1 _ (Index1 x) = x
       foldl1 _ (Index1 x) = x

    instance Traversable Index1 where
       traverse f (Index1 x) = pure Index1 <*> f x
       sequenceA (Index1 x) =  pure Index1 <*> x
       mapM f (Index1 x) = return Index1 `ap` f x
       sequence (Index1 x) = return Index1 `ap` x

    instance Storable a => Storable (Index1 a) where
       sizeOf    ~(Index1 s) = sizeOf s
       alignment ~(Index1 s) = alignment s
       peek = peekApplicativeTraversable
       poke = pokeFoldable

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

    peekApplicativeTraversable :: (Applicative t, Traversable t, Storable a) => Ptr (t a) -> IO (t a)
    peekApplicativeTraversable = Data.Traversable.mapM peek . addresses

    addresses :: (Applicative t, Traversable t, Storable a) => Ptr (t a) -> t (Ptr a)
    addresses = snd . mapAccumL nextPtr 0 . pure . castPtr

    nextPtr :: Storable a => Int -> Ptr a -> (Int, Ptr a)
    nextPtr offset ptr = (offset + 1, advancePtr ptr offset)

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

    pokeFoldable :: (Foldable t, Storable a) => Ptr (t a) -> t a -> IO ()
    pokeFoldable ptr xs = foldlM pokeAndAdvance (castPtr ptr) xs >> return ()

    pokeAndAdvance :: Storable a => Ptr a -> a -> IO (Ptr a)
    pokeAndAdvance ptr value = do
       poke ptr value
       return $ ptr `plusPtr` sizeOf value