#include "StdAfx.h"

#include "Fichier.h"
#include <string.h>
#include "ICompresseur.h"

#include "copieur.h"
#include "lzw.h"

const int Fichier::AL_AUTO=0;
const int Fichier::AL_COPIE=1;
const int Fichier::AL_RLE=2;
const int Fichier::AL_LZW=3;
const int Fichier::AL_HUFFMAN=4;

#include <iostream>
#include <sstream>
#include <string>
using namespace std;

#include "minuxexception.h"

Fichier::Fichier()
{
	type_algo=Fichier::AL_AUTO;
}

Fichier::Fichier(int algo)
{
	type_algo=algo;
}


void Fichier::comprimer(char*nom_fichier,FILE*dest)
{
	// ouvre un fichier pour le comprimer
	FILE*source;
	source=fopen(nom_fichier,"rb");
	if(source==NULL)
	{
		//printf("Fichier %s introuvable.\n",nom_fichier);
		//return;
		throw MinuxException(std::string("Fichier introuvable : ")+nom_fichier);
	}

	// détermines les informations à ranger dans l'entête
	unsigned char l;
	l=(unsigned char) strlen(nom_fichier);
	fwrite(&l,1,sizeof(char),dest);
	fwrite(nom_fichier,l,sizeof(char),dest);
	
	nom=new char[ l+1 ];
	strcpy(nom,nom_fichier);

	fseek(source,0,SEEK_END);
	taille_originale=taille_archive=ftell(source);
	
	// détermine le type d'algo
	if(type_algo==Fichier::AL_AUTO)
	{
		type_algo=AL_LZW;
	}

	fseek(source,0,SEEK_SET);

	fwrite(&taille_originale,1,sizeof(int),dest);
	fwrite(&taille_archive,1,sizeof(int),dest);
	fwrite(&type_algo,1,sizeof(int),dest);

	ICompresseur*comp;

	// utilise le compresseur approprié
	switch(type_algo)
	{
		case AL_COPIE:		
			comp=new Copieur;
			break;
		case AL_LZW:
			comp=new LZW;
			break;
	}
		
	int pos=ftell(dest); // notation la position courante
	taille_archive=comp->comprimer(nom_fichier,source,dest);
	int fin=ftell(dest);
	delete comp;

	// fini de remplir l'entête
	fseek(dest,pos-8,SEEK_SET);
	fwrite(&taille_archive,1,sizeof(int),dest);

	// retourne à la fin
	fseek(dest,fin,SEEK_SET);
	fclose(source);	
}

bool Fichier::lire_entete(FILE*source)
{
	int c;
	if( (c=fgetc(source)) ==-1)
		return false;

	nom=new char[ c+1 ];
	int i;
	for( i=0; i<c; i++)
		nom[i]=fgetc(source);

	nom[i]=0;

	fread(&taille_originale,1,sizeof(int),source);
	fread(&taille_archive,1,sizeof(int),source);
	fread(&type_algo,1,sizeof(int),source);

	fseek(source,taille_archive,SEEK_CUR);

	return true;
}

string fmt_taille(int t)
{
	string r;
	ostringstream os;
	if(t<1024)
	{
		os << t << " oct";		
	}
	else
	{
		double d=1.0*t/1024;
		os.precision(2);
		os << fixed << d << " ko ";		
	}
	os.flush();
	r=os.str();
	return r;
}

void Fichier::afficher_entete()
{		
	cout.width(30);		
	cout << left << nom;
	
	cout.width(15);
	cout << right << fmt_taille(taille_originale).c_str();

	cout.width(15);
	cout << fmt_taille(taille_archive).c_str();
	
	cout.width(8);
	switch(type_algo)
	{
	case AL_COPIE:
		cout << "Copie";	
		break;
	case AL_RLE:
		cout << "Rle";		
		break;
	case AL_LZW:
		cout << "LZW";		
		break;
	case AL_HUFFMAN:
		cout << "Huffman";		
		break;
	default:
		cout << "?";
		break;
	}
	
	// ratio
	double ratio=(1.0*taille_archive)/(taille_originale*1.0)*100;
	cout.width(8);
	cout.precision(2);
	cout << fixed << ratio << "%";

	cout << endl;	
}

void Fichier::expanser(FILE*source,char*destination)
{
	char temp[500];
	sprintf(temp,"%s\\%s",destination,nom);

	// on revient en arriere
	fseek(source,-taille_archive,SEEK_CUR);

	ICompresseur*comp;

	switch(type_algo)
	{
	case AL_COPIE:		
		comp=new Copieur;
		break;
	case AL_LZW:
		comp=new LZW;
		break;
	}

	comp->expanser(temp,taille_originale,taille_archive,source);
	delete comp;
}