#if _MSC_VER > 1500 
#include <stdint.h>
#else 
#include "stdint.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
#include <locale.h>
#include <sys/stat.h>
#include <errno.h>
#include <direct.h>
#include <string.h>

#ifdef _USE_WIN_API
/*ifndef _USE_LINUX_API*/
#include <Windows.h>
#endif

//#define  _CRT_SECURE_NO_WARNINGS

#define MAX_LEN	1024

#include "bcap_client.h"

/* *** Please change IP to the target RC8 *** */ 
#define TARGET_RC8_IP	"192.168.0.1"


HRESULT download_files(char *FilePath);
HRESULT find_download_files(int fd, uint32_t hParent, int bRoot, char *FilePath);
HRESULT get_file(int fd, uint32_t hFile, char *FilePath, char *FileName);
void recursive_makedir(char *path);
int is_path_exist(char *fn);

HRESULT upload_files(char *FilePath);
HRESULT create_file(int fd, uint32_t hParent, int bRoot, char *FileName, uint32_t *phFile);
HRESULT find_upload_files(int fd, uint32_t hParent, int bRoot, char *FilePath);
HRESULT put_file(int fd, uint32_t hFile, char *FilePath, char *FileName);

/*
  This program will download all files from the target RC8 and then upload them back. 
*/

int main()
{
	HRESULT hr;
	char path[MAX_LEN];

	/* Sets the locale to the system-default ANSI code page obtained from the OS. */
	setlocale( LC_ALL, "" );

	_getcwd(path, MAX_LEN);
	strcat(path, "\\Files\\");

	printf("Enter any key to get all files from RC8 [%s]. \n", TARGET_RC8_IP);
	printf("The files will download into [%s]. \n", path);
	getch();

	hr = download_files(path);
	if (SUCCEEDED(hr)) {
		printf("Enter any key to put all files to RC8 [%s]. \n", TARGET_RC8_IP);
		getch();

		hr = upload_files(path);
	}

	if (FAILED(hr)) {
		printf("Failed !!\n");
		getch();
	}

	return 0;
}

HRESULT download_files(char *FilePath)
{
	int fd;
	uint32_t hCtrl;
	BSTR bstr1, bstr2, bstr3, bstr4;
	HRESULT hr;

	/* Open connection. specify the IP address of bCapServer */
	printf("Open connection to tcp:%s\n", TARGET_RC8_IP);
	hr = bCap_Open_Client("tcp:" TARGET_RC8_IP, 1000, 3, &fd);

	if(SUCCEEDED(hr)){
		/* Send SERVICE_START packet */
		bCap_ServiceStart(fd, NULL);

		bstr1 = SysAllocString(L"");                    /* Name */
		bstr2 = SysAllocString(L"CaoProv.DENSO.VRC");   /* Provider */
		bstr3 = SysAllocString(L"localhost");			/* Machine */
		bstr4 = SysAllocString(L"");					/* Option */

		/* Connect RC8 */
		hr = bCap_ControllerConnect(fd, bstr1, bstr2, bstr3, bstr4, &hCtrl);

		SysFreeString(bstr1);
		SysFreeString(bstr2);
		SysFreeString(bstr3);
		SysFreeString(bstr4);

		if(SUCCEEDED(hr)){

			hr = find_download_files( fd, hCtrl, -1, FilePath );

			/* Disconnect RC8 */
			bCap_ControllerDisconnect(fd, &hCtrl);
		}

		/* Send SERVICE_STOP packet */
		bCap_ServiceStop(fd);

		/* Close connection */
		bCap_Close_Client(&fd);
	}

	return hr;
}

