{-# LANGUAGE ViewPatterns, NoImplicitPrelude, DeriveDataTypeable #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE DeriveGeneric #-}

module Graphics.Caramia.Shader.Internal where

import Data.Data ( Data )
import GHC.Generics ( Generic )
import Graphics.Caramia.Internal.OpenGLCApi
import Graphics.Caramia.OpenGLResource
import Graphics.Caramia.Prelude
import Graphics.Caramia.Resource

-- | A shader object for a specific shader stage.
--
-- OpenGL equivalent is the shader object.
data Shader = Shader
    { resource     :: !(Resource Shader_)
    , viewStage    :: !ShaderStage
    -- ^ Which stage does this shader belong to.
    , identifier   :: !Unique
    }
    deriving ( Typeable )

instance OpenGLResource GLuint Shader where
    getRaw shader = do
        CompiledShader name <- getRaw (WrappedOpenGLResource $ resource shader)
        return name
    touch shader = touch (WrappedOpenGLResource $ resource shader)
    finalize shader = finalize (WrappedOpenGLResource $ resource shader)

instance Eq Shader where
    (resource -> res1) == (resource -> res2) = res1 == res2

-- | The ordering has no inherent meaning but it allows shaders to be stored
-- correctly in containers that have `Ord` constraint.
instance Ord Shader where
    (identifier -> id1) `compare` (identifier -> id2) = id1 `compare` id2

newtype Shader_ = CompiledShader GLuint   -- OpenGL shader

-- | A pipeline object that references a collection of shaders.
--
-- OpenGL equivalent is the shader program object.
data Pipeline = Pipeline
    { resourcePL :: !(Resource Pipeline_)
    , pipelineIdentifier :: !Unique
    , shaders :: [Shader] }
    deriving ( Typeable )

-- | Despite the Haskell name, `Pipeline`, the object is a shader program
-- object.
instance OpenGLResource GLuint Pipeline where
    getRaw program = do
        Pipeline_ name <- getRaw (WrappedOpenGLResource $ resourcePL program)
        return name
    touch program = touch (WrappedOpenGLResource $ resourcePL program)
    finalize program = finalize (WrappedOpenGLResource $ resourcePL program)

instance Eq Pipeline where
    p1 == p2 = resourcePL p1 == resourcePL p2

instance Ord Pipeline where
    p1 `compare` p2 = pipelineIdentifier p1 `compare` pipelineIdentifier p2

newtype Pipeline_ = Pipeline_ GLuint

data ShaderStage =
    Vertex
  | Fragment
  | Geometry
  deriving ( Eq, Ord, Show, Read, Typeable, Enum, Data, Generic )