    -- | This module provide a totally partial and incomplete maping
    -- of Exif values. Used for Tiff parsing and reused for Exif extraction.
    module Codec.Picture.Metadata.Exif ( ExifTag( .. )
                                       , ExifData( .. )

                                       , tagOfWord16
                                       , word16OfTag
                                       ) where

    import Control.DeepSeq( NFData( .. ) )
    import Data.Int( Int32 )
    import Data.Word( Word16, Word32 )
    import qualified Data.Vector as V
    import qualified Data.ByteString as B

    -- | Tag values used for exif fields. Completly incomplete
    data ExifTag
      = TagPhotometricInterpretation
      | TagCompression -- ^ Short type
      | TagImageWidth  -- ^ Short or long type
      | TagImageLength -- ^ Short or long type
      | TagXResolution -- ^ Rational type
      | TagYResolution -- ^ Rational type
      | TagResolutionUnit --  ^ Short type
      | TagRowPerStrip -- ^ Short or long type
      | TagStripByteCounts -- ^ Short or long
      | TagStripOffsets -- ^ Short or long
      | TagBitsPerSample --  ^ Short
      | TagColorMap -- ^ Short
      | TagTileWidth
      | TagTileLength
      | TagTileOffset
      | TagTileByteCount
      | TagSamplesPerPixel -- ^ Short
      | TagArtist
      | TagDocumentName
      | TagSoftware
      | TagPlanarConfiguration -- ^ Short
      | TagOrientation
      | TagSampleFormat -- ^ Short
      | TagInkSet
      | TagSubfileType
      | TagFillOrder
      | TagYCbCrCoeff
      | TagYCbCrSubsampling
      | TagYCbCrPositioning
      | TagReferenceBlackWhite
      | TagXPosition
      | TagYPosition
      | TagExtraSample
      | TagImageDescription
      | TagPredictor
      | TagCopyright
      | TagMake
      | TagModel
      | TagDateTime
      | TagGPSInfo

      | TagJpegProc
      | TagJPEGInterchangeFormat
      | TagJPEGInterchangeFormatLength
      | TagJPEGRestartInterval
      | TagJPEGLosslessPredictors
      | TagJPEGPointTransforms
      | TagJPEGQTables
      | TagJPEGDCTables
      | TagJPEGACTables

      | TagExifOffset
      | TagUnknown !Word16
      deriving (Eq, Ord, Show)

    -- | Convert a value to it's corresponding Exif tag.
    -- Will often be written as 'TagUnknown'
    tagOfWord16 :: Word16 -> ExifTag
    tagOfWord16 v = case v of
      255 -> TagSubfileType
      256 -> TagImageWidth
      257 -> TagImageLength
      258 -> TagBitsPerSample
      259 -> TagCompression
      262 -> TagPhotometricInterpretation
      266 -> TagFillOrder
      269 -> TagDocumentName
      270 -> TagImageDescription
      271 -> TagMake
      272 -> TagModel
      273 -> TagStripOffsets
      274 -> TagOrientation
      277 -> TagSamplesPerPixel
      278 -> TagRowPerStrip
      279 -> TagStripByteCounts
      282 -> TagXResolution
      283 -> TagYResolution
      284 -> TagPlanarConfiguration
      286 -> TagXPosition
      287 -> TagYPosition
      296 -> TagResolutionUnit
      305 -> TagSoftware
      306 -> TagDateTime
      315 -> TagArtist
      317 -> TagPredictor
      320 -> TagColorMap
      322 -> TagTileWidth
      323 -> TagTileLength
      324 -> TagTileOffset
      325 -> TagTileByteCount
      332 -> TagInkSet
      338 -> TagExtraSample
      339 -> TagSampleFormat
      529 -> TagYCbCrCoeff
      512 -> TagJpegProc
      513 -> TagJPEGInterchangeFormat
      514 -> TagJPEGInterchangeFormatLength
      515 -> TagJPEGRestartInterval
      517 -> TagJPEGLosslessPredictors
      518 -> TagJPEGPointTransforms
      519 -> TagJPEGQTables
      520 -> TagJPEGDCTables
      521 -> TagJPEGACTables
      530 -> TagYCbCrSubsampling
      531 -> TagYCbCrPositioning
      532 -> TagReferenceBlackWhite
      33432 -> TagCopyright
      34665 -> TagExifOffset
      34853 -> TagGPSInfo
      vv -> TagUnknown vv

    -- | Convert a tag to it's corresponding value.
    word16OfTag :: ExifTag -> Word16
    word16OfTag t = case t of
      TagSubfileType -> 255
      TagImageWidth -> 256
      TagImageLength -> 257
      TagBitsPerSample -> 258
      TagCompression -> 259
      TagPhotometricInterpretation -> 262
      TagFillOrder -> 266
      TagDocumentName -> 269
      TagImageDescription -> 270
      TagMake -> 271
      TagModel -> 272
      TagStripOffsets -> 273
      TagOrientation -> 274
      TagSamplesPerPixel -> 277
      TagRowPerStrip -> 278
      TagStripByteCounts -> 279
      TagXResolution -> 282
      TagYResolution -> 283
      TagPlanarConfiguration -> 284
      TagXPosition -> 286
      TagYPosition -> 287
      TagResolutionUnit -> 296
      TagSoftware -> 305
      TagDateTime -> 306
      TagArtist -> 315
      TagPredictor -> 317
      TagColorMap -> 320
      TagTileWidth -> 322
      TagTileLength -> 323
      TagTileOffset -> 324
      TagTileByteCount -> 325
      TagInkSet -> 332
      TagExtraSample -> 338
      TagSampleFormat -> 339
      TagYCbCrCoeff -> 529
      TagJpegProc -> 512
      TagJPEGInterchangeFormat -> 513
      TagJPEGInterchangeFormatLength -> 514
      TagJPEGRestartInterval -> 515
      TagJPEGLosslessPredictors -> 517
      TagJPEGPointTransforms -> 518
      TagJPEGQTables -> 519
      TagJPEGDCTables -> 520
      TagJPEGACTables -> 521
      TagYCbCrSubsampling -> 530
      TagYCbCrPositioning -> 531
      TagReferenceBlackWhite -> 532
      TagCopyright -> 33432
      TagExifOffset -> 34665
      TagGPSInfo -> 34853
      (TagUnknown v) -> v

    -- | Possible data held by an Exif tag
    data ExifData
      = ExifNone
      | ExifLong      !Word32
      | ExifShort     !Word16
      | ExifString    !B.ByteString
      | ExifUndefined !B.ByteString
      | ExifShorts    !(V.Vector Word16)
      | ExifLongs     !(V.Vector Word32)
      | ExifRational  !Word32 !Word32
      | ExifSignedRational  !Int32 !Int32
      | ExifIFD       ![(ExifTag, ExifData)]
      deriving Show

    instance NFData ExifTag where
      rnf a = a `seq` ()

    instance NFData ExifData where
      rnf (ExifIFD ifds) = rnf ifds `seq` ()
      rnf (ExifLongs l) = rnf l `seq` ()
      rnf (ExifShorts l) = rnf l `seq` ()
      rnf a = a `seq` ()