HRESULT find_download_files(int fd, uint32_t hParent, int bRoot, char *FilePath)
{
	HRESULT hr = S_OK;
	VARIANT vResult;
	BSTR bstrOpt;

	bstrOpt = SysAllocString(L"");	// Option
	VariantInit(&vResult);
	if (bRoot) {
		/* get file list at controller root folder*/
		hr = bCap_ControllerGetFileNames(fd, hParent, bstrOpt, &vResult);
	} else {
		/* get file list at controller sub-folder*/
		hr = bCap_FileGetFileNames(fd, hParent, bstrOpt, &vResult);
	}
	SysFreeString(bstrOpt);

	if(SUCCEEDED(hr)){
		int32_t lbnd, ubnd, cnt, n;
		BSTR *pbstrData;
		/* find *.pcs, *.pns, *.h, *.bmp from the result */

		if (vResult.vt != (VT_BSTR | VT_ARRAY)) {
			return E_INVALIDARG;
		}

		SafeArrayGetLBound(vResult.parray, 1, &lbnd);
		SafeArrayGetUBound(vResult.parray, 1, &ubnd);
		cnt = ubnd - lbnd + 1;

		SafeArrayAccessData(vResult.parray, (void **)&pbstrData);
		for(n=0; n<cnt; n++) {
			int ilen, i, found;
			char filename[MAX_LEN]; // case insencetive
			int bFound, bFile, bToLower;

			/* convert UTF-16 to MBS */
			wcstombs(filename, pbstrData[n], MAX_LEN);

			/* change the file extension string to lowower charactor. */
			ilen = strlen(filename);
			found = 0;
			for(i=0; i<ilen; i++) {
				if (found) {
					filename[i] = tolower(filename[i]);
				} else if (filename[i] == '.') {
					found = -1;
				}
			}

			bFound = 0;
			bFile = 0;
			/* find '*.pcs' or '*.h' or '*.pns' or '*.bmp' */
			if ( strstr(filename, ".pcs") || strstr(filename, ".h") || strstr(filename, ".pns") || strstr(filename, ".bmp") ) { 
				bFound = -1;
				bFile = -1;
				printf("File [%s%s]\n", FilePath, filename);
			} else {
				/* sub-folder if the name is end with '\' */
				int nlen = strlen(filename);
				char ch = filename[nlen-1];
				if ((ch == '\\') || (ch == '/')) {
					bFound = -1;
					bFile = 0;
					printf("Folder [%s%s]\n", FilePath, filename);
				}
			}

			if (bFound) {
				uint32_t hFile;
				wchar_t wcFilename[MAX_LEN];
				BSTR bstrFilename;

				mbstowcs(wcFilename, filename, MAX_LEN);
				bstrFilename = SysAllocString(wcFilename);
				bstrOpt = SysAllocString(L"");
				if (bRoot) {
					hr = bCap_ControllerGetFile(fd, hParent, bstrFilename, bstrOpt, &hFile);
				} else {
					hr = bCap_FileGetFile(fd, hParent, bstrFilename, bstrOpt, &hFile);
				}
				SysFreeString(bstrFilename);
				SysFreeString(bstrOpt);
				
				if (bFile) {
					hr = get_file(fd, hFile, FilePath, filename);
				} else { /* sub-folder */
					char FullPath[MAX_LEN];
					strcpy(FullPath, FilePath);
					strcat(FullPath, filename);

					hr = find_download_files(fd, hFile, 0, FullPath);
				}

				bCap_FileRelease(fd, &hFile);
			}
		}
		SafeArrayUnaccessData(vResult.parray);
	}

	return hr;
}

HRESULT get_file(int fd, uint32_t hFile, char *FilePath, char *FileName)
{
	HRESULT hr;
	VARIANT vValue;

	/* get all context and put it all into a local file.*/
	VariantInit(&vValue);

	hr = bCap_FileGetValue(fd, hFile, &vValue);
	if(SUCCEEDED(hr)){
		FILE *fdFile;
		char filename[MAX_LEN];

		recursive_makedir(FilePath);

		sprintf(filename, "%s%s", FilePath, FileName);
		if ((fdFile = fopen(filename, "wb")) != NULL) {
			if (vValue.vt == VT_BSTR) {
				// Text UTF-16
				fprintf(fdFile, "%S", vValue.bstrVal);// %S : UTF-16 to MBS conversion			
			} else if (vValue.vt == (VT_UI1 | VT_ARRAY)) {
				// Binary
				//vValue.vt == (VT_UI1 | VT_ARRAY)
				int32_t lbnd, ubnd, cnt;
				void *pbuf;

				SafeArrayGetLBound(vValue.parray, 1, &lbnd);
				SafeArrayGetUBound(vValue.parray, 1, &ubnd);
				cnt = ubnd - lbnd + 1;

				SafeArrayAccessData(vValue.parray, (void **)&pbuf);
				fwrite(pbuf, cnt, 1, fdFile);			
				SafeArrayUnaccessData(vValue.parray);
			}

			fclose(fdFile);
			VariantClear(&vValue);
		} else {
				printf("Can't create [%s%s] file context !\n",  FilePath, FileName);
				return E_FAIL;
		}
	} else {
		printf("Can't get [%s] file context !\n", FileName);
	}

	return hr;
}

