/*----------------------------------------------------------------------------+
 |       ___     _       _                                                    |
 |      /   |   | |     | |                                                   |
 |     / /| | __| | ___ | |__   ___                                           |
 |    / /_| |/ _  |/ _ \|  _ \ / _ \                                          |
 |   / ___  | (_| | (_) | |_) |  __/                                          |
 |  /_/   |_|\__,_|\___/|____/ \___|                                          |
 |                                                                            |
 |                                                                            |
 |  ADOBE CONFIDENTIAL                                                        |
 |  __________________                                                        |
 |                                                                            |
 |  Copyright (c) 2003 - 2007, Adobe Systems Incorporated.                    |
 |  All rights reserved.                                                      |
 |                                                                            |
 |  NOTICE:  All information contained herein is, and remains the property    |
 |  of Adobe Systems Incorporated and its suppliers, if any. The intellectual |
 |  and technical concepts contained herein are proprietary to Adobe Systems  |
 |  Incorporated and its suppliers and may be covered by U.S. and Foreign     |
 |  Patents, patents in process, and are protected by trade secret or         |
 |  copyright law. Dissemination of this information or reproduction of this  |
 |  material is strictly forbidden unless prior written permission is         |
 |  obtained from Adobe Systems Incorporated.                                 |
 |                                                                            |
 |          Adobe Systems Incorporated       415.832.2000                     |
 |          601 Townsend Street              415.832.2020 fax                 |
 |          San Francisco, CA 94103                                           |
 |                                                                            |
 +----------------------------------------------------------------------------*/
#include "StdAfx.h"
#include "SimpleFileAdaptor.h"
#include "FileModule.h"

#ifdef WIN32

inline static int open( const char* file, int flags, int mode )	{ return _open( file, flags, mode ); }
inline static int close( int fd )									{ return _close( fd ); }
inline static int read( int fd, void* buf, unsigned len )			{ return _read( fd, buf, len ); }
inline static int write( int fd, void* buf, unsigned len )			{ return _write( fd, buf, len ); }
inline static int fstat( int fd, stbuf* st )						{ return _fstati64( fd, st ); }
inline static int stat( const char* file, stbuf* st )				{ return _stati64( file, st ); }
inline static int unlink( const char* file )						{ return _unlink(file); }
inline static int rmdir( const char* file )							{ return _rmdir(file); }
inline static int mkdir( const char* file, int mask)				{ return _mkdir(file); }
inline static __int64 lseek(int fd, __int64 offset, int origin)     { return _lseeki64(fd, offset, origin); }


BOOL WINAPI DllMain(
    HINSTANCE hinstDLL,  // Handle to DLL module
    DWORD fdwReason,     // Reason for calling function
    LPVOID lpReserved )  // Reserved
{
	return TRUE;
}
#endif

///////////////////////////////////////////////////////////////////

extern "C" void FCExport FmsCreateFileAdaptor(IFmsServerContext* pServerCtx, IFmsFileResponse* pResponse, IFmsFileAdaptor*& pFileAdaptor )
{
	pFileAdaptor = new SimpleFileAdaptor( pServerCtx, pResponse );
}

extern "C" void FCExport FmsDestroyFileAdaptor(IFmsFileAdaptor* pFileAdaptor)
{
	delete pFileAdaptor;
}

extern "C" void FCExport FmsCreateFileAdaptor2(IFmsServerContext* pServerCtx, void* pResponse, void*& pFileAdaptor, U32& iVersion )
{
	pFileAdaptor = new SimpleFileAdaptor( pServerCtx, (IFmsFileResponse*)pResponse );
	iVersion = MKLONG(IFMSFILEADAPTOR_MINOR_VERSION, IFMSFILEADAPTOR_MAJOR_VERSION); 
}

extern "C" void FCExport FmsDestroyFileAdaptor2(void* pFileAdaptor)
{
	delete (IFmsFileAdaptor*)pFileAdaptor;
}

///////////////////////////////////////////////////////////////////

