// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010  Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include <nel/misc/path.h>
#include <nel/misc/debug.h>
#include <nel/misc/file.h>

#include <nel/misc/sha1.h>

using namespace std;
using namespace NLMISC;


//-----------------------------------------------
//	main
//
//-----------------------------------------------
int main( int argc, char ** argv )
{
	if( argc < 3 || argc > 4 )
	{
		printf("Build a listing of the diff of two path contents and copy new files in <dest_path>\n");
		printf("usage: path_content <ref_path> <new_path> [<dest_path>]\n");
		return EXIT_FAILURE;
	}
	
	string DestPath;
	if( argc == 4 )
	{
		DestPath = CPath::standardizeDosPath(argv[3]);
		if(CFile::isExists(DestPath))
		{
			if(!CFile::isDirectory(DestPath))
			{
				printf("'%s' is not a directory\n", DestPath.c_str());
				return EXIT_FAILURE;
			}
		}
		else
		{
			if (!CFile::createDirectory(DestPath))
			{
				printf("Can't create directory: '%s'\n", DestPath.c_str());
				return EXIT_FAILURE;
			}
		}
	}

	// content of new path
	string newPath(argv[2]);
	vector<string> newPathContent;
	CPath::getPathContent(newPath, true, false, true, newPathContent);
	
	string outputFileName = CFile::findNewFile("path_content_diff.txt");
	FILE *output = fopen (outputFileName.c_str(), "wt");
	if( output == NULL )
	{
		nlwarning("Can't open output file %s",outputFileName.c_str());
		return EXIT_FAILURE;
	}

	// add ref path in search paths
	string refPath(argv[1]);
	CPath::addSearchPath(refPath, true, false);
	
	map<string,CHashKey> refSHAMap;
	CIFile refSHAFile;
	if( refSHAFile.open(refPath +".sha1key") )
	{
		// load the map of SHA hash key for the ref files
		refSHAFile.serialCont( refSHAMap );
		refSHAFile.close();
	}
	else
	{
		// build the map of SHA hash key for the ref files
		string extension;
		vector<string> refPathContent;
		CPath::getFileList(extension, refPathContent);
		vector<string>::const_iterator itFile;
		for( itFile = refPathContent.begin(); itFile != refPathContent.end(); ++itFile )
		{
			refSHAMap.insert( make_pair(*itFile,getSHA1(*itFile)) );
		}
		COFile refSHAFile(refPath + ".sha1key");
		refSHAFile.serialCont( refSHAMap );
	}


	// build the map of SHA hash key for new files
	map<string,CHashKey> newSHAMap;
	vector<string>::const_iterator itFile;
	for( itFile = newPathContent.begin(); itFile != newPathContent.end(); ++itFile )
	{
		newSHAMap.insert( make_pair(*itFile,getSHA1(*itFile)) );
	}

// display (debug)
	map<string,CHashKey>::iterator itSHA;
	/*
	for( itSHA = refSHAMap.begin(); itSHA != refSHAMap.end(); ++itSHA )
	{
		nlinfo("(ref) %s : %s",(*itSHA).first.c_str(),(*itSHA).second.toString().c_str());
	}
	for( itSHA = newSHAMap.begin(); itSHA != newSHAMap.end(); ++itSHA )
	{
		nlinfo("(new) %s : %s",(*itSHA).first.c_str(),(*itSHA).second.toString().c_str());
	}
	*/
//

	uint32 LastDisplay = 0, curFile = 0;

	// get the list of new or modified files
	vector<string> differentFiles;
	for( itFile = newPathContent.begin(); itFile != newPathContent.end(); ++itFile )
	{
		string newFileName = *itFile;
		string newFileNameShort = CFile::getFilename(newFileName);

		curFile++;

		if (CTime::getSecondsSince1970() > LastDisplay + 5)
		{
			printf("%d on %d files, %d left\n", curFile, newPathContent.size(), newPathContent.size() - curFile);
			LastDisplay = CTime::getSecondsSince1970();
		}

		if( CFile::getExtension(newFileNameShort) == "bnp" )
		{
			nlwarning ("BNP PROBLEM: %s is a big file, content of big files is not managed", newFileName.c_str());
			nlwarning ("The <new_path> must *not* contains .bnp files");
		}

		bool keepIt = false;

		string refFileName = CPath::lookup(strlwr(newFileNameShort), false, false, false);
		if( refFileName.empty() )
		{
			keepIt = true;
			nlinfo ("new file : %s",newFileNameShort.c_str());
		}
		else
		{
			itSHA = refSHAMap.find( newFileNameShort );
			CHashKey refSHA;
			if( itSHA != refSHAMap.end() )
			{
				refSHA = (*itSHA).second;
			}

			itSHA = newSHAMap.find( newFileName );
			CHashKey newSHA;
			if( itSHA != newSHAMap.end() )
			{
				newSHA = (*itSHA).second;
			}

			if( !(refSHA==newSHA) )
			{
				keepIt = true;
				nlinfo("file : %s , key : %s(%s), size : %d(%d)",newFileNameShort.c_str(), newSHA.toString().c_str(),refSHA.toString().c_str(),CFile::getFileSize(newFileName),CFile::getFileSize(refFileName));
			}
			
			/*
			uint32 refModificationDate = CFile::getFileModificationDate( refFileName );
			uint32 newModificationDate = CFile::getFileModificationDate( newFileName );		

			if( newModificationDate > refModificationDate )
			{
				keepIt = true;
				nlinfo ("DATE CHANGED: %s", newFileName.c_str());
			}
			else
			{
				// same date, must be same size
				uint32 refSize = CFile::getFileSize( refFileName );
				uint32 newSize = CFile::getFileSize( newFileName );
				if( refSize != newSize )
				{
					nlwarning ("DATE PROBLEM: file '%s' have the same date but not the same size than '%s'", newFileName.c_str(), refFileName.c_str());
				}
			}
			*/
		}

		if( keepIt )
		{
			differentFiles.push_back( newFileName );

			//uint32 newCreationDate = CFile::getFileCreationDate( newFileName );
			//string outputLine = newFileName + "\t\t"+toString(newSize) + "\t" + toString(newModificationDate) + "\t" + toString(newCreationDate) + "\n";
			string outputLine = newFileName + "\n";
			fprintf (output, outputLine.c_str());
			
			if( !DestPath.empty() )
			{
				string systemStr = "copy /Y " + CPath::standardizeDosPath(newFileName) + " " + DestPath;
				//nlinfo("System call '%s'",systemStr.c_str());
				system( systemStr.c_str() );
			}
		}
	}

	return EXIT_SUCCESS;
}