/* 
 * Thomas Pantzer: libfuncs.cc, v0.1b  10.8.99 19:1 
 * 
 * 
 * Copyright 1999 by Thomas Pantzer
 * 
 * Permission to use, copy, modify, and distribute this software for noncommercial 
 * use and without fee is hereby granted, provided that the above copyright notice 
 * appear in all copies and that both that copyright notice and this permission 
 * notice appear in supporting documentation, and that the name of the Author not be 
 * used in advertising or publicity pertaining to distribution of the software 
 * without specific, written prior permission. The Author makes no representations about
 * the suitability of this software for any purpose.  It is provided "as is" 
 * without expressed or implied warranty. 
 * 
 * 
 * pantec@isys2.informatik.uni-leipzig.de   (Thomas Pantzer) 
 * 
 */



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

#include "copyright.h"

#include "rle_bits.h"

// int save_idx = 0;
// int read_idx = 0;


#ifdef DEBUG
unsigned int max = 0;
unsigned long int avg = 0;
unsigned long int cnt = 0;

unsigned char max_pixel = 0;
unsigned char min_pixel = 255;

#endif

char rle_warning_done = false;

int ncolors2write = 0;

#define LINE_BUFSIZE	(4*2048)


void rle_init()
{
	LINE = new unsigned char [LINE_BUFSIZE + 256];
        rle_Colormap = new t_color [256];
	rle_Savemap = NULL;
	line_ptr = 0;
        ncolors_read = false;
	READLN = NULL;
	read_ptr = 0;
}


void rle_done()
{	
	if (LINE != NULL)
	   delete LINE;

	if (rle_Colormap != NULL)
	   delete rle_Colormap;

	if (READLN != NULL)
	   delete READLN;
}

void rle_saveto_line(int fd)
{
//      line_ptr = 0;
//      for (int i=0; i<items; i++)
//	{
	 if (LINE == NULL)
	    return;

	 LINE[line_ptr] = Last.Pixel;
	 line_ptr++;

	 unsigned long L = Last.Len;

	 int i = 0;
         while (L > 0)
           {
	    unsigned char c = (unsigned char)(L & 0x7f);

	    if (L > 127)
	       c |= 0x80;	

	    L = L >> 7;

	    LINE[line_ptr] = c;
	    line_ptr++;
	    i++;
	   } 
#ifdef DEBUG
         if (i > 2)
	    fprintf(stderr,"3-byte length detected\n");  
#endif
//	}  

      if (line_ptr > LINE_BUFSIZE)
	 {
          write(fd,LINE,line_ptr);
          line_ptr = 0;
         }
}

int rle_lookup( t_color *C)
{
	int i=0;

	if (ncolors2write > 0)
	   {
	    for (i=0; i<ncolors2write; i++)
	      {
	       t_color *F = &rle_Colormap[i];	
	       if ((F->r == C->r) && (F->g == C->g) && (F->b == C->b))
		  return i; 	
	      }	
	   }

	if (i < 256)
	   {
	    rle_Colormap[i].r = C->r;
	    rle_Colormap[i].g = C->g;
	    rle_Colormap[i].b = C->b;
	
	    ncolors2write++;
		
	    return i;	
	   }

	return -1;
}

void rle_save_3dpixel(int fd, t_color *C)
{
	int i = rle_lookup(C);

	if ((i >=0) && (i <= 255))
	   {
	    unsigned char c = (unsigned char)i;
	    rle_save_pixel(fd,c);	
	    return;	
	   }

	if (rle_warning_done == false)	
	   {
	    rle_warning_done = true;
	    fprintf(stderr,"num colors exceed 256\n");	
	   }

	rle_save_pixel(fd,0);
}

