#include "ZipLoader.h"
#include <SFML/System.hpp>
#include <iostream>
#include <unzip.h>

/*--------------------------------------------------------------------------*/
// Class statics 

namespace Zip
	{
	// Define data file
	const char *DataFile="Data.zip";
	
	// Define load mutex
	sf::Mutex LoadMutex;
	
	// Declare loading functions
	const std::string AbsolutePath(const std::string &Name);
	char *LoadNormalFile(const std::string &Filename, unsigned int &Size);
	char *LoadZipFile(const std::string &Filename, unsigned int &Size);
	char *Load(const std::string &Filename, unsigned int &Size);
	}

/*--------------------------------------------------------------------------*/
// Parses path to absolute value
//
const std::string Zip::AbsolutePath(const std::string &Name)
	{
	// All \:s need to be changed to /
	std::string Filename(Name);
	std::string::size_type Pos=0;
	while ((Pos=Filename.find('\\',Pos)) != std::string::npos)
		Filename[Pos]='/';
	
	// Check if directory has "..", and remove it
	while ((Pos=Filename.find("/..")) != std::string::npos)
		{
		// Modify path to get absolute path
		std::string::size_type Start=Filename.rfind('/',Pos-1);
		if (Start!=std::string::npos)
			// Remove previous path and ..
			Filename=Filename.substr(0,Start)+Filename.substr(Pos+3);
		}
	
	// Return generated filename
	return Filename;
	}

/*--------------------------------------------------------------------------*/
// Loads normal file
//
char *Zip::LoadNormalFile(const std::string &Filename, unsigned int &Size)
	{
	// Try to open the file
	FILE *fp=fopen(Filename.c_str(),"rb");
	if (fp==NULL)
		return NULL;
	
	// Resolve file size
	fseek(fp,0,SEEK_END);
	Size=ftell(fp);
	fseek(fp,0,SEEK_SET);
	
	// Allocate memory and read file
	char *Buffer=new char[Size+1];
	fread(Buffer,1,Size,fp);
	Buffer[Size]=0;		// Add terminating zero
	fclose(fp);
	
	// Return data
	return Buffer;
	}

/*--------------------------------------------------------------------------*/
// Loads file from zip
//
char *Zip::LoadZipFile(const std::string &Filename, unsigned int &Size)
	{
	// Open zip file
	unzFile ZipHandle=unzOpen(DataFile);
	if (ZipHandle==NULL)
		return NULL;	// Can't open zip archive
	
	// Create absolute path
	std::string Path=AbsolutePath(Filename);
	
	// Find file
	int Ok=unzLocateFile(ZipHandle,Path.c_str(),false);
	if (Ok!=UNZ_OK)
		return NULL;	// File not found
	
	// Try to open it
	Ok=unzOpenCurrentFile(ZipHandle);
	if (Ok!=UNZ_OK)
		return NULL;	// Can't open requested file from zip
	
	// Resolve file size
	unz_file_info FileInfo;
	unzGetCurrentFileInfo(ZipHandle, &FileInfo, NULL,0, NULL,0, NULL,0);
	Size=FileInfo.uncompressed_size;
	
	// Allocate memory and read the file
	char *Buffer=new char[Size+1];
	unzReadCurrentFile(ZipHandle,Buffer,Size);
	Buffer[Size]=0;
	
	// Close file handles
	unzCloseCurrentFile(ZipHandle);
	unzClose(ZipHandle);
	
	// Return data
	return Buffer;
	}

/*--------------------------------------------------------------------------*/
// Reads file to memory
//
char *Zip::Load(const std::string &Filename, unsigned int &Size)
	{
	// Grab mutex
	sf::Lock Lock(LoadMutex);
	
	// Try to load normal file
	char *Data=LoadNormalFile(Filename,Size);
	
	// If that failed, load file from zip
	if (!Data)
		Data=LoadZipFile(Filename,Size);
	
	// If even that failed, then we can't load the file
	if (!Data)
		{
		// You could for example throw an error here, now just returning NULL
		std::cout << "Can't open file: " << Filename << std::endl;
		return NULL;
		}
	
	// Return loaded file
	return Data;
	}

/*--------------------------------------------------------------------------*/
// Storage class for file data
/*--------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------*/
// Constructors
//

// Creates empty file
Zip::File::File() :
		myData(NULL),
		mySize(0)
	{
	// Nothing here
	}

// Construct from filename and load requested
Zip::File::File(const std::string &Filename) :
		myData(NULL),
		mySize(0)
	{
	// Load file
	Load(Filename);
	}

/*--------------------------------------------------------------------------*/
// Destructor
//
Zip::File::~File()
	{
	// Just close the file
	Close();
	}

/*--------------------------------------------------------------------------*/
// Load file
//
void Zip::File::Load(const std::string &Filename)
	{
	// Close old file
	Close();
	
	// Load file
	myData=Zip::Load(Filename,mySize);
	}

/*--------------------------------------------------------------------------*/
// Close file, free allocated memory
//
void Zip::File::Close(void)
	{
	// Free old memory
	delete[] myData;
	mySize=0;
	}
