// Copyright (c) 2005-2008 ASCLEPIOS Project, INRIA Sophia-Antipolis (France)
// All rights reserved.
//
// This file is part of the ImageIO Library, and as been adapted for
// CGAL (www.cgal.org).
// You can redistribute it and/or  modify it under the terms of the
// GNU Lesser General Public License as published by the Free Software Foundation;
// either version 3 of the License, or (at your option) any later version.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// These files are provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// $URL: https://github.com/CGAL/cgal/blob/releases/CGAL-4.14.3/CGAL_ImageIO/include/CGAL/ImageIO/bmp_impl.h $
// $Id: bmp_impl.h 0698f79 2017-10-20T23:34:14+02:00 Sébastien Loriot
// SPDX-License-Identifier: LGPL-3.0+
//
//
// Author(s)     :  ASCLEPIOS Project (INRIA Sophia-Antipolis), Laurent Rineau

#ifdef CGAL_HEADER_ONLY
#define CGAL_INLINE_FUNCTION inline
#else
#define CGAL_INLINE_FUNCTION
#endif

#include <stdio.h>
#include <stdlib.h>
#include <CGAL/ImageIO/bmptypes.h>
#include <CGAL/ImageIO/bmpendian.h>
#include <CGAL/ImageIO/bmpread.h>






/** Magic header for bmp */
#define BMP_MAGIC "BM"


#ifdef CGAL_HEADER_ONLY

inline int& get_static_verbose_bmp()
{
  static int _VERBOSE_BMP_ = 1;
  return _VERBOSE_BMP_;
}

#else // CGAL_HEADER_ONLY

static int _VERBOSE_BMP_ = 1;
inline int& get_static_verbose_bmp()
{ return _VERBOSE_BMP_; }

#endif // CGAL_HEADER_ONLY


CGAL_INLINE_FUNCTION
int testBmpHeader(char *magic,const char *) {
  if( !strncmp(magic, BMP_MAGIC, strlen(BMP_MAGIC)))
    return 0;
  else 
    return -1;
}

CGAL_INLINE_FUNCTION
PTRIMAGE_FORMAT createBMPFormat() {
  PTRIMAGE_FORMAT f=(PTRIMAGE_FORMAT) ImageIO_alloc(sizeof(IMAGE_FORMAT));

  f->testImageFormat=&testBmpHeader;
  f->readImageHeader=&readBmpImage;
  f->writeImage=0;
  strcpy(f->fileExtension,".bmp");
  strcpy(f->realName,"BMP");
  return f;
}

CGAL_INLINE_FUNCTION
int readBmpImage( const char *name,_image *im)
{
  int dimx, dimy, dimv;

  im->data = _readBmpImage( name, &dimx,  &dimy,  &dimv );
  if ( im->data == NULL ) {
    fprintf( stderr, "readBmpImage: unable to read \'%s\'\n", name );
    return( -1 );
  }

  im->xdim = dimx;
  im->ydim = dimy;
  im->vdim = dimv;

  im->zdim = 1;

  im->wdim = 1;
  im->wordKind = WK_FIXED;
  im->sign = SGN_UNSIGNED;

  return 1;
}



