/*
 * Copyright (c) 2008 by Oleg Trifonov <otrifonow@gmail.com>
 *
 * This file is free software; you can redistribute it and/or modify
 * it under the terms of either the GNU General Public License version 2
 * or the GNU Lesser General Public License version 2.1, both as
 * published by the Free Software Foundation.
 */

#include <wx/wx.h>
#include <wx/dir.h>
#include <wx/file.h>
#include <vector>

#include <stdio.h>
#include <iostream>

using namespace std;

struct TFileInfo {
	wxString name;			// full filename
	unsigned int size;	// file size
	long long crc;			// checksum
	bool duplicated;		// it is a duplicate file?
	bool first;				// for duplicated files == true if it is the first file in group
};

vector <wxString> paths;
vector <TFileInfo> files;

char DIR_SEPARATOR = '\\';


void addFiles(wxString dirName) {
	printf("Browsing: %s\n", dirName);
	wxDir dir(dirName);
	wxString filename;

	bool cont = dir.GetFirst(&filename, "", wxDIR_FILES);
	while ( cont ) {
		TFileInfo rec;
		rec.duplicated = false;
		rec.first = false;
		rec.name = dirName + DIR_SEPARATOR + filename;

		wxFile f(rec.name.c_str());
		rec.size = f.Length();
			
		files.push_back(rec);
//		printf("%s %i\n", rec.name.c_str(), rec.size);
		cont = dir.GetNext(&filename);
	}

	// добавляем подкаталоги
	cont = dir.GetFirst(&filename, "", wxDIR_DIRS);
	while ( cont ) {
		TFileInfo rec;
		paths.push_back(dirName + DIR_SEPARATOR + filename);		
		cont = dir.GetNext(&filename);
	}

}






long long hash(wxString fileName) {
	wxFile f(fileName);
	long long result = 0;
	unsigned int size = f.Length();
	unsigned int pos = 0;

	const int BUF_SIZE = 1024*64;

	unsigned char* buffer = new unsigned char[BUF_SIZE];
	while ( pos < size ) {
		size_t readedCount = f.Read(buffer, BUF_SIZE);
		pos += readedCount;
		for ( size_t i = 0; i < readedCount; i++)
			result = 0xFF * result + buffer[i];
	}
	delete[] buffer;
	return result;
}



int main(int argc, char* argv[]) {
	// prepare list of directories
	if ( argc < 2 ) 
		paths.push_back(wxGetCwd());
	else 
		for ( int i = 1; i < argc; i++) {
			wxString s = argv[i];
			if ( s[s.length()-1] == DIR_SEPARATOR )
				s = s.substr(0, s.length()-1);
			paths.push_back(s);
		}

	printf("directories: \n");
	for ( unsigned int i = 0; i < paths.size(); i++ )
		printf("%s\n", paths[i].c_str());
	printf("\n");

	// scan directory and list files
	size_t dirIndex = 0;
	while ( dirIndex < paths.size() ) 
		addFiles(paths[dirIndex++]);
	
	int filesProcessed = files.size();	

	printf("Found: %i files\n", filesProcessed);
	// loop through all the files and delete from the list of those that have a unique size
	for ( size_t i = 0; i < files.size(); i++) {
		bool original = true;
		for ( size_t j = 0; j < files.size(); j++ ) {
			if ( i != j && files[i].size == files[j].size ) {
				original = false;
				break;
			}			
		}
		files[i].duplicated = !original;
	}

	// remove unique files from the list and compute hashes for other files
	vector <TFileInfo>::iterator it = files.begin();
	while ( it != files.end() ) {
		if ( !(*it).duplicated ) 
			files.erase(it);
		else {
			(*it).crc = hash((*it).name);
			it++;
		}
	}



	vector <TFileInfo> toDelete;
	size_t toDeleteSize = 0;

	printf("Duplicated files:\n");
	size_t index = 0;
	// scan the list, select the files with the same crc and delete all duplicates
	vector <TFileInfo>::iterator it1 = files.begin();
	while ( it1 != files.end() ) {
		vector <TFileInfo>::iterator it2 = it1 + 1;

		while ( it2 != files.end() ) {
			if ( (*it2).crc == (*it1).crc && (*it2).size == (*it1).size ) {
				toDelete.push_back((*it2));
				toDeleteSize += (*it2).size;
				printf("%i: %s [%i]  (%s)\n", ++index, (*it2).name, (*it2).size, (*it1).name);
				files.erase(it2);
			} else
				it2 ++;
		}
		it1++;
	}

	
//	for (size_t i = 0; i < toDelete.size(); i++)
//		printf("%i: %s [%i]\n", i+1, toDelete[i].name, toDelete[i].size);
	printf("\nTotal: %i kbytes in %i files\n", toDeleteSize / 1024, toDelete.size());
	printf("Processed: %i files\n", filesProcessed);


	if ( toDelete.size() > 0 ) {
		printf("Remove duplicated files? (y/n)\n");
		char ch;
		while ( true ) {
			std::cin >> ch;
			if ( ch == 'y' || ch == 'Y' ) {
				for (size_t i = 0; i < toDelete.size(); i++)
					remove(toDelete[i].name.c_str());
				break;
			} else if ( ch == 'n' || ch == 'N' )
				break;
		}
	}





	return 0;
}

