/* 
   NSBitmapImageRep.m

   Bitmap image representation.

   Copyright (C) 1996 Free Software Foundation, Inc.
   
   Author:  Adam Fedor <fedor@colorado.edu>
   Date: Feb 1996
   
   This file is part of the GNUstep GUI Library.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.
   
   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.
   
   You should have received a copy of the GNU Library General Public
   License along with this library; see the file COPYING.LIB.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include <gnustep/gui/config.h>
#include <stdlib.h>
#include <math.h>
#include <tiff.h>

#include <Foundation/NSException.h>
#include <Foundation/NSArray.h>
#include <Foundation/NSData.h>
#include <AppKit/NSGraphics.h>
#include <AppKit/NSPasteboard.h>
#include <AppKit/NSBitmapImageRep.h>
#include <AppKit/AppKitExceptions.h>

#include <gnustep/gui/config.h>
#include <gnustep/gui/nsimage-tiff.h>

/* Maximum number of planes */
#define MAX_PLANES 5

/* Backend protocol - methods that must be implemented by the backend to
   complete the class */
@protocol NXBitmapImageRepBackend
- (BOOL) draw;
@end

@implementation NSBitmapImageRep 

/* Given a TIFF image (from the libtiff library), load the image information
   into our data structure.  Reads the specified image. */
- _initFromImage: (TIFF *)image number: (int)imageNumber
{
  NSString* space;
  NSTiffInfo* info;

  info = NSTiffGetInfo(imageNumber, image);
  if (!info) 
    {
      [NSException raise:NSTIFFException format: @"Read invalid TIFF info"];
    }

  /* 8-bit RGB will be converted to 24-bit by the tiff routines, so account
     for this. */
  space = nil;
  switch(info->photoInterp) 
    {
    case PHOTOMETRIC_MINISBLACK: space = NSDeviceWhiteColorSpace; break;
    case PHOTOMETRIC_MINISWHITE: space = NSDeviceBlackColorSpace; break;
    case PHOTOMETRIC_RGB: space = NSDeviceRGBColorSpace; break;
    case PHOTOMETRIC_PALETTE: 
      space = NSDeviceRGBColorSpace; 
      info->samplesPerPixel = 3;
      break;
    default:
      break;
    }

  [self initWithBitmapDataPlanes: NULL
		pixelsWide: info->width
		pixelsHigh: info->height
		bitsPerSample: info->bitsPerSample
		samplesPerPixel: info->samplesPerPixel
		hasAlpha: (info->samplesPerPixel > 3)
		isPlanar: (info->planarConfig == PLANARCONFIG_SEPARATE)
		colorSpaceName: space
		bytesPerRow: 0
		bitsPerPixel: 0];
  compression = info->compression;

  if (NSTiffRead(imageNumber, image, NULL, [self bitmapData]))
    {
      [NSException raise:NSTIFFException format: @"Read invalid TIFF image"];
    }

  return self;
}

+ (id) imageRepWithData: (NSData *)tiffData
{
  NSArray* array;

  array = [self imageRepsWithData: tiffData];
  if ([array count])
    return [array objectAtIndex: 0];
  return nil;
}

+ (NSArray *) imageRepsWithData: (NSData *)tiffData
{
  int images;
  TIFF*       image;
  NSTiffInfo* info;
  NSMutableArray* array;

  image = NSTiffOpenData((char *)[tiffData bytes], [tiffData length], 
			 "r", NULL);
  if (!image)
    {
      [NSException raise:NSTIFFException format: @"Read invalid TIFF data"];
    }

  array = [NSMutableArray arrayWithCapacity:1];
  images = 0;
  while ((info = NSTiffGetInfo(images, image))) 
    {
      NSBitmapImageRep* imageRep;

      OBJC_FREE(info);
      imageRep = [[[[self class] alloc]
		   _initFromImage: image number: images] autorelease];
      [array addObject: imageRep];
      images++;
    }
  NSTiffClose(image);

  return array;
}

/* Loads only the default (first) image from the TIFF image contained in
   data. */
- (id) initWithData: (NSData *)tiffData
{
  TIFF 	*image;

  image = NSTiffOpenData((char *)[tiffData bytes], [tiffData length], 
			 "r", NULL);
  if (!image)
    {
      [NSException raise:NSTIFFException format: @"Read invalid TIFF data"];
    }

  [self _initFromImage:image number: -1];
  NSTiffClose(image);
  return self;
}

- (id) initWithFocusedViewRect: (NSRect)rect
{
  return [self notImplemented: _cmd];
}

/* This is the designated initializer */
/* Note: If data is actaully passed to us in planes, we DO NOT own this
   data and we DO NOT copy it. Just assume that it will always be available.
*/
- (id) initWithBitmapDataPlanes: (unsigned char **)planes
		pixelsWide: (int)width
		pixelsHigh: (int)height
		bitsPerSample: (int)bps
		samplesPerPixel: (int)spp
		hasAlpha: (BOOL)alpha
		isPlanar: (BOOL)isPlanar
		colorSpaceName: (NSString *)colorSpaceName
		bytesPerRow: (int)rowBytes
		bitsPerPixel: (int)pixelBits;
{
  if (!bps || !spp || !width || !height) 
    {
      [NSException raise: NSInvalidArgumentException
        format: @"Required arguments not specified creating NSBitmapImageRep"];
    }

  _pixelsWide = width;
  _pixelsHigh = height;
  size.width  = width;
  size.height = height;
  bitsPerSample = bps;
  numColors     = spp;
  hasAlpha   = alpha;  
  _isPlanar   = isPlanar;
  _colorSpace = [colorSpaceName retain];
  if (!pixelBits)
    pixelBits = bps * ((_isPlanar) ? 1 : spp);
  bitsPerPixel            = pixelBits;
  if (!rowBytes) 
    rowBytes = ceil((float)width * bitsPerPixel / 8);
  bytesPerRow            = rowBytes;

  if (planes) 
    {
      int i;
      OBJC_MALLOC(imagePlanes, unsigned char*, MAX_PLANES);
      for (i = 0; i < MAX_PLANES; i++)
 	imagePlanes[i] = NULL;
      for (i = 0; i < ((_isPlanar) ? numColors : 1); i++)
 	imagePlanes[i] = planes[i];
    }
  return self;
}