int is_path_exist(char *fn)
{
    struct stat stbuf;
    extern int errno;

    if (stat(fn, &stbuf)) {
		if (errno == ENOENT) {
			return 0;
		} else {
            return 0;
        }
    } else {
		if (stbuf.st_mode & S_IFDIR) {
			return 2;
		} else {
			return 1;
		}
    }
}

void recursive_makedir(char *path)
{
    char *end1, *end2;

    if (path[0] == '\\') end1 = path + 1;
    else if (path[1] == ':' && path[2] == '\\') end1 = path + 3;
    else end1 = path;

    for(;;) {
        end2 = strchr(end1, '\\');
        if (end2 == NULL) {
            if (*end1 == 0) break;
        } else *end2 = 0;
        if (is_path_exist(path) <= 0) mkdir(path);
        if (end2 == NULL) break;
        else {
            *end2 = '\\';
            end1 = end2 + 1;
        }
    }
}

HRESULT upload_files(char *FilePath)
{
	int fd;
	uint32_t hCtrl;
	BSTR bstr1, bstr2, bstr3, bstr4;
	HRESULT hr;

	/* Open connection. specify the IP address of bCapServer */
	printf("Open connection to tcp:%s\n", TARGET_RC8_IP);
	hr = bCap_Open_Client("tcp:" TARGET_RC8_IP, 1000, 3, &fd);

	if(SUCCEEDED(hr)){
		/* Send SERVICE_START packet */
		bCap_ServiceStart(fd, NULL);

		bstr1 = SysAllocString(L"");                    /* Name */
		bstr2 = SysAllocString(L"CaoProv.DENSO.VRC");   /* Provider */
		bstr3 = SysAllocString(L"localhost");			/* Machine */
		bstr4 = SysAllocString(L"");					/* Option */

		/* Connect RC8 */
		hr = bCap_ControllerConnect(fd, bstr1, bstr2, bstr3, bstr4, &hCtrl);

		SysFreeString(bstr1);
		SysFreeString(bstr2);
		SysFreeString(bstr3);
		SysFreeString(bstr4);

		if(SUCCEEDED(hr)){

			hr = find_upload_files( fd, hCtrl, -1, FilePath );

			/* Disconnect RC8 */
			bCap_ControllerDisconnect(fd, &hCtrl);
		}

		/* Send SERVICE_STOP packet */
		bCap_ServiceStop(fd);

		/* Close connection */
		bCap_Close_Client(&fd);
	}

	return hr;
}

HRESULT create_file(int fd, uint32_t hParent, int bRoot, char *FileName, uint32_t *phFile)
{
	HRESULT hr;
	wchar_t wcFilename[MAX_LEN];
	BSTR bstrFilename;
	BSTR bstrOpt;

	mbstowcs(wcFilename, FileName, MAX_LEN);
	bstrFilename = SysAllocString(wcFilename);
	bstrOpt = SysAllocString(L"@Create=1");
	if (bRoot) {
		hr = bCap_ControllerGetFile(fd, hParent, bstrFilename, bstrOpt, phFile);
	} else {
		hr = bCap_FileGetFile(fd, hParent, bstrFilename, bstrOpt, phFile);
	}
	SysFreeString(bstrFilename);
	SysFreeString(bstrOpt);

	if(FAILED(hr)){
		printf("Can't create [%s] file !\n", FileName);
	} 

	return hr;
}

