{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}
module Graphics.Transform.Magick.Types where

import Foreign
import Foreign.C.Types
import Foreign.C.String

-- types used for representing data from GraphicsMagick

-- Types for external use. These are part of the Haskell
-- GraphicsMagick interface.

-- The idea here is that when we first create an image we
-- have an exception info and an image info.
-- Later after it's loaded, we have an image as well.
-- getFilename and setFilename are class methods that work on
-- either one, because if we have an image that's not loaded
-- yet, we want to be able to set the filename (for loading it later),
-- and if we have an image that *is* loaded, we want to be able to set
-- the filename in both the image *and* the info.

-- TODO: don't export the selectors for this.
data HImage = HImage {image::ForeignPtr HImage_,
                      otherInfo::ImageNotLoaded}
data ImageNotLoaded = ImageNotLoaded { imageInfo::ForeignPtr HImageInfo, 
                                       exceptionInfo::ForeignPtr ExceptionInfo }

-- A rectangle is represented as a width, height, horizontal offset, and
-- vertical offset
data Rectangle = Rectangle { width  :: Word,
                             height :: Word,
                             x      :: Int,
                             y      :: Int }
  deriving Show

data AffineMatrix = AffineMatrix { sx::Double,
                                   rx::Double,
                                   ry::Double,
                                   sy::Double,
                                   tx::Double,
                                   ty::Double }

data PixelPacket a = PixelPacket { red::a,
                                   green::a,
                                   blue::a,
                                   opacity::a}
data Level = Level { black::Double,
                     mid::Double,
                     white::Double }

data Modulation = Modulation { brightness::Double,
                               saturation::Double,
                               hue::Double }

data Negation = AllPixels | GrayscalePixels

-- TODO: quantum depth (number of bits in a pixel)
-- is determined at GraphicsMagick compile time. need
-- to reflect that (I guess in a config file for this
-- library...)
type PixelPacketByte = PixelPacket Word8

data ChannelType = 
  UndefinedChannel|
  RedChannel|
  CyanChannel|
  GreenChannel|
  MagentaChannel|
  BlueChannel|
  YellowChannel|
  OpacityChannel|
  BlackChannel|
  MatteChannel
   deriving Enum

getImage :: HImage -> ForeignPtr HImage_
getImageInfo :: HImage -> ForeignPtr HImageInfo
getExceptionInfo :: HImage -> ForeignPtr ExceptionInfo

getImage = image
getImageInfo = imageInfo.otherInfo
getExceptionInfo = exceptionInfo.otherInfo

mkUnloadedImage :: ForeignPtr HImageInfo -> ForeignPtr ExceptionInfo -> ImageNotLoaded
mkUnloadedImage iInfo exInfo = 
   ImageNotLoaded{ imageInfo = iInfo, exceptionInfo = exInfo }


data FilterTypes = 
  UndefinedFilter
  | PointFilter
  | BoxFilter
  | TriangleFilter
  | HermiteFilter
  | HanningFilter
  | HammingFilter
  | BlackmanFilter
  | GaussianFilter
  | QuadraticFilter
  | CubicFilter
  | CatromFilter
  | MitchellFilter
  | LacrosFilter
  | BesselFilter
  | SincFilter
    deriving Enum

data CompositeOp = Undefined
   | Over
   | In
   | Out
   | Atop
   | Xor
   | Plus
   | Minus
   | Add
   | Subtract
   | Difference
   | Multiply
   | Bumpmap
   | Copy
   | CopyRed
   | CopyGreen
   | CopyBlue
   | CopyOpacity
   | Clear
   | Dissolve
   | Displace
   | Modulate
   | Threshold
   | No
   | Darken
   | Lighten
   | Hue
   | Saturate
   | Colorize
   | Luminize
   | Screen
   | Overlay
   | CopyCyan
   | CopyMagenta
   | CopyYellow
   | CopyBlack
  deriving Enum

data Contrast = IncreaseContrast | DecreaseContrast

data ImageCharacteristics = ImageC {
      cmyk::Bool,
      grayscale::Bool,
      mONOCHROME::Bool,
      opaque::Bool,
      palette::Bool
}

-- TODO:
-- the Right Thing to do would be
-- to use type classes rather than all these underscores
data ImageStatistics = ImageS {
      red_::ImageChannelStatistics,
      green_::ImageChannelStatistics,
      blue_::ImageChannelStatistics,
      opacity_::ImageChannelStatistics
}