- (void) dealloc
{
  OBJC_FREE(imagePlanes);
  [imageData release];
  [super dealloc];
}

+ (BOOL) canInitWithData: (NSData *)data
{
  TIFF *image = NULL;
  image = NSTiffOpenData((char *)[data bytes], [data length], "r", NULL);
  NSTiffClose(image);

  return (image) ? YES : NO;
}

+ (BOOL) canInitWithPasteboard: (NSPasteboard *)pasteboard
{
  [self notImplemented: _cmd];
  return NO;
}

+ (NSArray *) imageFileTypes
{
  return [self imageUnfilteredFileTypes];
}

+ (NSArray *) imagePasteboardTypes
{
  return [self imageUnfilteredPasteboardTypes];
}

+ (NSArray *) imageUnfilteredFileTypes
{
  return [NSArray arrayWithObjects: @"tiff", @"tif", nil];
}

+ (NSArray *) imageUnfilteredPasteboardTypes
{
  return [NSArray arrayWithObjects: NSTIFFPboardType, nil];
}

//
// Getting Information about the Image 
//
- (int) bitsPerPixel
{
  return bitsPerPixel;
}

- (int) samplesPerPixel
{
  return numColors;
}

- (BOOL) isPlanar
{
  return _isPlanar;
}

- (int) numberOfPlanes
{
  return (_isPlanar) ? numColors : 1;
}

- (int) bytesPerPlane
{
  return bytesPerRow*_pixelsHigh;
}

- (int) bytesPerRow
{
  return bytesPerRow;
}

//
// Getting Image Data 
//
- (unsigned char *) bitmapData
{
  unsigned char *planes[MAX_PLANES];
  [self getBitmapDataPlanes: planes];
  return planes[0];
}

- (void) getBitmapDataPlanes: (unsigned char **)data
{
  int i;

  if (!imagePlanes || !imagePlanes[0])
    {
      long length;
      unsigned char* bits;
      
      length = (long)numColors * bytesPerRow * _pixelsHigh 
 	* sizeof(unsigned char);
      imageData = [[NSMutableData dataWithLength: length] retain];
      if (!imagePlanes)
 	OBJC_MALLOC(imagePlanes, unsigned char*, MAX_PLANES);
      bits = [imageData mutableBytes];
      if (_isPlanar) 
	{
	  for (i=1; i < numColors; i++) 
	    imagePlanes[i] = bits + i*bytesPerRow * _pixelsHigh;
	  for (i= numColors; i < MAX_PLANES; i++) 
	    imagePlanes[i] = NULL;
	}
      else
	{
	  imagePlanes[0] = bits;
	  for (i= 1; i < MAX_PLANES; i++) 
	    imagePlanes[i] = NULL;
	}
      
    }
  if (data)
    for (i=0; i < numColors; i++)
      data[i] = imagePlanes[i];
}

- (BOOL) draw
{
  return NO;
}

//
// Producing a TIFF Representation of the Image 
//
+ (NSData *) TIFFRepresentationOfImageRepsInArray: (NSArray *)anArray
{
  [self notImplemented: _cmd];
  return nil;
}

+ (NSData *) TIFFRepresentationOfImageRepsInArray: (NSArray *)anArray
		usingCompression: (NSTIFFCompression)compressionType
		factor: (float)factor
{
  [self notImplemented: _cmd];
  return nil;
}

- (NSData *) TIFFRepresentation
{
  [self notImplemented: _cmd];
  return nil;
}

- (NSData *) TIFFRepresentationUsingCompression: (NSTIFFCompression)compressionType
		factor: (float)factor
{
  [self notImplemented: _cmd];
  return nil;
}

//
// Setting and Checking Compression Types 
//
+ (void) getTIFFCompressionTypes: (const NSTIFFCompression **)list
		count: (int *)numTypes
{
  [self notImplemented: _cmd];
}

+ (NSString *) localizedNameForTIFFCompressionType: (NSTIFFCompression)compression
{
  [self notImplemented: _cmd];
  return nil;
}

- (BOOL) canBeCompressedUsing: (NSTIFFCompression)compression
{
  [self notImplemented: _cmd];
  return NO;
}

- (void) getCompression: (NSTIFFCompression *)compression
		factor: (float *)factor
{
  [self notImplemented: _cmd];
}

- (void) setCompression: (NSTIFFCompression)compression
		factor: (float)factor
{
  [self notImplemented: _cmd];
}

//
// NSCoding protocol
//
- (void) encodeWithCoder: aCoder
{
  [self notImplemented: _cmd];
}

- initWithCoder: aDecoder
{
  [self notImplemented: _cmd];
  return nil;
}

@end
