#include <stdio.h>
#include <stdlib.h>
#include <string.h>
FILE *infile;
FILE *outfile;

int compress(unsigned char* inbuf, unsigned char* outbuf, int size, unsigned char* oute);
void error( const char* msg );
void quantize(unsigned char* inbuf, int size, int t);

int main(int argc, char *argv[])
{
    unsigned int size,outsize,i,t;
    unsigned short clocks;
    unsigned char escape,temp;
    unsigned char thershold[8];
    unsigned char * inbuf;
    unsigned char * outbuf;
    float percent;
    puts("---RLE Compressor---\n  Created by James Montelongo");
    
    if (argc < 3){
        printf("Usage:  %s infile outfile\n",argv[0]);
        return 1;
    }
    t=0;
    if (argc == 4){
        strcpy(thershold,argv[3]);
        t= atoi(thershold)&0xFF;
    }
    printf("Thereshold is %d\n",t);
      
    if (!(infile = fopen(argv[1],"rb"))) error("Could not open in in file.");
    fseek(infile,0,SEEK_END);
    size = ftell(infile);
    rewind(infile);
    
    if (!(inbuf = (unsigned char*) malloc (size))) error ("Insufficient memory.");
    if (!(outbuf = (unsigned char*) malloc (size))) error ("Insufficient memory.");
    clocks= (fgetc(infile)&0xff)+((fgetc(infile)&0xFF)<<8);
    
    for(i = 0; i < size; i++) inbuf[i] = fgetc(infile);
    fclose(infile);
    printf("Size of in file = %d\n",size);
    quantize(inbuf, size, t);
    outsize = compress(inbuf,outbuf,size,&escape);
    printf("Size of out file = %d\n",outsize);

    if (!(outfile = fopen(argv[2],"wb"))) 
        error("Could not open the output file.");
        
    fputc((clocks&0xFF),outfile);
    fputc(((clocks>>8)&0xFF),outfile);
    fputc((escape&0xFF),outfile);
    for(i=0; (i<outsize);i++) {
        temp =outbuf[i];
        fputc((temp&0xFF),outfile);
    }
    percent = 100*((float)outsize)/((float)size);
    printf("Compressed to %3.2f percent of the original file size.\n",percent);
    
    puts("File written");
    fclose(outfile);


    exit(0);
}

void error( const char* msg ) {
    printf("\nError: %s\n",msg);
    exit(1);
}

/* RLE compression
 * ESCAPE,LENGTH,DATA
 * Alters original buffer
 * Find least used vaule
 * and increases it
 * that vaule becomes the 
 * escape byte.
 */
int compress(unsigned char* inbuf, unsigned char* outbuf, int size, unsigned char* oute) {
    unsigned int numcount[256];
    unsigned int escape;
    int i,pnt,temp;
    
    for(i=0;i<256;i++) numcount[i]=0;
    /* count each level for occurance frequency */
    /* determine the least used vaule */
    /* this is used for the escape vaule */
    for(i=0; i<size;i++) {
        escape = inbuf[i];
        numcount[escape]++;
    }

    escape = 0;
    for(i=0; i<256;i++) {
        if (numcount[escape]>numcount[i]) escape=i;
    }

/*    for(i=0; i<256;i+=16) {
        printf("%X %X %X %X ",numcount[i+0],numcount[i+1],numcount[i+2],numcount[i+3]);
        printf("%X %X %X %X ",numcount[i+4],numcount[i+5],numcount[i+6],numcount[i+7]);
        printf("%X %X %X %X ",numcount[i+8],numcount[i+9],numcount[i+10],numcount[i+11]);
        printf("%X %X %X %X\n",numcount[i+12],numcount[i+13],numcount[i+14],numcount[i+15]);
    } */
    /* This is not typical */
    /* it alters the least used vaule */
    /* to insure the size does not increase */
    if ((temp=numcount[escape+1])<numcount[escape-1]) temp=numcount[escape-1];
    if (escape==0) temp =1;
    if (escape==255) temp=254;
    for(i=0; i<size; i++) {
        if (inbuf[i]==escape) inbuf[i]=temp;
    }
    printf("escape byte is %d with frequency of %d.\n",escape,numcount[escape]);
    *oute = escape;
    /* Traditional rle compression */
    pnt = 0;
    for(i=0; i<size;i++) {
        if (inbuf[i]==escape) {
            for(temp=0;( (escape==inbuf[i+temp]) && (temp<256) );temp++);
            outbuf[pnt++]=escape;
            outbuf[pnt++]=temp;
            i+= temp-1;
        }
        else {
            for(temp=0;( (inbuf[i]==inbuf[i+temp]) && (temp<256) );temp++);
            if (temp>3) {
                outbuf[pnt++]=escape;
                outbuf[pnt++]=temp; //run length
                i+= temp-1;
            }
        }
        outbuf[pnt++]=inbuf[i];
    }
    return pnt;
}
    
    
void quantize(unsigned char* inbuf, int size, int t) {
    unsigned char* rptr;
    unsigned char* msize;
    unsigned char* tbuf;    
    unsigned int temp,rtemp;
    int pnt;
    int i;
    if (!(tbuf = (unsigned char*) malloc (size))) error ("Insufficient memory.");
    

    msize = inbuf+size;
    for(pnt=0; (pnt<size); pnt++) {
        temp = inbuf[pnt];
        for(rptr = inbuf+pnt+1; ( (rptr<msize) && ((((rtemp = *rptr)-temp)<=t) || ((temp-rtemp)<= t)) ); rptr++);
        tbuf[pnt]= rptr - (inbuf+pnt);
//        printf("%d\n",100*pnt/size);
        
    }

    for(pnt=0;pnt<size;pnt++) {
        temp = tbuf[pnt];
        if (temp!=0) {
            if (temp<4) tbuf[pnt]=1;
            else {
                for(i=1; ( ((pnt+i)<size) && ((temp-i)>=tbuf[pnt+i])  );i++) tbuf[pnt+i]=0;
                tbuf[pnt]=i;
            }
        }
//        printf("%d\n",100*pnt/size);
    }

    pnt=0;
    while(pnt<size) {
        temp = tbuf[pnt];
        if (temp==0) pnt++;
        else {
            rtemp =inbuf[pnt];
            for(i=0;i<temp;i++) inbuf[pnt+i]=rtemp;
            pnt +=temp;
        }
//        printf("%d\n",100*pnt/size);
    }
}
    
    
    
    
    