data ImageChannelStatistics = ImageCS {
      maximum::Double,
      minimum::Double,
      mean::Double,
      standard_deviation::Double,
      variance::Double
}

data SegmentInfo = SegmentInfo {
      x1::Double, y1::Double, x2::Double, y2::Double
}

data ImageOrder = LeftToRight | TopToBottom
  deriving Enum
----------- Storage (used by constituteImage)
data StorageType = CharPixel | ShortPixel | IntegerPixel | LongPixel 
                 | FloatPixel | DoublePixel
   deriving Enum

-- OMG functional dependencies squee!!
class Storable b => StorablePixel a b | a -> b where
   storageType    :: a -> StorageType
   marshalPixel   :: a -> b
   unmarshalPixel :: b -> a

instance StorablePixel Word8 CUChar where
    storageType _ = CharPixel
    marshalPixel  = fromIntegral
    unmarshalPixel  = fromIntegral
instance StorablePixel Word16 CUShort where
    storageType _ = ShortPixel
    marshalPixel  = fromIntegral
    unmarshalPixel  = fromIntegral
instance StorablePixel Word32 CUInt where
    storageType _ = IntegerPixel
    marshalPixel  = fromIntegral
    unmarshalPixel  = fromIntegral
instance StorablePixel Word64 CULong where
    storageType _ = LongPixel
    marshalPixel  = fromIntegral
    unmarshalPixel  = fromIntegral
instance StorablePixel Float CFloat where
    storageType _ = FloatPixel
    marshalPixel  = realToFrac
    unmarshalPixel  = realToFrac
instance StorablePixel Double CDouble where
    storageType _ = DoublePixel
    marshalPixel  = realToFrac
    unmarshalPixel  = realToFrac

-- TODO:
-- should have better constraints. ex. no repeated
-- quantums, list can't be empty. I don't think all
-- combinations are legal.
newtype PixMap = PixMap [QuantumType]

instance Show PixMap where
  show (PixMap things) = concatMap show things

pixelSize :: PixMap -> Int
pixelSize (PixMap quantums) = length quantums

data QuantumType = R|G|B|A|O|T|C|Y|M|K|I|P
  deriving Show

-- TODO: better name
data QuantumType2 = UndefinedQuantum | IndexQuantum | GrayQuantum
  | IndexAlphaQuantum | GrayAlphaQuantum | RedQuantum | CyanQuantum | GreenQuantum
  | YellowQuantum | BlueQuantum | MagentaQuantum | AlphaQuantum | BlackQuantum
  | RGBQuantum | RGBAQuantum | CMYKQuantum | CMYKAQuantum | CIEYQuantum
  | CIEXYZQuantum
   deriving Enum

-- All types below should only be used internally to the library.

-- we append underscores to names for fields that appear 
-- in multiple different record types, but there's ugly.
-- must be a better way (type classes?)
data ExceptionInfo = ExceptionInfo {
      severity     :: ExceptionType,
      reason       :: CString,
      description  :: CString,
      error_number :: CInt,
      mODULE       :: CString,
      function     :: CString,
      line         :: CULong,
      signature__  :: CULong
}

------ TODO: stubs
type ExportPixelAreaOptions = Word32
type ExportPixelAreaInfo = Word32
type ImportPixelAreaOptions = Word32
type ImportPixelAreaInfo = Word32

-------------
type ImagePtr = Ptr Image
type Image = Word32
data CharArray = CharArray 
type CompressionType = Word32
type InterlaceType = Word32
type EndianType = Word32
type ResolutionType = Word32
type ColorspaceType = Word32
type ImageType = Word32
type StreamHandler = Word32
type PreviewType = Word32
type ClassType = Word32
type ChromaticityInfo = Word32
type OrientationType = Word32
type RenderingIntent = Word32
type GravityType = Word32
type DisposeType = Word32
type ErrorInfo = Word32
type TimerInfo = Word32
type CacheInfoPtr = Word32
type ImageAttributePtr = Word32
type Ascii85InfoPtr = Word32
type BlobInfoPtr = Word32
type SemaphoreInfoPtr = Word32
type ExceptionType = CUInt -- actually an enum type

