{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
-- |
-- Module: Graphics.ChalkBoard.Types
-- Copyright: (c) 2009 Andy Gill
-- License: BSD3
--
-- Maintainer: Andy Gill <andygill@ku.edu>
-- Stability: unstable
-- Portability: ghc
--
-- This module contains the types used by chalkboard, except Board itself.
--
module Graphics.ChalkBoard.Types 
	( -- * Basic types
	  UI, R, Point, Radian,
	  -- * Overlaying
	  Over(..),
	  stack,
	  -- * Scaling
	  Scale(..),
	  -- * Linear Interpolation
	  Lerp(..),
	  -- * Averaging
	  Average(..),
{-
	  -- * Alpha Channel support
	  Alpha(..),
	 alpha, transparent, withAlpha, unAlpha,
	  -- * Z buffer support
	  Z(..),
-}
	  -- * Constants
	  nearZero
	  -- * Colors
	, Gray
	, RGB(..)
	, RGBA(..)
	) where

import Data.Binary
import Control.Monad
import Data.Ratio

-- | A real number.
type R = Float

-- | Unit Interval: value between 0 and 1, inclusive.
type UI = R

-- | A point in R2.
type Point = (R,R)

-- | Angle units
type Radian = Float	

-- | Close to zero; needed for @Over (Alpha c)@ instance.
nearZero :: R
nearZero = 0.0000001 


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

infixr 5 `over`

-- | For placing a value literally /over/ another value. The 2nd value /might/ shine through.
-- The operation /must/ be associative.
class Over c where
  over :: c -> c -> c

instance Over Bool where
  over = (||)

instance Over (a -> a) where
  over = (.)

instance Over (Maybe a) where
  (Just a) `over` _    = Just a
  Nothing `over` other = other

-- | 'stack' stacks a list of things over each other, 
-- where earlier elements are 'over' later elements.
-- Requires non empty lists, which can be satisfied by using an explicitly
-- transparent @Board@ as one of the elements.

stack :: (Over c) => [c] -> c
stack = foldr1 over

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

-- | 'Scale' something by a value. scaling value can be bigger than 1.

class Scale c where
  scale :: R -> c -> c

instance Scale R where
  scale u v = u * v

instance (Scale a,Scale b) => Scale (a,b) where
  scale u (x,y) = (scale u x,scale u y)

instance Scale Rational where
  scale u r = toRational u * r

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

-- | Linear interpolation between two values.
class Lerp a where
  lerp :: UI -> a -> a -> a

instance Lerp R where
  lerp s v v' = v + (s * (v' - v))

instance Lerp Rational where
  lerp s v v' = v + (toRational s * (v' - v))

-- | 'Lerp' over pairs

instance (Lerp a,Lerp b) => Lerp (a,b) where
  lerp s (a,b) (a',b') = (lerp s a a',lerp s b b')

instance (Lerp a) => Lerp (Maybe a) where
  lerp _ Nothing  Nothing  = Nothing
  lerp _ (Just a) Nothing  = Just a 
  lerp _ Nothing  (Just b) = Just b
  lerp s (Just a) (Just b) = Just (lerp s a b)

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

-- | 'Average' a set of values. weighting can be achived using multiple entries.

class Average a where
  -- | average is not defined for empty list
  average :: [a] -> a 

instance Average R where
  average xs = sum xs / fromIntegral (length xs)
  
instance (Average a,Average b) => Average (a,b) where
  average xs = (average $ map fst xs,average $ map snd xs)

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

-- | 'Gray' is just a value between 0 and 1, inclusive.
-- Be careful to consider if this is pre or post gamma.
type Gray = UI

instance Over Gray where
  over r _ = r

------------------------------------------------------------------------------
-- Simple colors

-- | 'RGB' is our color, with values between 0 and 1, inclusive.
data RGB  = RGB !UI !UI !UI deriving Show

instance Over RGB where
  -- simple overwriting
  over x _y = x

instance Lerp RGB where
  lerp s (RGB r g b) (RGB r' g' b')
	 = RGB (lerp s r r') 
	       (lerp s g g')
	       (lerp s b b')

instance Scale RGB where
  scale s (RGB r g b)
	 = RGB (scale s r) 
	       (scale s g)
	       (scale s b) 

instance Average RGB where
  average cs = RGB (average reds) (average greens) (average blues)
     where
	reds   = [ r | RGB r _ _ <- cs ]
	greens = [ g | RGB _ g _ <- cs ]
	blues  = [ b | RGB _ _ b <- cs ]


-- Consider using 4 bytes for color, rather than 32 bytes for the double (or are they floats?)
instance Binary RGB where
  put (RGB r g b) = put r >> put g >> put b
  get = liftM3 RGB get get get


------------------------------------------------------------------------------------------
-- Colors with alpha

-- | 'RGBA' is our color, with values between 0 and 1, inclusive.
-- These values are *not* prenormalized
data RGBA = RGBA !UI !UI !UI !UI deriving Show

 -- Todo: rethink what this means

instance Over RGBA where
  over (RGBA r g b a) (RGBA r' g' b' a') =
	RGBA (f r r')			-- (SrcAlpha, OneMinusSrcAlpha)
	     (f g g')
	     (f b b')	
	     (a + a' * (1 - a))		-- (One, OneMinusSrcAlpha)
    where f x y  = a * y + (1 - a) * x

{-
   -- An associative algorithm for handling the alpha channel
	-- Associative; please reinstate later

	| a <= nearZero = RGBA r' g' b' a_new
	| otherwise     = RGBA 
			(lerp r' (scale (1/a) r) a) 
			(lerp g' (scale (1/a) g) a) 
			(lerp b' (scale (1/a) b) a) 
				 a_new
     where
	-- can a_new be 0? only if a == 0 and a' == 0
	a_new     = a + a' * (1 - a)
-}

instance Binary RGBA where
  put (RGBA r g b a) = put r >> put g >> put b >> put a
  get = liftM4 RGBA get get get get
