#ifndef b_pack22_haha
#define b_pack22_haha

#include <string.h>
#include <stdlib.h>
#include <pragma/dos_lib.h>
#include <exec/memory.h>
#include <pragma/exec_lib.h>

int P1Pack2(char*,char*,int);
int P1UnPack2(char*,char*,int);

struct QL1Element{
 QL1Element* next;
 void* inhalt;
};

struct QL1Liste{
 QL1Element* first;
 QL1Element* last;
 char name[10];
 int data;
};

struct C1Buffer{
 ULONG numbits;
 ULONG countbit;
 unsigned char* buffer;
};

struct C1CharInfo{
 ULONG count;
 C1CharInfo* left,* right,* prevtree;
 char ischar; 
 unsigned char wert;
 char code[30];
 char clength;
};

void P1Sort(C1CharInfo**,int); //private
void P1Eintragen(C1CharInfo*); //private
void P1Hilf1(C1CharInfo*); //private

void QL1AllesLoeschen(QL1Liste&,int dodelete=0); //private
void QL1Init(QL1Liste&); //private
QL1Element* QL1Einfuegen(QL1Liste&,QL1Element*); //private

void P1Hilf1(C1CharInfo* c1){
 if(c1->left){
  strcpy(c1->left->code,c1->code);
  strcat(c1->left->code,"0");
  P1Hilf1(c1->left);
 }
 if(c1->right){
  strcpy(c1->right->code,c1->code);
  strcat(c1->right->code,"1");
  P1Hilf1(c1->right);
 }
}

void P1Eintragen(C1CharInfo* baum){
 if(!(baum->left)){if(!(baum->right)){baum->code[0]='0';return;}}
 if(baum->left){
   baum->left->code[0]='0';P1Hilf1(baum->left);
 }
 if(baum->right){
  baum->right->code[0]='1';P1Hilf1(baum->right);
 }
}

void P1Sort(C1CharInfo* a[],int N){
 int i,j;
 C1CharInfo* v;
 for(i=1;i<=N;i++){
  v = a[i];j=i;
  while(j>0 && a[j-1]->count > v->count) {a[j] = a[j-1];j--;}
  a[j]=v;
 }
}

