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

#include <glib.h>

#include "zip_header.h"
#include "inflate.h"
#include "zip_wrapper.h"
#include "zip_testcrc.h"

ZipFile* zip_file_new(void)
{
	ZipFile *zip;

	zip=(ZipFile*)malloc(sizeof(ZipFile));
	g_return_val_if_fail(zip != NULL,NULL);

	zip->filename=0;
	zip->cdre=0;
	zip->records=g_slist_alloc();
	zip->opened=0;

	return zip;
}

int zip_file_open(const char *filename,ZipFile *zip)
{
	FILE *file=0;
	int status=0;

	ZipCDREnd *e;

	file=fopen(filename,"r");
	if (file == NULL)
        {
                return 0;
        }

	status=zip_find_end_of_central_dir_record(file);
	if (status==0)
	{
		return 0;
	}

	e=(ZipCDREnd*)malloc(sizeof(ZipCDREnd));
	g_return_val_if_fail(e != NULL,0);
	
	status=zip_read_end_of_central_dir_record(file,e);
	g_return_val_if_fail(status != 0,0);

	zip->cdre=e;

	status=zip_goto_central_dir_record_start(file,e);
	g_return_val_if_fail(status != 0,0);

	status=zip_file_add_records_with_file(file,zip);

	if (status!=0)
		zip->opened=1;
	else {
		free(zip->cdre);
		zip->cdre=0;
		zip->opened=0;
	}

	fclose(file);

	zip->filename=g_strdup(filename);
	g_return_val_if_fail(zip->filename != NULL,0);

	return status;
}

/* zip_file_add_records()
   Function adds central dir records to single linked list */

int zip_file_add_records_with_file(FILE *file,ZipFile *zip) /* 0 = failure */
{
	int a=0;
	int status=0;
	ZipCDR *r;

	g_return_val_if_fail(file != NULL,0);
	g_return_val_if_fail(zip != NULL,0);

	for (a=0;a<zip->cdre->totalentriesonthisdisk;a++) {
		r=(ZipCDR*)malloc(sizeof(ZipCDR));

		g_return_val_if_fail(r != NULL,0);
		
		status=zip_read_central_dir_record(file,r);

		if (status!=0) {
			zip->records=g_slist_append(zip->records,
						    (gpointer)r);
		} else {
			g_warning("Error when reading cdrs\n");
		}
	}
	return 1;
}

int zip_file_add_records(ZipFile *zip) /* 0 = failure */
{
	FILE *file=0;
	int a=0;
	int status=0;
	ZipCDR *r;

	g_return_val_if_fail(file != NULL,0);
	g_return_val_if_fail(zip != NULL,0);
	g_return_val_if_fail(zip->filename != NULL,0);

	file=fopen(zip->filename,"r");

	g_return_val_if_fail(file!=NULL,0);

	for (a=0;a<zip->cdre->totalentriesonthisdisk;a++) {
		r=(ZipCDR*)malloc(sizeof(ZipCDR));

		g_return_val_if_fail(r != NULL,0);
		
		status=zip_read_central_dir_record(file,r);

		if (status!=0) {
			zip->records=g_slist_append(zip->records,
						    (gpointer)r);
		} else {
			g_warning("Error when reading cdrs\n");
		}
	}
	fclose(file);
	return 1;
}

void zip_file_free_current(gpointer data,gpointer user_data)
{
	ZipCDR *r;

	if (data==NULL)
		return;

	r=(ZipCDR*)data;
	if (r->filename)
		free(r->filename);

	r->filename=NULL;
}

int zip_file_free(ZipFile *zip)
{
	g_return_val_if_fail(zip != NULL,0);

	if (zip->filename)
		g_free(zip->filename);
	zip->filename=NULL;

	zip->opened=0;

	if (zip->cdre != NULL)
		g_free(zip->cdre);

	g_slist_foreach(zip->records,zip_file_free_current,
			(gpointer)NULL);

	g_slist_free(zip->records);
	zip->records=NULL;
	
	free(zip);
	zip=NULL;

	return 1;
}