SimpleFileAdaptor::SimpleFileAdaptor( IFmsServerContext* pServer, IFmsFileResponse* pResponse ) :
	m_pServer(pServer), m_pResponse(pResponse)
{

#ifdef WIN32
/*
This section introduces a thread pool implementation used by the plug-in. Any calls from
Flash Media Interactive Server should be returned by the plug-in as quickly as possible.
If a call requires processing time, the plug-in must store received parameters
and pass them through the implemented thread pool for further processing.
This sample only provides a Windows thread pool implementation
and should be revisited on Linux.
*/
	static const int THREADPOOL_SIZE = 5;

	HRESULT hr = m_pool.Initialize((void*)this, THREADPOOL_SIZE);
	if( FAILED( hr ) ) {
		PrintLog("Failed to init thread pool!");
	}
#endif
}

SimpleFileAdaptor::~SimpleFileAdaptor()
{
#ifdef WIN32
/*
This section introduces a thread pool implementation used by the plug-in. Any calls from
Flash Media Interactive Server should be returned by the plug-in as quickly as possible.
If a call requires processing time, the plug-in must store received parameters
and pass them through the implemented thread pool for further processing.
This sample only provides a Windows thread pool implementation
and should be revisited on Linux.
*/
	// Shutdown the thread pool.
	m_pool.Shutdown();
#endif
}

void SimpleFileAdaptor::PrintLog(const char* messsage)
{
	// A message is printed in the server log; if true it is also printed in the event log.
	m_pServer->log(messsage, IFmsServerContext::kInformation, false);
}

int SimpleFileAdaptor::close( int iHandle, void* pCtx )
{		
	if (iHandle < 0)
		return -1;

	U32 uStatus = !::close(iHandle) ?  0 : IFmsFileResponse::kSystemErrors ;

	if (pCtx)
		m_pResponse->onClose(uStatus, pCtx );

	return 0;
}

int SimpleFileAdaptor::remove( const char* sFileName,  unsigned long uFlags, void* pCtx )
{
	if (!sFileName || !strlen(sFileName))
	{
		return -1;
	}

	int nVal = 0;

	if (uFlags & kFile)
		{
			nVal = unlink(sFileName) ;
		}
	else
		if (uFlags & kEmptyDirs)
		{
			nVal = ::rmdir(sFileName);
		}
	else
		if (uFlags & kSubDirs || uFlags & kWildCards)
		{
			nVal = removeDir(sFileName, uFlags);
		}
	else
		{
			nVal = -1;
		}
	U32 uStatus = !nVal  ? 0 : IFmsFileResponse::kSystemErrors ;
	if (pCtx)
		m_pResponse->onRemove(uStatus, pCtx);
	return 0;
}

int SimpleFileAdaptor::createDir( const char* sFileName, void* pCtx )
{
	int len;

	if (!sFileName || !(len = strlen(sFileName)))
		return -1;

	char* fname = new char [len + 1];
	int	res;
	strcpy(fname, sFileName);

	int mask = 0;

#ifndef WIN32
	mask = S_IRWXU|S_IRWXG|S_IRWXO;
#endif

	for (int i=0; i < len; i++)
	{
		if ((fname[i] == '/' || fname[i] == '\\') && i != 0)
		{
			fname[i] = '\0';
			res = ::mkdir(fname, mask);
			fname[i] = '/';
		}
	}
	// Create the last directory.
	if (fname[len - 1] != '/' && fname[len - 1] != '\\')
	{
		res = ::mkdir(fname, mask);
	}

	U32 uStatus = !res ? 0 : IFmsFileResponse::kSystemErrors;
	m_pResponse->onCreateDir(uStatus, pCtx);
	delete [] fname;
	return 0;
}