-- Correspondences:
{- HImage_ <=> Image
   HImageInfo <=> ImageInfo 
-}




-- This is from magick/image.h in GraphicsMagick 1.2. It may be subject to change!

-- default values?
-- some of these need fixing. ex., verbose should be
-- a boolean, but the Storable instance should convert it appropriately.
data HImageInfo = HImageInfo {
 compression :: CompressionType,
 temporary   :: CUInt,
 adjoin      :: CUInt,
 antialias   :: CUInt,
 subimage    :: CULong,
 subrange    :: CULong,
 depth       :: CULong,
 size        :: CString,
 tile        :: CString,
 page        :: CString,
 interlace   :: InterlaceType,
 endian      :: EndianType,
 units       :: ResolutionType,
 quality     :: CULong,
 sampling_factor :: CString,
 server_name     :: CString,
 font            :: CString,
 texture         :: CString,
 density         :: CString,
 pointsize       :: CDouble,
 fuzz            :: CDouble,
 pen             :: PixelPacketByte,
 background_color :: PixelPacketByte,
 border_color     :: PixelPacketByte,
 matte_color      :: PixelPacketByte,
 dither          :: CUInt,
 monochrome      :: CUInt,
 progress        :: CUInt,
 colorspace      :: ColorspaceType,
 tYPE            :: ImageType,
 group           :: CLong,
 verbose         :: CUInt,
 view            :: CString,
 authenticate    :: CString,
 client_data     :: CString,
 file            :: Ptr CFile,
-- these two are actually represented as arrays
 magick          :: String,
 filename        :: String,
-- private from here on out
 cache           :: CString,
 definitions     :: CString,
 attributes      :: Ptr Image,
 ping            :: CUInt,
 preview_type    :: PreviewType,
 affirm          :: CUInt,
 blob            :: CString,
 lENGTH          :: CSize,
 unique          :: CString,
 zero            :: CString,
 signature       :: CULong   
}

-- Could we eliminate the duplicated fields and add code to copy them
-- back and forth between the Image and the ImageInfo to the Storable
-- instances?
data HImage_ = HImage_ {
  storage_class :: ClassType,
  colorspace_   :: ColorspaceType,
  compression_ :: CompressionType,
  dither_      :: CUInt,
  matte       :: CUInt,
  columns     :: CULong,
  rows        :: CULong,
  colors      :: CUInt,
  depth_       :: CUInt,
  colormap    :: CIntPtr,
  background_color_ :: PixelPacketByte,
  border_color_ :: PixelPacketByte,
  matte_color_  :: PixelPacketByte,
  gamma       :: CDouble,
  chromaticity :: ChromaticityInfo,
  orientation  :: OrientationType,
  rendering_intent :: RenderingIntent,
  units_       :: ResolutionType,
  montage      :: CString,
  directory    :: CString,
  geometry     :: CString,
  offset       :: CLong,
  x_resolution :: CDouble,
  y_resolution :: CDouble,
  page_        :: Rectangle,
  tile_info    :: Rectangle,
  blur         :: CDouble,
  fuzz_        :: CDouble,
  fILTER       :: FilterTypes,
  interlace_   :: InterlaceType,
  endian_      :: EndianType,
  gravity      :: GravityType,
  compose      :: CompositeOp,
  dispose      :: DisposeType,
  scene        :: CULong,
  delay        :: CULong,
  iterations   :: CULong,
  total_colors :: CULong,
  start_loop   :: CLong,
  eRROR        :: ErrorInfo,
  timer        :: TimerInfo,
  client_data_ :: CIntPtr,
  filename_    :: String,
  magick_filename :: String,
  magick_      :: String,
  magick_rows  :: CULong,
  exception    :: ExceptionInfo,
  previous     :: CIntPtr,
  next         :: CIntPtr,
  -- private from here on
  profiles     :: CIntPtr,
  is_monochrome :: CUInt,
  is_grayscale :: CUInt,
  taint        :: CUInt,
  clip_mask    :: CIntPtr,
  cache_       :: CacheInfoPtr,
  attributes_  :: ImageAttributePtr,
  ascii85      :: Ascii85InfoPtr,
  blob_        :: BlobInfoPtr,
  reference_count :: CLong,
  semaphore    :: SemaphoreInfoPtr,
  logging      :: CUInt,
  list         :: CIntPtr,
  signature_   :: CULong
}