int zip_file_close(ZipFile *zip)
{
	g_return_val_if_fail(zip != NULL,0);

	if (zip->filename)
		g_free(zip->filename);
	zip->filename=NULL;

	zip->opened=0;

	if (zip->cdre != NULL) {
		g_free(zip->cdre);
		zip->cdre=NULL;
	}

	g_slist_foreach(zip->records,zip_file_free_current,
			(gpointer)NULL);

	g_slist_free(zip->records);
	zip->records=g_slist_alloc();

	return 1;
}

/* zip_file_uncompress_all()
   extra_flags:
    0x01 - skip errors
    0x02 - lowercase names */

int zip_file_uncompress_all(const char *dir,ZipFile *zip,int extra_flags)
{
	ZipFileVars vars;
	FILE *file;

	g_return_val_if_fail(dir != NULL,0);
	g_return_val_if_fail(zip != NULL,0);

	vars.skip_errors=extra_flags;
	vars.dir=dir;
	vars.error_count=0;

	file=fopen(zip->filename,"r");
	g_return_val_if_fail(file != NULL,0);

	vars.file=file;

	g_slist_foreach(zip->records,zip_file_uncompress_current,
			(gpointer)&vars);

	if (vars.error_count>0)
		return -vars.error_count;

	fclose(file);

	return 1;
}

int zip_file_uncompress_nth(unsigned int n,const char *dir,ZipFile *zip,
			    int extra_flags)
{
	ZipFileVars vars;
	FILE *file;
	GSList *entry;

	g_return_val_if_fail(dir != NULL,0);
	g_return_val_if_fail(zip != NULL,0);

	vars.skip_errors=extra_flags;
	vars.dir=dir;
	vars.error_count=0;

	file=fopen(zip->filename,"r");
	g_return_val_if_fail(file != NULL,0);

	entry=g_slist_nth(zip->records,n);
	g_return_val_if_fail(entry != NULL,0);

	vars.file=file;

	zip_file_uncompress_current(entry->data,(gpointer)&vars);

	if (vars.error_count>0)
		return -vars.error_count;

	fclose(file);

	return 1;
}

void zip_file_uncompress_current(gpointer data,gpointer user_data)
{
	ZipFileVars *vars;
	ZipCDR *r;
	int status=0;

	if (data==NULL)
		return;

	g_return_if_fail(user_data != (gpointer)NULL);
	
	vars=(ZipFileVars*)user_data;
	r=(ZipCDR*)data;
	
	status=uncompress_file_with_dir(r,vars->file,vars->dir,
					vars->skip_errors);
	if (status==0)
		vars->error_count++;
}

GSList *zip_file_test(ZipFile *zip,int *status)
{
	ZipFileVars vars;
	FILE *file;

	g_return_val_if_fail(zip != NULL,0);

	vars.skip_errors=1;
	vars.error_count=0;
	vars.errors=g_slist_alloc();
	g_return_val_if_fail(vars.errors != NULL,NULL);

	file=fopen(zip->filename,"r");
	g_return_val_if_fail(file != NULL,0);

	vars.file=file;

	g_slist_foreach(zip->records,zip_file_test_current,
			(gpointer)&vars);

	fclose(file);

	if (vars.error_count>0) {
		*status=0;
	} else 
		*status=1;

	return vars.errors;
}

void zip_file_test_current(gpointer data,gpointer user_data)
{
	ZipFileVars *vars;
	ZipCDR *r;
	int status=0;

	if (data==NULL)
		return;

	g_return_if_fail(user_data != NULL);
	
	vars=(ZipFileVars*)user_data;
	r=(ZipCDR*)data;
	
	status=zip_test_crc(r,vars->file);
	if (status==0) {
		vars->error_count++;
		vars->errors=g_slist_append(vars->errors,r->filename);
	}
}