int SimpleFileAdaptor::getAttributes( const char* sFileName, int* pAttrKeys, int nAttrs, void* pCtx )
{
	if (!sFileName || !strlen(sFileName))
	{
		return -1;
	}

	U32 uStatus = 0;
	int nSize = nAttrs;
	FmsFileAttribute* pAttribute = NULL;

	stbuf buffer64;

	if (!stat(sFileName, &buffer64))
	{
		if (isAttributeAllExist(pAttrKeys, nAttrs))
		{
			nSize = FmsFileAttribute::kMaxAttr;
			pAttribute = new FmsFileAttribute[nSize];
		}
		else
			pAttribute = new FmsFileAttribute[nAttrs];
		setupFileAttribute(pAttrKeys, pAttribute, nSize, &buffer64);
		m_pResponse->onGetAttributes(uStatus,pAttribute,nSize,pCtx);

		if (pAttribute)
		{
			delete [] pAttribute;
			pAttribute = NULL;
		}
	}
	else
	{
		m_pResponse->onGetAttributes(IFmsFileResponse::kInvalidHandle,pAttribute,nSize,pCtx);
	}
	return 0;
}

int SimpleFileAdaptor::getAttributes( int iHandle, int* pAttrKeys, int nAttrs, void* pCtx )
{
	if (iHandle < 0)
		return -1;

	U32 uStatus = 0;
	int nSize = nAttrs;
	FmsFileAttribute* pAttribute = NULL;

	stbuf buffer64;
	fstat(iHandle, &buffer64);
	if (isAttributeAllExist(pAttrKeys, nAttrs))
	{
		nSize = FmsFileAttribute::kMaxAttr;
		pAttribute = new FmsFileAttribute[nSize];
	}
	else
		pAttribute = new FmsFileAttribute[nAttrs];
	setupFileAttribute(pAttrKeys, pAttribute, nSize, &buffer64);
	m_pResponse->onGetAttributes(uStatus,pAttribute,nSize,pCtx);
	if (pAttribute)
	{
		delete [] pAttribute;
		pAttribute = NULL;
	}
	return 0;
}

int SimpleFileAdaptor::open( const char* sFileName, int iFlags, int iMode, int* pAttrKeys, int nAttrs, void* pCtx )
{
	if (!sFileName || !strlen(sFileName))
	{
		return -1;
	}

#ifdef WIN32
/*
This section introduces a thread pool implementation used by the plug-in. Any calls from
Flash Media Interactive Server should be returned by the plug-in as quickly as possible.
If a call requires processing time, the plug-in must store received parameters
and pass them through the implemented thread pool for further processing.
This sample only provides a Windows thread pool implementation
and should be revisited on Linux.
*/

	struct open_param par;
	par.sFileName = sFileName;
	par.iFlags = iFlags;
	par.iMode = iMode;
	par.pAttrKeys = pAttrKeys;
	par.nAttrs = nAttrs;
	par.pCtx = pCtx;
	par.pAdaptor = this;
	CTaskBase* pTask = new CTaskOpen(par);
	if (!m_pool.QueueRequest((CMyWorker::RequestType) pTask ))
		return -1;
	return 0;
#else
	return openFile(sFileName, iFlags, iMode, pAttrKeys, nAttrs, pCtx);
#endif
}