void rle_save_pixel(int fd, unsigned char C)
{
	if (Last.Pixel == C)
           {
            Last.Len++;
#ifdef DEBUG
	    if (Last.Len > max)
	       max = Last.Len;	
#endif
           }
         else
           {
#ifdef DEBUG
	    cnt++;
	    avg += Last.Len;

            if (max_pixel < C)
	       max_pixel = C;

            if (min_pixel > C)
	       min_pixel = C;
#endif	
	    if (Last.Len > 0)
               rle_saveto_line(fd);

	    Last.Pixel = C;
            Last.Len = 1;
	   } 
}



void rle_sync(int fd)
{
	rle_saveto_line(fd);
        write(fd,LINE,line_ptr);

	if (rle_Colormap == rle_Savemap)
	   {	
	    lseek(fd, 128, SEEK_SET);
	    write(fd,rle_Colormap,sizeof(t_color)*256);
	    fprintf(stderr,"truecolor image written, %d colors detected\n",ncolors2write);	
	   }

	close(fd);
//	fprintf(stderr,"maxlen: %d\tavg: %g\tmax/min_pixel: %d/%d\n",
//                   max,(double)avg/(double)cnt,max_pixel,min_pixel); 
}

void rle_save_header(int fd, int width, int height, t_color *map)
{
// 	if (map == NULL)
//	   return;

	char st[256];
	memset(st,'.',192);
        sprintf(st,"RLE bitmap width=%d height=%d\n8bit colormap @ offset 0x%02x\nimage @ 0x%04x \n\n",width,height,128,128+768);
	write(fd,st,128);
        
	if (map != NULL)
	   rle_Savemap = map; 
	 else
	   rle_Savemap = rle_Colormap;	

	write(fd,rle_Savemap,sizeof(t_color)*256);

// 	save_idx = 0;
        Last.Pixel = 0;
	Last.Len = 0;
	line_ptr = 0;
}


char rle_get_header(int fd, int &width, int &height)
{
	char st[256];

	int r = read(fd,st,128);
	if (r < 24)
	   return false;
 
	st[r] = '\0';
	int i = sscanf(st,"RLE bitmap width=%d height=%d",&width,&height);

        if (i != 2)
	   return false;

	r = read(fd,rle_Colormap,sizeof(t_color)*256);
	if (r != (sizeof(t_color)*256))
	   return false; 

        ncolors_read = false;
	Last.Pixel = 0;
	Last.Len = 0;
	return true;
}



char rle_get_pixel(int fd, unsigned char &C)
{
	if (Last.Len <= 0)
	   {
            if (ncolors_read == false)
	       {
	        long int pc = lseek(fd,0,SEEK_CUR);

//		fprintf(stderr,"pos:%ld\n",pc);
	        long int pos = lseek(fd,0,SEEK_END);
//	        long int pos = 20*1024;

		pos -= pc;
		if (pos < 0)
	           return false;

		lseek(fd,pc,SEEK_SET);

		if (READLN != NULL)
	           delete READLN;

                READLN = new unsigned char [pos];
		
   	        long int r = read(fd,READLN,pos);
		if (r != pos)
		   {
//	            fprintf(stderr,"to much\n"); 
//                    return false;
                   } 


		ncolors_read = true;
		read_ptr = 0;
               } 
	
	    Last.Pixel = READLN[read_ptr];
	    read_ptr++;

	    unsigned long L,B;
	    L = 0;
	    unsigned char c[8];
	    int i = 0;

            do {
                c[i] = READLN[read_ptr];
	        read_ptr++;

  	        B = (unsigned long)(c[i] & 0x7f);   
	        B = B << (i * 7);
	        L |= B;	
	        i++;
	      } while ((c[i-1] & 0x80) > 0);

#ifdef DEBUG
            if (i > 1)
	       {
                for (int j=0; j<i; j++)
                  fprintf(stderr,"0x%02x,",c[j]);
  
	        fprintf(stderr,"\tP: 0x%02x\tL: 0x%08lx\n",Last.Pixel,L);
	       }
#endif
            Last.Len = L;            
           }

	C = Last.Pixel;
	Last.Len--;		
	
	return true;	
}