CGAL_INLINE_FUNCTION
void *_readBmpImage( const char *name, 
		     int *dimx, int *dimy, int *dimz )
{
  const char *proc="_readBmpImage";
  void *buf = (void*)NULL;
  unsigned char *myBuf = NULL;

  FILE *fp = NULL;
  RGB **argbs = NULL;
  char **xorMasks = NULL, **andMasks = NULL;
  CGAL_UINT32 *heights = NULL, *widths = NULL, row = 0, col = 0;
  CGAL_UINT16 fileType = 0;
  long filePos = 0;
  int numImages = 0, i = 0;
  int rc = 0;
    
    fp = fopen(name, "rb");
    if (fp == NULL) {
      if ( get_static_verbose_bmp() ) 
	fprintf( stderr, "%s: error in opening %s\n", proc, name );
      return( (void*)NULL );
    }


    /*
     * Read the first two bytes as little-endian to determine the file type.
     * Preserve the file position.
     */
    filePos = ftell(fp);
    rc = readUINT16little(fp, &fileType);
    if (rc != 0) {
      fclose(fp);
      if ( get_static_verbose_bmp() ) 
	fprintf( stderr, "%s: error in getting file type %s\n", proc, name );
      return( (void*)NULL );
    }
    fseek(fp, filePos, SEEK_SET);

    /*
     * Read the images.
     */
    switch (fileType) {
    case TYPE_ARRAY:
	/*
	 * If this is an array of images, read them.  All the arrays we need
	 * will be allocated by the reader function.
	 */
	rc = readMultipleImage(fp, &argbs, &xorMasks, &andMasks, &heights,
			       &widths, &numImages);
	break;
    case TYPE_BMP:
    case TYPE_ICO:
    case TYPE_ICO_COLOR:
    case TYPE_PTR:
    case TYPE_PTR_COLOR:
	/*
	 * If this is a single-image file, we've a little more work.  In order
	 * to make the output part of this test program easy to write, we're
	 * going to allocate dummy arrays that represent what
	 * readMultipleImage would have allocated.  We'll read the data into
	 * those arrays.
	 */
	argbs = (RGB **)calloc(1, sizeof(RGB *));
	if (argbs == NULL)
	{
	    rc = 1005;
	    break;
	}
	xorMasks = (char **)calloc(1, sizeof(char *));
	if (xorMasks == NULL)
	{
	    free(argbs);
	    rc = 1005;
	    break;
	}
	andMasks = (char **)calloc(1, sizeof(char *));
	if (andMasks == NULL)
	{
	    free(argbs);
	    free(xorMasks);
	    rc = 1005;
	    break;
	}
	heights = (CGAL_UINT32 *)calloc(1, sizeof(CGAL_UINT32));
	if (heights == NULL)
	{
	    free(argbs);
	    free(xorMasks);
	    free(andMasks);
	    rc = 1005;
	    break;
	}
	widths = (CGAL_UINT32 *)calloc(1, sizeof(CGAL_UINT32));
	if (widths == NULL)
	{
	    free(argbs);
	    free(xorMasks);
	    free(andMasks);
	    free(heights);
	    rc = 1005;
	    break;
	}
	numImages = 1;

	/*
	 * Now that we have our arrays allocted, read the image into them.
	 */
	switch (fileType) {
	case TYPE_BMP:
	    rc = readSingleImageBMP(fp, argbs, widths, heights);
	    break;
	case TYPE_ICO:
	case TYPE_PTR:
	    rc = readSingleImageICOPTR(fp, xorMasks, andMasks, widths,
				       heights);
	    break;
	case TYPE_ICO_COLOR:
	case TYPE_PTR_COLOR:
	    rc = readSingleImageColorICOPTR(fp, argbs, xorMasks, andMasks,
					    widths, heights);
	    break;
	}
	break;
    default:
	rc = 1000;
    }
    fclose(fp);


  
    /*
     * At this point, everything's been read.  Display status messages based
     * on the return values.
     */
    switch (rc) {
    case 1000:
    case 1006:
      if ( get_static_verbose_bmp() ) 
	fprintf( stderr, "%s: File is not a valid bitmap file\n", proc );
      break;
    case 1001:
      if ( get_static_verbose_bmp() ) 
	fprintf( stderr, "%s: Illegal information in an image\n", proc );
      break;
    case 1002:
      if ( get_static_verbose_bmp() ) 
	fprintf( stderr, "%s: Legal information that I can't handle yet in an image\n", proc );
      break;
    case 1003:
    case 1004:
    case 1005:
      if ( get_static_verbose_bmp() ) 
	fprintf( stderr, "%s: Ran out of memory\n", proc );
      break;
    case 0:
      if ( get_static_verbose_bmp() > 1 ) 
	fprintf( stderr, "%s: Got good data from file, writing results\n", proc );
      break;
    default:
      if ( get_static_verbose_bmp() ) 
	fprintf( stderr, "%s: Error reading file rc=%d\n", proc,rc );
      break;
    }

    /*
     * If the return value wasn't 0, something went wrong.
     */
    if (rc != 0)
    {
	if (rc != 1000 && rc != 1005)
	{
	    for (i=0; i<numImages; i++)
	    {
		if (argbs[i] != NULL)
		    free(argbs[i]);
		if (andMasks[i] != NULL)
		    free(andMasks[i]);
		if (xorMasks[i] != NULL)
		    free(xorMasks[i]);
	    }
	    free(argbs);
	    free(andMasks);
	    free(xorMasks);
	    free(widths);
	    free(heights);
	}
	return( (void*)NULL );
    }
    
    
    /*
     * Dump the images.
     */
    if ( get_static_verbose_bmp() > 1 ) 
      fprintf (stderr, "%s: There are %d images in the file\n", proc, numImages);

    if ( numImages >= 2 ) 
      fprintf (stderr, "%s: read only first image among %d\n", proc, numImages );
    
    /*
     * my stuff:
     * just reading one bmp image
     */
    if ( (numImages > 0) &&
	 (argbs[0] != NULL) ) {

      buf = (void*)malloc( widths[0]*heights[0]*3 * sizeof( unsigned char ) );
      if ( buf == (void*)NULL ) {
	if ( get_static_verbose_bmp() ) 
	  fprintf( stderr, "%s: error in allocating data buffer for %s\n", proc, name );

	for (i=0; i<numImages; i++) {
	  if (argbs[i] != NULL)
	    free(argbs[i]);
	  if (andMasks[i] != NULL)
	    free(andMasks[i]);
	  if (xorMasks[i] != NULL)
	    free(xorMasks[i]);
	}
	free(argbs);
	free(andMasks);
	free(xorMasks);
	free(widths);
	free(heights);

	return( (void*)NULL );
      }

      myBuf = (unsigned char*)buf;
      i = 0;
      for (row = 0; row < heights[0]; row++)
      for (col = 0; col < widths[0]; col++,i+=3) {
	myBuf[i  ] = argbs[0][row * widths[0] + col].red;
	myBuf[i+1] = argbs[0][row * widths[0] + col].green;
	myBuf[i+2] = argbs[0][row * widths[0] + col].blue;
      }
      
      *dimx = int(widths[0]);
      *dimy = int(heights[0]);
      *dimz = 3;

    } else {
      if ( get_static_verbose_bmp() ) 
	fprintf( stderr, "%s: no image or null image\n", proc );
      
      for (i=0; i<numImages; i++) {
	if (argbs[i] != NULL)
	  free(argbs[i]);
	if (andMasks[i] != NULL)
	  free(andMasks[i]);
	if (xorMasks[i] != NULL)
	  free(xorMasks[i]);
      }
      free(argbs);
      free(andMasks);
      free(xorMasks);
      free(widths);
      free(heights);
      
      return( (void*)NULL );
    }



    for (i=0; i<numImages; i++) {
      if (argbs[i] != NULL)
	free(argbs[i]);
      if (andMasks[i] != NULL)
	free(andMasks[i]);
      if (xorMasks[i] != NULL)
	free(xorMasks[i]);
    }
    free(argbs);
    free(andMasks);
    free(xorMasks);
    free(widths);
    free(heights);
    
    return( buf );





#if 0
    /*
     * old stuff from test.c
     */

    for (i=0; i<numImages; i++)
    {
	/*
	 * Loop through all the images that were returned.
	 */
      if ( get_static_verbose_bmp() ) {
	fprintf (stderr, "%s: Doing image number %d\n\n", proc, i+1);
	fprintf (stderr, "%s: Image dimensions: (%ld,%ld)\n", proc, widths[i], heights[i]);
      }

      if (argbs[i] != NULL) {
	/*
	 * If the image has colors, dump them (BMP, color ICO and color
	 * PTR files
	 */
	fprintf(stderr, "Colors");
	for (row = 0; row < heights[i]; row++)
	  {
	    fprintf (stderr, "\n\nRow %ld pixels (R,G,B), hex values:\n",
		     row);
	    for (col = 0; col < widths[i]; col++)
	      {
		fprintf (stderr, "(%2.2x,%2.2x,%2.2x)",
			 argbs[i][row * widths[i] + col].red,
			 argbs[i][row * widths[i] + col].green,
			 argbs[i][row * widths[i] + col].blue);
	      }
	  }
      } else {
	/*
	 * If there image has no colors, say so.  (monochrome ICO and PTR
	 * files) 
	 */
	fprintf (stderr, "No color image\n");
      }


      
      if (xorMasks[i] != NULL) {
	/*
	 * If the image has an xor mask, dump it.  (ICO and PTR files)
	 */
	fprintf (stderr, "\nXOR mask\n");
	for (row = 0; row < heights[i]; row++)
	  {
	    for (col = 0; col < widths[i]; col++)
	      {
		fprintf (stderr, "%c",
			 xorMasks[i][row * widths[i] + col] ? '@' : '.');
	      }
	    fprintf (stderr, "\n");
	  }
      } else {
	/*
	 * If the image has no xor mask, say so.  (BMP files).
	 */
	fprintf (stderr, "No xor mask\n");
      }
      


      if (andMasks[i] != NULL) {
	/*
	 * If the image has an and mask, dump it.  (ICO and PTR files)
	 */
	fprintf (stderr, "\nAND mask\n");
	for (row = 0; row < heights[i]; row++)
	  {
	    for (col = 0; col < widths[i]; col++)
	      {
		fprintf (stderr, "%c",
			 andMasks[i][row * widths[i] + col] ? '@' : '.');
	      }
	    fprintf (stderr, "\n");
	  }
      } else {
	/*
	 * If the image has noand mask, say so.  (BMP files)
	 */
	fprintf (stderr, "No and mask\n");
      }
      

      if (i != numImages-1)
	fprintf (stderr, "\n------------------------------------------\n\n");
      
    }
    
    /*
     * Dumping is complete.  Free all the arrays and quit
     */
    for (i=0; i<numImages; i++)
    {
	if (argbs[i] != NULL)
	    free(argbs[i]);
	if (andMasks[i] != NULL)
	    free(andMasks[i]);
	if (xorMasks[i] != NULL)
	    free(xorMasks[i]);
    }
    free(argbs);
    free(andMasks);
    free(xorMasks);
    free(widths);
    free(heights);
    
    return( buf );
#endif
}

CGAL_INLINE_FUNCTION
void IoBmp_verbose ( )
{
  if ( get_static_verbose_bmp() <= 0 )
    get_static_verbose_bmp() = 1;
  else 
    get_static_verbose_bmp() += 1;
}

CGAL_INLINE_FUNCTION
void IoBmp_noverbose ( )
{
  get_static_verbose_bmp() = 0;
}