HRESULT find_upload_files(int fd, uint32_t hParent, int bRoot, char *FilePath) 
{
	HRESULT hr;
	WIN32_FIND_DATAA ffd;
	HANDLE hHandle;
	char filepath[MAX_LEN];

	sprintf(filepath, "%s*.*", FilePath);

	hr = S_OK;
	hHandle = FindFirstFileA(filepath, &ffd);
	if ( hHandle != INVALID_HANDLE_VALUE ) {
		do {
			// Ignore .. and .
			if ( strcmp( ffd.cFileName, ".." ) != 0 && strcmp( ffd.cFileName, "." ) != 0 ) {
				// Ignore System File
				if ( ( ffd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN ) != FILE_ATTRIBUTE_HIDDEN
					&& ( ffd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM ) != FILE_ATTRIBUTE_SYSTEM
					&& ( ffd.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY ) != FILE_ATTRIBUTE_TEMPORARY) {
						// if the file is a directory
					if ( (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) == FILE_ATTRIBUTE_DIRECTORY ) {
						char curpath[MAX_LEN];
						char filename[MAX_LEN];
						uint32_t hFile;
						sprintf(filename, "%s\\", ffd.cFileName);
						sprintf(curpath, "%s%s", FilePath, filename);

						hr = create_file(fd, hParent, bRoot, filename, &hFile);
						if(SUCCEEDED(hr)) {
							hr = find_upload_files(fd, hFile, 0, curpath);
						}
						bCap_FileRelease(fd, &hFile);

					} else {

						int ilen, i, found;
						char filename[MAX_LEN];

						strcpy(filename, ffd.cFileName);
						/* change the file extension string to lowower charactor. */
						ilen = strlen(filename);
						found = 0;
						for(i=0; i<ilen; i++) {
							if (found) {
								filename[i] = tolower(filename[i]);
							} else if (filename[i] == '.') {
								found = -1;
							}
						}

						/* find '*.pcs' or '*.h' or '*.pns' or '*.bmp' */
						if ( strstr(filename, ".pcs") || strstr(filename, ".h") || strstr(filename, ".pns") || strstr(filename, ".bmp") ) {
							uint32_t hFile;

							hr = create_file(fd, hParent, bRoot, filename, &hFile);
							if(SUCCEEDED(hr)) {
								hr = put_file(fd, hFile, FilePath, filename);
							}
							bCap_FileRelease(fd, &hFile);
						}
					}
					if (FAILED(hr)) break;
				}
			}
		} while (FindNextFileA(hHandle, &ffd));
		FindClose(hHandle);
	}

	return hr;
}


HRESULT put_file(int fd, uint32_t hFile, char *FilePath, char *FileName)
{
	HRESULT hr;
	FILE *fdFile;
	char filename[MAX_LEN];
	VARIANT vValue;

	/* put all context and put it all into a local file.*/

	VariantInit(&vValue);

	hr = E_FAIL;
	sprintf(filename, "%s%s", FilePath, FileName);
	if ((fdFile = fopen(filename, "rb")) != NULL) {
		fpos_t fsize;
		
		hr = S_OK;

		fseek(fdFile, 0, SEEK_END);
		fgetpos(fdFile, &fsize);
		fseek(fdFile, 0, SEEK_SET);

		/* find '*.pcs' or '*.h' or '*.pns'*/
		if ( strstr(FileName, ".pcs") || strstr(FileName, ".h") || strstr(FileName, ".pns") ) {
			char *sbuf;
			wchar_t *wcContext;
			size_t requiredSize;

			sbuf = malloc(fsize+1); // +NULL
			fread(sbuf, fsize, 1, fdFile);
			sbuf[fsize] = NULL;

			requiredSize = mbstowcs(NULL, sbuf, 0);
			/* Add one to leave room for the NULL terminator */
			wcContext = (wchar_t *)malloc( (requiredSize + 1) * sizeof( wchar_t ));

			mbstowcs(wcContext, sbuf, requiredSize + 1);
			free(sbuf);

			// Text UTF-16
			vValue.vt = VT_BSTR;
			vValue.bstrVal = SysAllocString(wcContext); // WCS to UTF-16 conversion			
			free(wcContext);

		} else {
			// Binary
			/* *.bmp */
			void *pbuf;

			vValue.vt = (VT_UI1 | VT_ARRAY);
			vValue.parray = SafeArrayCreateVector(VT_UI1, 0, fsize);
			hr = SafeArrayAccessData(vValue.parray, (void **)&pbuf);
			if (SUCCEEDED(hr)) {
				fread(pbuf, fsize, 1, fdFile);
				SafeArrayUnaccessData(vValue.parray);
			}
		}

		fclose(fdFile);

		if (SUCCEEDED(hr)) {
			hr = bCap_FilePutValue(fd, hFile, vValue);
			if(FAILED(hr)){
				printf("Can't put [%s] file context !\n", FileName);
			} 
		}

		VariantClear(&vValue);
	}

	return hr;
}
