#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <utime.h>
#include <stdio.h>
#include "functhread.h"

cFuncThread::cFuncThread(cOsd *pStatusOSD, const char *sFromFile, const char *sToFile, int iFunction, int *piReturn)
{
    m_sFromFile = strdup(sFromFile);
    m_sToFile   = strdup(sToFile);
    m_iProgressBarVal = -1;
    m_iFunction = iFunction;
    m_piReturn   = piReturn;
    SetReturn(-1);
    pOSD = pStatusOSD;
    Start();
}

void cFuncThread::SetReturn(int iValue)
{
    if ( m_piReturn )
	*m_piReturn = iValue;
}

void cFuncThread::Action(void)
{
    dsyslog("VDRC: function thread started (pid=%d)", getpid());
    switch(m_iFunction)
    {
	case func_copy_file:
		if ( Copy_File_File(m_sFromFile, m_sToFile) )
			SetReturn(0);
		break;
	case func_copy_dir:
		if ( Copy_Dir_Dir(m_sFromFile, m_sToFile) )
			SetReturn(0);
		break;
	case func_delete_dir:
		if ( Erase_Recursive(m_sFromFile) )
			SetReturn(0);
		break;
    }
    dsyslog("VDRC: end function thread");
}

cFuncThread::~cFuncThread()
{
    Cancel(0);
    delete m_sFromFile;
    delete m_sToFile;
}

bool cFuncThread::Copy_File_File(char *sSrc, char *sDest)
{
    bool bReturn = false;
    int fSource = 0;
    int fDest   = 0;
    struct stat stFile;
    
    if ( (fSource = open(sSrc, O_RDONLY)) > 0) // | O_LINEAR)) > 0)
    {
        int iBufSize = 8192; // Buffer Size 8K
	
	uid_t uidSrc = (uid_t) -1;
	uid_t gidSrc = (gid_t) -1;
	int   modeSrc = 0;
	struct utimbuf tmSrc;
	
	fstat(fSource, &stFile);
	
	// Get Data for CHOWN
	uidSrc		= stFile.st_uid;
	gidSrc		= stFile.st_gid;
	
	// Get Data for CHMOD
	modeSrc		= stFile.st_mode;
	
	// Get Time Data
	tmSrc.actime	= stFile.st_atime;
	tmSrc.modtime	= stFile.st_mtime;
	
//	dsyslog("VDRC: File Size %d", (int) stFile.st_size);
	if ( (fDest = open(sDest, O_WRONLY | O_CREAT | O_TRUNC, 0600) ) > 0)
	{
	    char *sBuf;
	    int iReadBytes  = 1;
	    int iWriteBytes = 0;
	    int iAllReadBytes = 0;
	    int iFileSize;
	    
	    sBuf = (char *) malloc(iBufSize);
	    
	    iFileSize = stFile.st_size / 1024;
	    ProgressBar(0, iFileSize);
	    
	    while (iReadBytes > 0)
	    {
		iReadBytes = read(fSource, sBuf, iBufSize);
		
		if (iReadBytes > 0)
		{
		    ProgressBar((iAllReadBytes / 1024), iFileSize);
		    iWriteBytes = write(fDest, sBuf, iReadBytes);
		    
		    iAllReadBytes += iReadBytes;
		}
	    }
	    
	    close(fDest);

	    if ( chown(sDest, uidSrc, gidSrc) == 0 )
		if ( chmod(sDest, modeSrc) == 0 )
		    utime(sDest, &tmSrc);
	    
	    free(sBuf);
	    
	    bReturn = true;
	}
	
	close(fSource);
    }
    
    return bReturn;
}


char *cFuncThread::MakeFileWithPath(char *sPath, char *sFile)
{
    char *sRet = NULL;
    
    if (strlen(sPath) > 1)
	asprintf(&sRet, "%s/%s", sPath, sFile);
    else
	asprintf(&sRet, "%s%s", sPath, sFile);

    return sRet;
}

