/* TreeSize for Unix  http://treesize.sf.net/
 * Copyright (c) 2006-2007 Marcos Diez <marcos_AT_unitron.com.br>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU 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 General Public License for
 * more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "TSmonitor.h" 
#define _FILE_OFFSET_BITS 64
#define GO_TO_FOLDER 1
int lstat(const char *file_name, struct stat *buf);

#include "FIfolderInfo.h"
#include "FAfolderAnalyzer.h"

void printSize(FolderInfo thisFolderInfo) {
	printf("%lld\t%s\n",
	/* 	 printFileSize( thisFolderInfo->totalLogicalSize ) , */
	thisFolderInfo->totalLogicalSize, thisFolderInfo->name);

}
void printInfo(char *name, struct stat *fileStat) {

	printf("(%4u ) ", (unsigned int) fileStat->st_dev );

	printf("%10u %2u"
		"%10u %4u %8u "
		"%s %s \n", (unsigned int) fileStat->st_ino ,
			(unsigned int) fileStat->st_nlink ,

			(unsigned int) fileStat->st_size ,
			(unsigned int) fileStat->st_blksize ,
			(unsigned int) fileStat->st_blocks , 

			S_ISREG( fileStat->st_mode ) ? "FILE" :
			S_ISDIR( fileStat->st_mode ) ? "DIR " : "OTHR", name);
}

static char *makeDirPath(char *dirName, FolderInfo father) {
	if ( !father)
		return strdup(dirName);
	{
		int dirNameSize= strlen(dirName);
		int fatherSize = strlen( FIgetPath( father ));

		char *answear = malloc(sizeof(char)*(dirNameSize + fatherSize + 2 ));

		strcpy(answear, FIgetPath( father ));
		answear[fatherSize]='/';
		strcpy(answear + 1 + fatherSize, dirName);

		return answear;
	}
}
static int folderSorter(const void *ff1, const void *ff2) {
	const FolderInfo f1=*( ( FolderInfo *) ff1);
	const FolderInfo f2=*( ( FolderInfo *) ff2);

	/* for quickSort, biggest files first */
	if (ff1 == 0)
		return 1;
	if (ff2 == 0)
		return -1;

	if ( FIgetPhysicalSize( f1 ) == FIgetPhysicalSize( f2 )) {
		return strcoll( FIgetName( f1 ) , FIgetName( f2 ));
	}
	if ( FIgetPhysicalSize( f1 ) > FIgetPhysicalSize( f2 ))
		return -1;
	return 1;
}

char tempDirName[MAX_DIR_NAME];
bool FAanalyzeRec( char *dirName , struct stat *dirStat , FolderInfo father , FolderInfo *newFolderInfo , bool onlyOneFileSystem ) {

	int numSubFiles=0;
	int numSubFolders=0;
	DIR *dirFD;
	struct dirent *dirEnt;

	bool accessible=true;
	FolderInfo thisFolderInfo;
	char *dirPath;

	if( ! TSrunning ) return false;

	/* let's go to the directory! */
	if( chdir( dirName ) == -1 ) {
		accessible=false;
		perror( dirName );
	}
	dirFD = opendir( "." );
	if( !dirFD ) {
		accessible=false;
		perror( dirName );
	}

	dirPath= makeDirPath( dirName, father );

	/*   printf("dirName [%s] , dirPath [%s]\n" , dirName , dirPath ); */

	*newFolderInfo=thisFolderInfo=FInew( dirPath , dirStat , accessible , father );
	
	if( accessible &&                          // this folder MUST be accessible 
		( !onlyOneFileSystem ||                // onlyOneFileSystem must not be checked
				!father ||                     // there is no father
				FIsameDevs( father , (*newFolderInfo) ) )  // the filesystems are the same
			
	
		) {
		while ((dirEnt = readdir ( dirFD ))) {
			struct stat fileStat;
			char *name = dirEnt->d_name;
			if( lstat( name , &fileStat ) == -1 ) {
				perror( name );
			} else {
				/* 	  printInfo( name , &fileStat ); */
				if( strcmp( name , ".." )) {
					if( !strcmp( name , "." ) ||
							S_ISREG( fileStat.st_mode ) ) { /* regular file or a "special" directory */
						/* 	      	    	    printInfo( name , &fileStat ); */
						FIaddFile( thisFolderInfo , name , &fileStat );
						numSubFiles++;
					} else if( S_ISDIR( fileStat.st_mode ) ) { /* directory */
						numSubFolders++;
						FIaddFolder( thisFolderInfo , name , &fileStat , onlyOneFileSystem );
					}
				}
			}
		}
		/* ok, I have all my subfonder now. Let me sort them! */
		/*     void qsort(void *base, size_t nmemb, size_t size, */
		/* 	       int(*compar)(const void *, const void *)); */

		if( numSubFolders && numSubFolders ) {
			FIaddFileFolder( thisFolderInfo );
		}
		qsort( FIgetSubFolders( thisFolderInfo ) ,
				FIgetNumSubFolders( thisFolderInfo ) ,
				sizeof( thisFolderInfo ) , folderSorter );

	}
	closedir( dirFD );
	if( father ) chdir( FIgetPath( father ));
	return accessible;
}

bool FAgetFolderAbsoluteName( char *relativeFolderName , char *absoluteFolderName , struct stat *dirStat , bool goToFolder ) {
	/* if you give me "." , I will return /tmp, for example */
	char actualDirName[MAX_DIR_NAME];

	if( !goToFolder ) {
		getcwd( actualDirName, MAX_DIR_NAME );
		if( actualDirName[0] == 0 ) {
			perror("getcwd");

		}
	}

	if( dirStat ) {
		if( stat( relativeFolderName , dirStat ) == -1 ) {
			perror( relativeFolderName );
		}

		if( !S_ISDIR( dirStat->st_mode )) {
			printf("ERROR, %s was supposed to be a directory\n" , relativeFolderName );
			return false;
		}
	}

	if( chdir( relativeFolderName ) == -1 ) {
		perror( relativeFolderName );
		return false;
	}

	getcwd( absoluteFolderName , MAX_DIR_NAME );
	if( absoluteFolderName[0] == 0 ) {
		perror("getcwd");
		return false;
	}

	if( !goToFolder ) {
		getcwd( actualDirName, MAX_DIR_NAME );
		if( actualDirName[0] == 0 ) {
			perror("getcwd");
			return false;
		}
	}

	return true;
}

FolderInfo FAanalyze(char *dirName, bool onlyOneFileSystem) {
	struct stat dirStat;
	FolderInfo rootFolder;
	bool ok;
	char absoluteDirName[MAX_DIR_NAME];
	FAgetFolderAbsoluteName(dirName, absoluteDirName, &dirStat, GO_TO_FOLDER);
	/*   printf("%s %s\n" , __FUNCTION__ , dirName ); */

	/*   puts( absoluteDirName ); */
	ok = FAanalyzeRec(absoluteDirName, &dirStat, 0, &rootFolder,
			onlyOneFileSystem);
	/*   FIprint( rootFolder ); */

	return ok ? rootFolder : 0;
}