int P1UnPack2(char* f1,char* f2,int doprev){
 int err=0;
 BPTR file1=0;
 char kennung[6];
 ULONG numbits;
 unsigned char flags1[32];
 unsigned char flag2;
 C1CharInfo* c1,* c2,* c3;
 C1CharInfo* t1=0;
 int x,y;
 int charoffset,bitoffset;
 unsigned char uc1;
 unsigned short us1;
 ULONG ul1; 
 C1CharInfo* baum1=0;
 C1CharInfo** t2=0,** t3=0;
 C1Buffer buf1;
 QL1Liste ql1;
 QL1Element elem;

 QL1Init(ql1);
 buf1.numbits=0;buf1.countbit=0;

 if(!(t1 = new C1CharInfo[256])){err=1;goto beenden;}
 if(!(file1 = Open(f1,MODE_OLDFILE))){err=2;goto beenden;}
 if(!(FRead(file1,kennung,5,1))){err=3;goto beenden;}
 kennung[5]=0;
 if(strcmp("P-0-2",kennung)){err=4;goto beenden;}
 if(!(FRead(file1,&numbits,sizeof(ULONG),1))){err=3;goto beenden;}
 if(!(FRead(file1,flags1,32,1))){err=3;goto beenden;}
 if(!(FRead(file1,&flag2,1,1))){err=3;goto beenden;}

 for(x=0,y=0;x<256;x++){ 
  charoffset=x>>3;
  bitoffset=x-(charoffset<<3);
  if(flags1[charoffset] & (128>>bitoffset)){
   y++;
   switch(flag2){
    case 0: {if(!(FRead(file1,&uc1,1,1))){err=3;goto beenden;} t1[x].count=uc1;break;}
    case 1: {if(!(FRead(file1,&us1,sizeof(short),1))){err=3;goto beenden;} t1[x].count=us1;break;}
    case 2: {if(!(FRead(file1,&ul1,sizeof(ULONG),1))){err=3;goto beenden;} t1[x].count=ul1;break;}
   }
  }
  t1[x].ischar=1;t1[x].wert=x;  
 }
 if(!y){err=5;goto beenden;}

 if(!(t2=new C1CharInfo*[y])){err=1;goto beenden;}
 for(x=0,y=0;x<256;x++){
  if(t1[x].count){t2[y]=&t1[x];y++;}
 }
 t3=t2;
 baum1=t3[0];
 while(y>1){
  P1Sort(t3,y-1);
  c1=t3[0];c2=t3[1];
  if(!(c3=new C1CharInfo)){err=1;goto beenden;}
  elem.inhalt=c3;
  if(!(QL1Einfuegen(ql1,&elem))){err=1;goto beenden;}
  c3->count = c1->count + c2->count;
  c3->left=c1;c3->right=c2;
  c1->prevtree=c3;c2->prevtree=c3;
  baum1=c3;
  t3[1]=c3;
  t3=&t3[1];
  y--;
 }
 P1Eintragen(baum1); 
 for(x=0;x<256;x++){
  if(t1[x].count){
   y=t1[x].clength=strlen(t1[x].code);
   buf1.numbits+=y*t1[x].count;
  }
 }
 if(buf1.numbits != numbits){err=5;goto beenden;}
 if(!(buf1.buffer = new unsigned char[(buf1.numbits+8)>>3])){err=1;goto beenden;}
 if(!(FRead(file1,buf1.buffer,(buf1.numbits+8)>>3,1))){err=3;goto beenden;}
 Close(file1);file1=0;

 if(!(file1 = Open(f2,MODE_NEWFILE))){err=2;goto beenden;}
 
 c2=baum1;x=-1;
 loop99:
 x++;if(x==buf1.numbits) goto beenden;
 charoffset=x>>3;
 bitoffset=x-(charoffset<<3);
 if(c2->ischar){if(-1==FPutC(file1,c2->wert)){err=3;goto beenden;}goto loop99;}
 if(buf1.buffer[charoffset] &  (128>>bitoffset)){
  c2=c2->right;
  if(c2->ischar){if(-1==FPutC(file1,c2->wert)){err=3;goto beenden;}c2=baum1;}
  goto loop99;
 } 
 else{
  c2=c2->left;
  if(c2->ischar){if(-1==FPutC(file1,c2->wert)){err=3;goto beenden;}c2=baum1;}
  goto loop99;
 }
 
 beenden:
 if(file1) Close(file1);
 if(t1) delete[] t1;
 if(t2) delete[] t2;
 QL1AllesLoeschen(ql1,1);
 return err;
}