int SimpleFileAdaptor::openFile( const char* sFileName, int iFlags, int iMode, int* pAttrKeys, int nAttrs, void* pCtx )
{	
	stbuf buffer64;

	//remove slash if directory
	std::string strFileName(sFileName);
	if (strFileName.at(strFileName.length() - 1) == '/' || strFileName.at(strFileName.length() - 1) == '\\')
		strFileName = strFileName.substr(0, strFileName.length() - 1);
	
	if (stat(strFileName.c_str(), &buffer64))
	{	
		char buf[1024];
		sprintf(buf, "failed open stat file %s", sFileName);
		PrintLog(buf);

		m_pResponse->onOpen(IFmsFileResponse::kSystemErrors,-1,NULL,nAttrs,pCtx);
		
		return 0;
	}
	
	U32 uStatus = 0;
	int nSize = nAttrs;
	int nHandle = -1;
	FmsFileAttribute* pAttribute = NULL;

#ifndef WIN32
	if (iMode & S_IWRITE)
	{
		iMode = S_IREAD|S_IWRITE|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
	}
	else if (iMode & S_IREAD)
	{
		iMode = S_IREAD|S_IRGRP|S_IROTH;
	}
#endif
	
	if (!S_ISDIR(buffer64.st_mode))
	{	
		char buf[1024];
		sprintf(buf, "open file %s", sFileName);
		PrintLog(buf);
		nHandle = (iFlags & O_CREAT) ? ::open(sFileName, iFlags, iMode) : ::open(sFileName, iFlags);
	}
	else
	{
		char buf[1024];
		sprintf(buf, "open directory %s", sFileName);
		PrintLog(buf);
		//open temp file and write dir content
#ifdef WIN32		
		FILE* hFile = NULL;
		if (!tmpfile_s(&hFile))
		{
			nHandle = fileno(hFile);
			if (nHandle >= 0) 
				nHandle = ::dup(nHandle);
			if (nHandle >= 0)
				lseek(nHandle, 0L, SEEK_SET);
			fclose(hFile);
		}
#else
		char sfn[] = "/tmp/fileXXXXXX";
		nHandle = mkstemp(sfn);
		unlink(sfn);
#endif					
		if (nHandle >= 0)
		{					
			stbuf* pbuff = &buffer64;
			if (!writeDirContent(sFileName, pbuff, nHandle))
			{
				::close(nHandle);
				nHandle = -1;			
			}					
		}				
	}

	if (nHandle < 0)
	{
		uStatus = IFmsFileResponse::kSystemErrors;
	}

	if (!pAttrKeys || !nAttrs || uStatus)
	{
		m_pResponse->onOpen(uStatus,nHandle,NULL,nAttrs,pCtx);
		
		return 0;
	}
	
	if (isAttributeAllExist(pAttrKeys, nAttrs))
	{
		nSize = FmsFileAttribute::kMaxAttr;
	}
	pAttribute = new FmsFileAttribute[nSize];

	setupFileAttribute(pAttrKeys, pAttribute, nSize, &buffer64);
	m_pResponse->onOpen(uStatus,nHandle,pAttribute,nSize,pCtx);
	if (pAttribute)
	{
		delete [] pAttribute;
		pAttribute = NULL;
	}
	
	return 0;
}


int SimpleFileAdaptor::read( int iHandle, I64 uOffset, char* pBuffer, int nBytes, void* pCtx )
{
	if ((iHandle < 0) || !pBuffer || (nBytes < 0))
	{
		return -1;
	}

	if (uOffset != -1) //no seek
	{
		if (lseek(iHandle, uOffset, SEEK_SET) == -1)
		{
			m_pResponse->onRead(IFmsFileResponse::kSystemErrors, pBuffer, 0, pCtx);
			return 0;
		}
	}

	int nCount = ::read(iHandle, pBuffer, nBytes);
	if ( nCount == -1 )
	{
		m_pResponse->onRead(IFmsFileResponse::kSystemErrors, pBuffer, 0, pCtx);
	}
	else
	{
		m_pResponse->onRead(0,pBuffer,nCount,pCtx);
	}
	return 0;
}

int SimpleFileAdaptor::write( int iHandle, I64 uOffset, char* pBuffer, int nBytes, void* pCtx )
{
	if ((iHandle < 0) || !pBuffer || (nBytes < 0))
	{
		return -1;
	}

	if (uOffset != -1) //no seek
	{
		if (lseek(iHandle, uOffset, SEEK_SET) == -1)
		{
			m_pResponse->onWrite(IFmsFileResponse::kSystemErrors, 0, pCtx);
			return 0;
		}
	}

	int nCount = ::write(iHandle, pBuffer, nBytes);
	if (nCount == -1)
	{
		m_pResponse->onWrite(IFmsFileResponse::kSystemErrors, 0, pCtx);
	}
	else
	{
		m_pResponse->onWrite(0,nCount,pCtx);
	}
	return 0;
}