bool cFuncThread::Erase_Recursive(char* sPath)
{
    bool		bReturn = false;
    DIR			*dDir;
    struct dirent	*stDir;
    
    dDir = opendir(sPath);
    
    if (dDir)
    {
	bool bNoError = true;
	
	while ( (stDir = readdir(dDir)) && bNoError)
	{
	    struct stat statFile;

	    if ( strcmp(stDir->d_name, ".")  == 0 ||
		 strcmp(stDir->d_name, "..") == 0 )
		 continue;
	
	    char *sFullFile = MakeFileWithPath(sPath, stDir->d_name);
	    	 
	    bNoError = (lstat(sFullFile, &statFile) == 0);
	    
	    if (bNoError)
	    {
		if ( S_ISDIR(statFile.st_mode) )
		    bNoError = Erase_Recursive(sFullFile);
		else
		{
		    bNoError = ( unlink(sFullFile) == 0 );
//		    dsyslog("vdrc: delete rec file \"%s\"", sFullFile);
		}
	    }
	    
	    if (sFullFile)
		free(sFullFile);
	}
	
	if (bNoError)
	{
	    closedir(dDir);
	    bNoError = (rmdir(sPath) == 0);
//	    dsyslog("vdrc: delete rec dir \"%s\"", sPath);
	}
	    
	if (bNoError)
	    bReturn = true;
    }
    
    return bReturn;
}

bool cFuncThread::Copy_Dir_Dir(char *sSrc, char *sDest)
{
    bool bReturn = false;
    struct stat stSrcFile;
    struct stat stDestFile;
    char *sD1;
    char *sD2;

    asprintf(&sD1, "%s/", sSrc);
    asprintf(&sD2, "%s/", sDest);
    if (strncasecmp(sD2, sD1, strlen(sD1)) != 0)
    {
        if ( lstat(sSrc, &stSrcFile) == 0 && S_ISDIR(stSrcFile.st_mode) )
	{
		bool bNoError = true;
		bool bCreated = ( lstat(sDest, &stDestFile) == 0);
		struct utimbuf tmSrc;

		// Get Time Data
		tmSrc.actime	= stSrcFile.st_atime;
		tmSrc.modtime	= stSrcFile.st_mtime;

		if ( !bCreated || S_ISDIR(stDestFile.st_mode) )
		{
			DIR		*dDir;
			struct dirent	*stDir;

			if (!bCreated)
			{

				bNoError = ( mkdir(sDest, stSrcFile.st_mode) == 0 );
				if (bNoError)
					bNoError = ( chown(sDest, stSrcFile.st_uid, stSrcFile.st_gid) == 0 );

//				dsyslog("vdrc: copy rec make dir \"%s\"", sDest);
			}


			if (bNoError)
			{    
				dDir = opendir(sSrc);
    
				if (dDir)
				{
					while ( (stDir = readdir(dDir)) && bNoError)
					{
						struct stat statFile;

						if ( strcmp(stDir->d_name, ".")  == 0 ||
							strcmp(stDir->d_name, "..") == 0 )
							continue;
	
						char *sSrcFullFile  = MakeFileWithPath(sSrc, stDir->d_name);
						char *sDestFullFile = MakeFileWithPath(sDest, stDir->d_name);
	    	 
						bNoError = (lstat(sSrcFullFile, &statFile) == 0);
	    
						if (bNoError)
						{
							if ( S_ISDIR(statFile.st_mode) )
								bNoError = Copy_Dir_Dir(sSrcFullFile, sDestFullFile);
							else
							{
								bNoError = Copy_File_File(sSrcFullFile, sDestFullFile);
//								dsyslog("vdrc: copy rec file \"%s\" to \"%s\"", sSrcFullFile, sDestFullFile);
							}
						}
  
						if (sSrcFullFile)
							free(sSrcFullFile);
						if (sDestFullFile)
							free(sDestFullFile);
					}
			
					closedir(dDir);
				}
			}

	    		bNoError = ( chmod(sDest, stSrcFile.st_mode) == 0 );
			if (bNoError)
				bNoError = ( utime(sDest, &tmSrc) == 0 );

		}
		else
		{
		    if (bCreated)
			dsyslog("Error copy Dir on File");
		    bNoError = false;
		}
			
		if (bNoError)
		    bReturn = true;
	}

    }
    else
	dsyslog("Error copy Parent into Child");

    free(sD1);
    free(sD2);

    return bReturn;
}

void cFuncThread::ProgressBar(int Current, int Total)
{
    if (Total > 0)
    {
 int Width = (Setup.OSDWidth * charWidth);
 int y = ((Setup.OSDHeight - 1)* lineHeight);
 int y1 = ((Setup.OSDHeight)* lineHeight);
	long p = Current * Width / Total;

	if (p != m_iProgressBarVal && (p % 16 == 0))
	{
 pOSD->DrawRectangle(m_iProgressBarVal + 0, y, p, y1, clr_vdrc_pbar);
 pOSD->DrawRectangle(p + 1, y, Width, y1, clr_vdrc_pbar_bk);
	    pOSD->Flush();
	    
	    m_iProgressBarVal = p;
	}
    }
}