int P1Pack2(char* f1,char* f2,int doprev){
 int err=0;
 C1CharInfo* t1=0;
 C1CharInfo** t2=0,** t3;
 C1CharInfo* c1,* c2,* c3;
 C1CharInfo* baum1=0;
 C1Buffer buf1;
 unsigned char field1[32]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,};
 unsigned char flag1=0;
 unsigned char u1;
 unsigned short s1;
 ULONG l1;
 FileInfoBlock* exfh=0;
 unsigned char* buf2=0;
 QL1Liste ql1;
 QL1Element elem;

 BPTR file1=0;
 LONG x,y;
 LONG xx,yy;
 int charoffset,bitoffset;

 buf1.numbits=0;buf1.countbit=0;
 QL1Init(ql1);

 if(!(exfh = (FileInfoBlock*)AllocMem(sizeof(FileInfoBlock),MEMF_ANY))){err=1;goto beenden;}
 if(!(t1 = new C1CharInfo[256])){err=1;goto beenden;}
 if(!(file1 = Open(f1,MODE_OLDFILE))){err=2;goto beenden;}
 if(!(ExamineFH(file1,exfh))){err=3;goto beenden;}
 if(!(buf2 = new unsigned char[exfh->fib_Size])){err=1;goto beenden;}

 if(!(exfh->fib_Size==Read(file1,buf2,exfh->fib_Size))){err=4;goto beenden;}
 Close(file1);file1=0;

 for(x=0;x<(exfh->fib_Size);x++){
  y=buf2[x];
  t1[y].count++;
 }
 next1:
 for(x=0,y=0;x<256;x++){
  if(t1[x].count) y++;
  t1[x].wert=x;
  t1[x].ischar=1;
 }

 if(y==0){err=4;goto beenden;}
 if(!(t2=new C1CharInfo*[y])){err=1;goto beenden;}
 for(x=0,y=0;x<256;x++){
  if(t1[x].count){t2[y]=&t1[x];y++;}
 }
 t3=t2;
 baum1=t3[0];
 while(y>1){
  P1Sort(t3,y-1);
  c1=t3[0];c2=t3[1];
  if(!(c3=new C1CharInfo)){err=1;goto beenden;}
  elem.inhalt=c3;
  if(!(QL1Einfuegen(ql1,&elem))){err=1;goto beenden;}
  c3->count = c1->count + c2->count;
  c3->left=c1;c3->right=c2;
  c1->prevtree=c3;c2->prevtree=c3;
  baum1=c3;
  t3[1]=c3;
  t3=&t3[1];
  y--;
 }
 P1Eintragen(baum1); 
 for(x=0;x<256;x++){
  if(t1[x].count){
   y=t1[x].clength=strlen(t1[x].code);
   buf1.numbits+=y*t1[x].count;
  }
 }
 if(!(buf1.buffer = new unsigned char[(buf1.numbits+8)/8])){err=1;goto beenden;}

 for(y=0;y<exfh->fib_Size;y++){
  x=buf2[y];
  xx=t1[x].clength;
  yy=0;
  while(xx){
   if(t1[x].code[yy]=='1'){
    charoffset=buf1.countbit>>3;
    bitoffset=buf1.countbit-(charoffset<<3);
    buf1.buffer[charoffset] += ((unsigned char)128)>>bitoffset;
   }
   xx--;yy++;buf1.countbit++;
  } 
 }

 Close(file1);file1=0;
 if(!(file1=Open(f2,MODE_NEWFILE))){err=2;goto beenden;}
 if(doprev){FPuts(file1,"P-0-2");}

 for(x=0;x<256;x++){
  if(t1[x].count){
   charoffset=x>>3;
   bitoffset=x-(charoffset<<3);
   field1[charoffset]+=((unsigned char)128)>>bitoffset;
   if(t1[x].count>255){if(t1[x].count>65535){flag1=2;}else{flag1=1;}}
  }
 }
 if(1 != FWrite(file1,&(buf1.numbits),sizeof(ULONG),1)){err=3;goto beenden;}
 if(1 != FWrite(file1,field1,32,1)){err=3;goto beenden;}
 if(1 != FWrite(file1,&flag1,1,1)){err=3;goto beenden;}
 for(x=0;x<256;x++){
  if(t1[x].count){
   switch(flag1){
    case 0: {u1=t1[x].count;if(1 != FWrite(file1,&u1,1,1)){err=3;goto beenden;}break;}
    case 1: {s1=t1[x].count;if(1 != FWrite(file1,&s1,sizeof(short),1)){err=3;goto beenden;}break;}
    case 2: {l1=t1[x].count;if(1 != FWrite(file1,&l1,sizeof(ULONG),1)){err=3;goto beenden;}break;}
   }
  }
 }
 if(((buf1.numbits+8)>>3) != FWrite(file1,buf1.buffer,1,(buf1.numbits+8)>>3)){err=3;goto beenden;}
 
 beenden:
 if(t2) delete[] t2;
 if(t1) delete[] t1;
 if(file1) Close(file1);
 QL1AllesLoeschen(ql1,1);
 if(exfh) FreeMem(exfh,sizeof(FileInfoBlock));
 if(buf2) delete[] buf2;
 return err;
}

void QL1Init(QL1Liste& ql1){
 ql1.first=0;
 ql1.last=0;
 ql1.name[0]=0;
 ql1.data=0;
};

void QL1AllesLoeschen(QL1Liste& ql1,int dodelete){
 QL1Element* ez=ql1.first;
 QL1Element* hilf;
 while(ez){
  if(dodelete){
   delete ez->inhalt;
  }
  hilf=ez;
  ez=ez->next;
  delete hilf;
 }
 QL1Init(ql1); 
}

QL1Element* QL1Einfuegen(QL1Liste& ql1,QL1Element* ez1){
 QL1Element* ez2;
 if(!(ez2 = new QL1Element)) return 0;
 ez2->inhalt = ez1->inhalt;
 ez2->next=0;
 if(!ql1.first){
  ql1.first = ez2;
  ql1.last = ez2;
 } 
 else{
  ql1.last->next=ez2;
  ql1.last=ez2;
 }
 return ez2;
}

#endif