bool SimpleFileAdaptor::writeDirContent(const char* file, stbuf* &st, int fd)
{	
	///walking directory	
	std::string 	strFileName(file);
	if (strFileName.at(strFileName.length() - 1) == '/' || strFileName.at(strFileName.length() - 1) == '\\')
		strFileName = strFileName.substr(0, strFileName.length() - 1);
	if (!stat(strFileName.c_str(), st) && S_ISDIR(st->st_mode))
	{
		char endl = '\n';
		std::string strFileSearch(file);	
					
#ifdef WIN32
		if (strFileSearch.at(strFileSearch.length() - 1) != '/')
			strFileSearch += '/';
		strFileSearch += '*';
		
		WIN32_FIND_DATA wfd;
		HANDLE h = FindFirstFile(strFileSearch.c_str(), &wfd);
		
		if (h != INVALID_HANDLE_VALUE)	
		{			
			do 
			{	
				std::string strFileName(std::string(wfd.cFileName));
				if (strFileName.length() && strFileName.compare(".") != 0 && strFileName.compare("..") != 0)
				{					
					::write(fd, strFileName.c_str(), strFileName.length());
					::write(fd, &endl, 1);					
				}
			}	 
			while (FindNextFile(h, &wfd));
			FindClose(h);
			return true;
		}
#else		    

		DIR					*dirStruct;      
		struct dirent		*direntp;        
		if ((dirStruct = opendir(file)) != NULL)
		{				
			while ((direntp = readdir(dirStruct)) != NULL)
			{				
				if (direntp->d_name && strcmp(direntp->d_name, ".") != 0 && strcmp(direntp->d_name, "..") != 0)
				{				
					::write(fd, direntp->d_name, strlen(direntp->d_name));
					::write(fd, &endl, 1);					
				}
			}			         
			closedir(dirStruct);
			return true;
		}
#endif
	}	
	return false;
}

//////////////////////////////////////////////////////////////////////////////
/*
This section introduces a thread pool implementation used by the plug-in. Any calls from
Flash Media Interactive Server should be returned by the plug-in as quickly as possible.
If a call requires processing time, the plug-in must store received parameters
and pass them through the implemented thread pool for further processing.
This sample only provides a Windows thread pool implementation
and should be revisited on Linux.
*/

#ifdef WIN32

CMyWorker::CMyWorker()
{
}

BOOL CMyWorker::Initialize(void *pvParam)
{
	char buf[1024];
	SimpleFileAdaptor* pAdaptor = (SimpleFileAdaptor*)pvParam;
	sprintf(buf, "[%d]: CMyWorker.Initialize(%d)", (DWORD_PTR)::GetCurrentThreadId(), (DWORD_PTR)pvParam );
	pAdaptor->PrintLog(buf);
	return TRUE;
}

void CMyWorker::Terminate(void* pvParam)
{
	char buf[1024];
	SimpleFileAdaptor* pAdaptor = (SimpleFileAdaptor*)pvParam;
	sprintf(buf,  "CMyWorker.Terminate");
	pAdaptor->PrintLog(buf);
}

void CMyWorker::Execute(RequestType dw, void *pvParam, OVERLAPPED* pOverlapped) throw()
{
	ATLASSERT(pvParam != NULL);

	char buf[1024];
	SimpleFileAdaptor* pAdaptor = (SimpleFileAdaptor*)pvParam;

	sprintf(buf, "[%d] CMyWorker::Execute(dw=%d, pvParam=%d, pOverlapped=%d",
	::GetCurrentThreadId(), dw, (DWORD_PTR)pvParam, (DWORD_PTR)pOverlapped);
	//pAdaptor->PrintLog(buf);

	CTaskBase* pTask = (CTaskBase*)(DWORD_PTR)dw;
	pTask->DoTask(pvParam, pOverlapped);
	delete pTask;
}

BOOL CMyWorker::GetWorkerData(DWORD /*dwParam*/, void ** /*ppvData*/)
{
    return FALSE;
}

void CTaskOpen::DoTask(void *pvParam, OVERLAPPED *pOverlapped)
{
	char buf[1024];
	sprintf(buf, "[%d]: CTaskOpen::DoTask(pvParam=%d, pOverlapped=%d",
			::GetCurrentThreadId(), (DWORD_PTR)pvParam, (DWORD_PTR)pOverlapped);
	//m_Param.pAdaptor->PrintLog(buf);
	m_Param.pAdaptor->openFile( m_Param.sFileName.c_str(), m_Param.iFlags, m_Param.iMode, m_Param.pAttrKeys, m_Param.nAttrs, m_Param.pCtx );
}
#endif

