/* Copyright (c) 2012 MJ Rutter 
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version 2
 * as published by the Free Software Foundation.
 * 
 * This program 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA  02110-1301, USA.
 */ 


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "bmp2eps.h"

unsigned to_rle(unsigned char *raw,unsigned char *rle, unsigned len);
unsigned to_group3(struct bitmap *image, unsigned char **out);
unsigned to_group4(struct bitmap *image, unsigned char **out);
void lzw_flate(struct bitmap *image);

void ppmread(FILE *in, struct bitmap *image, int binary);
void pgmread(FILE *in, struct bitmap *image, int binary);
void pbmread(FILE *in, struct bitmap *image, int binary);
void gifread(FILE *in, struct bitmap *image);
void jpegread(FILE *in, struct bitmap *image);
void bmpread(FILE *in, struct bitmap *image);
#ifdef USE_LIBPNG
void pngread(FILE *in, struct bitmap *image);
#endif
void tgaread(FILE *in, struct bitmap *image, unsigned char *buff);

void grey_check(struct bitmap *image);
int grey_bits(struct bitmap *image);
void palette_check(struct bitmap *image);
void colour_count(struct bitmap *image);


int read_image(struct bitmap *image, FILE *infile,
	       int user_trans, unsigned int ctrans[3]){

  int i,j,debug,compress;
#ifndef FIG2PDF
  unsigned char *imagez,*p;
#endif
  unsigned char buff[10];
  static unsigned char trans_buff[3];
  unsigned int width,height;

  debug=image->flags&DEBUG_MASK;

  /* Check magic */

  for(j=0;j<=2;j++){
    i=fgetc(infile);

    if(i==EOF){
      fprintf(stderr,"Unable to read from input.\n");
      exit(1);
    }
    
    buff[j]=i;
  }

  if ((buff[0]=='P')
      &&((buff[1]=='6')||(buff[1]=='3'))
      &&(strchr(" \t\r\n",buff[2]))) ppmread(infile,image,
                                             (buff[1]=='6')?1:0);
  else if ((buff[0]=='P')
      &&((buff[1]=='5')||(buff[1]=='2'))
      &&(strchr(" \t\r\n",buff[2]))) pgmread(infile,image,
                                             (buff[1]=='5')?1:0);
  else if ((buff[0]=='P')
      &&((buff[1]=='4')||(buff[1]=='1'))
      &&(strchr(" \t\r\n",buff[2]))) pbmread(infile,image,
                                             (buff[1]=='4')?1:0);
  else if ((buff[0]=='G')
           &&(buff[1]=='I')
           &&(buff[2]=='F')) gifread(infile,image);
  else if ((buff[0]==0xff)
           &&(buff[1]==0xd8)
           &&(buff[2]==0xff)) jpegread(infile,image);
  else if ((buff[0]==137)
           &&(buff[1]=='P')
           &&(buff[2]=='N')){
#ifdef USE_LIBPNG
    pngread(infile,image);
#else
    fprintf(stderr,"Error, PNG not supported."
                   "Try 'pngtopnm file.png | bmp2eps'\n");
    exit(1);
#endif
  }
#ifndef FIG2PDF
  else if ((buff[0]=='B')
           &&(buff[1]=='M')) bmpread(infile,image);
  else if (((buff[1]&0xfe)==0)&&(buff[2]!=0)&&((buff[2]&0xf4)==0)) /* Maybe a TGA */
    tgaread(infile,image,buff);
#endif
  else {
    fprintf(stderr,"Error, unknown file type.\n");
    exit(1);
  }

  if (debug>1) fprintf(stderr,"Data read, %d bytes\n",image->data_len);

  width=image->width;
  height=image->height;
  compress=image->compression;

  if (debug){
    fprintf(stderr,"Image %d x %d depth %d",width,height,
            image->depth);
    if (image->type==PALETTE)
      fprintf(stderr," with palette of %d entries",image->pal_len);
    else if (image->type==DCT)
      fprintf(stderr," with DCT compression");
    if (image->colour==GREY) fprintf(stderr," greyscale");
    fprintf(stderr,"\n");
  }

/* Sort out transparency issues */

  switch (user_trans){
  case -1: /* User wants transparency ignored */
    if (image->trans) image->trans=NULL;
    break;
  case 1: /* User wants given colour to be transparent */
    if (image->type==PALETTE){
      for(i=0;i<image->pal_len;i++)
        if(((unsigned char)ctrans[0]==image->palette[3*i])&&
           ((unsigned char)ctrans[1]==image->palette[3*i+1])&&
           ((unsigned char)ctrans[2]==image->palette[3*i+2])){
           image->trans=trans_buff;
           *image->trans=(unsigned char)i;
           break;
        }
    }
    else{
      image->trans=trans_buff;
      image->trans[0]=(unsigned char)ctrans[0];
      image->trans[1]=(unsigned char)ctrans[1];
      image->trans[2]=(unsigned char)ctrans[2];
    }
    break;
  }

  /* If we did not construct palette, check for trailing unused entries */

  if ((image->depth==8)&&(image->type==PALETTE)) palette_check(image);

  /* Check full colour image to ensure more than 256 colours */

  if ((image->depth==24)&&(image->type==BMP)&&
      (!(image->flags&KEEP24))) colour_count(image);

  /* Check paletted images for greyscale conversion */

  if ((image->depth==8)&&(image->type==PALETTE)) grey_check(image);

  /* Compact images with few palette entries */

  if ((image->type==PALETTE)&&(image->pal_len<=16)&&(image->depth==8))
      compact(image); 

  /* Try to reduce greyscale images in a similar fashion */

  if ((image->depth==8)&&(image->type==BMP)&&(image->colour==GREY)){
    i=grey_bits(image);
    if (debug>1) fprintf(stderr,"Greyscale bits used: %d\n",i);
    if ((i==1)||(i==2)||(i==4)){
      image->pal_len=1<<i;
      compact(image);
      image->pal_len=0;
    }
  }
    
  /* Compression */

#ifndef FIG2PDF
  if ((compress==CCITT)||(compress==CCITTG4)){
    if (image->depth!=1){
      if (debug) fprintf(stderr,"CCITT compression requested, but image depth not 1\n"
                         "Reverting to default compression\n");
      compress=DEFAULT_COMPRESS;
    }
    else{
      if (compress==CCITT) i=to_group3(image,&p);
      else i=to_group4(image,&p);
      if (i){
        if (debug>1) fprintf(stderr,
                             "Before CCITT %d bytes\nAfter CCITT:  %d bytes\n",
                             image->data_len,i);
        free(image->data);
        image->data=p;
        image->data_len=i;
      }
      else compress=0;
    }
  }

  if (compress==RLE){
    if(image->depth==1){
      i=to_group4(image,&p);
      if (i){
        if (debug>1) fprintf(stderr,
                             "Before CCITT %d bytes\nAfter CCITT:  %d bytes\n",
                             image->data_len,i);
        free(image->data);
        image->data=p;
        image->data_len=i;
        compress=CCITTG4;
      }
      else compress=0;
    }
    else if (image->depth<=8){

      if (!(imagez=malloc(image->data_len+2+(image->data_len)/100))){
        fprintf(stderr,"Malloc error for RLE\n");
        exit(1);
      }

      i=to_rle(image->data,imagez,image->data_len);

      if (debug>1) fprintf(stderr,
                           "Before rle: %d bytes\nAfter rle:  %d bytes\n",
                           image->data_len,i);

      if(i<image->data_len){
        p=realloc(imagez,image->data_len);
        if (p) imagez=p;
        free(image->data);
        image->data=imagez;
        image->data_len=i;
      } else{
        compress=0;
        free(imagez);
      }
    }
    else if (image->depth==24) compress=RLE24;
    else{
      fprintf(stderr,"RLE specified with unexpected depth %d\n",image->depth);
      exit(1);
    }
  }
#endif

  image->compression=compress;

  if ((compress==LZW)||(compress==FLATE)) lzw_flate(image);

  return 0;

}
