// echofileDlg.cpp : implementation file
//

#include "stdafx.h"
#include "echofile.h"
#include "echofileDlg.h"
#include "comdef.h"
#include "..\wmsoapstruct\wmsoapstruct.h"
#include "..\wmsoapstruct\wmsoapstruct_i.c"
#include "..\soap_source\wmdimeclient.h"
#include "..\soap_source\bytearrayconv.h"
#include <fstream>
#include <string>
using namespace std;

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// Dialog Data
	//{{AFX_DATA(CAboutDlg)
	enum { IDD = IDD_ABOUTBOX };
	//}}AFX_DATA

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CAboutDlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:
	//{{AFX_MSG(CAboutDlg)
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
	//{{AFX_DATA_INIT(CAboutDlg)
	//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CAboutDlg)
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
	//{{AFX_MSG_MAP(CAboutDlg)
		// No message handlers
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CEchofileDlg dialog

CEchofileDlg::CEchofileDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CEchofileDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CEchofileDlg)
	m_strWSDLURL = _T("http://localhost/wsdl/echofile.wsdl");
	m_strFilePath = _T("");
	m_nTransferMode = 0;
	//}}AFX_DATA_INIT
	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CEchofileDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CEchofileDlg)
	DDX_Text(pDX, IDC_EDIT_WSDL_URL, m_strWSDLURL);
	DDX_Text(pDX, IDC_EDIT_FILE_PATH, m_strFilePath);
	DDX_Radio(pDX, IDC_RADIO_MODE1, m_nTransferMode);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CEchofileDlg, CDialog)
	//{{AFX_MSG_MAP(CEchofileDlg)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BUTTON_PATH_BROWSE, OnButtonPathBrowse)
	ON_BN_CLICKED(IDOK, OnOk)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CEchofileDlg message handlers

BOOL CEchofileDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Add "About..." menu item to system menu.

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	
	// TODO: Add extra initialization here
	
	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CEchofileDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CEchofileDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CEchofileDlg::OnQueryDragIcon()
{
	return (HCURSOR) m_hIcon;
}

void CEchofileDlg::OnButtonPathBrowse() 
{
	// these are used for decomposing/recomposing file pathnames
	char szDrive[3];
	char szDir[MAX_PATH];
	char szFilename[MAX_PATH];
	char szExt[MAX_PATH];

	char szPath[MAX_PATH];	// recomposed directory path

	UpdateData(TRUE);
	// create basic file open dialog
	CFileDialog dlg(TRUE,
					"*",
					m_strFilePath,
					OFN_HIDEREADONLY,
					"All Files|*.*||",
					NULL);
	// set the title
	dlg.m_ofn.lpstrTitle = _T("Select file to send in echoDataFile request");
	// set the initial directory
	_splitpath(m_strFilePath, szDrive, szDir, szFilename, szExt);
	_makepath(szPath, szDrive, szDir, NULL, NULL);
	dlg.m_ofn.lpstrInitialDir = szPath;
	// set the initial filename
	char szFile[MAX_PATH] ;
	_makepath(szFile, NULL, NULL, szFilename, szExt);
	dlg.m_ofn.lpstrFile = szFile;
	if (dlg.DoModal() == IDOK)
	{
		m_strFilePath = dlg.GetPathName();
	}
	UpdateData(FALSE);
}

CString PromptSaveFileAs(const CString& strFilename)
{
	CString strPath;
	// these are used for decomposing/recomposing file pathnames
	char szDrive[3];
	char szDir[MAX_PATH];
	char szFilename[MAX_PATH];
	char szExt[MAX_PATH];

	char szPath[MAX_PATH];	// recomposed directory path

	// create basic file open dialog
	CFileDialog dlg(FALSE,
					"*",
					strFilename,
					OFN_OVERWRITEPROMPT,
					"All Files|*.*||",
					NULL);
	// set the title
	dlg.m_ofn.lpstrTitle = _T("Save echoed file as...");
	// set the initial directory
	_splitpath(strFilename, szDrive, szDir, szFilename, szExt);
	_makepath(szPath, szDrive, szDir, NULL, NULL);
	dlg.m_ofn.lpstrInitialDir = szPath;
	// set the initial filename
	char szFile[MAX_PATH] ;
	_makepath(szFile, NULL, NULL, szFilename, szExt);
	char szPrefixedFile[MAX_PATH];
	sprintf(szPrefixedFile, "echoed_%s", szFile);
	dlg.m_ofn.lpstrFile = szPrefixedFile;
	if (dlg.DoModal() == IDOK)
	{
		strPath = dlg.GetPathName();
	}
	else
	{
		strPath = "";
	}
	return strPath;
}

IStorage *CreatePayload(const CString& strFilename)
{
	IStorage *pIStorage;
	HRESULT hRes = StgCreateDocfile(NULL,
									STGM_DELETEONRELEASE | STGM_DIRECT | STGM_READWRITE| STGM_CREATE | STGM_SHARE_EXCLUSIVE,
									0,
									&pIStorage);
	if (FAILED(hRes))
	{
		return NULL;
	}
	IStream *pIStream;
	hRes = pIStorage->CreateStream( L"",
									STGM_CREATE | STGM_WRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE,
									0,
									NULL,
									&pIStream);
	if (FAILED(hRes))
	{
		pIStorage->Release();
		return NULL;
	}
	// open the file
    FILE* pFile = fopen((LPCSTR)strFilename, "rb");
    if(pFile == NULL)
	{
        return NULL;
	}
	// copy contents to stream
	const unsigned long ulSize = 4096;
	char szBuffer[ulSize];
	unsigned long ulBytesWritten = 0;
	unsigned long ulCount;
	do
	{
		ulCount = fread(szBuffer, sizeof(char), ulSize, pFile);
		hRes = pIStream->Write(szBuffer, ulCount, &ulBytesWritten);
		if (FAILED(hRes) || ulCount != ulBytesWritten)
		{
		    fclose(pFile);
			pIStream->Release();
			pIStorage->Release();
			return NULL;
		}
	} while (ulCount == ulSize);
    // close the file
    fclose(pFile);
	// done with stream
	pIStream->Release();
	return pIStorage;
}

SAFEARRAY *LoadFileToByteArray(const CString& strFilename)
{
	// open the WDSL file
    FILE* pFile = fopen((LPCSTR)strFilename, "rb");
    if(pFile == NULL)
	{
        return false;
	}
    // Compute the size of the file
    fseek(pFile, 0, SEEK_END);
    int nSize = ftell(pFile);
    // reset pointer to the beginning
    fseek(pFile, 0, SEEK_SET);

    char* pszBuffer = (char *)malloc(nSize);
    fread(pszBuffer, sizeof(char), nSize, pFile);
    // Close the file
    fclose(pFile);
	return ByteArrayFromBuffer(pszBuffer, nSize);
}

void CEchofileDlg::OnOk() 
{
	CWaitCursor wait;
	UpdateData(TRUE);
	string strServiceName("EchoFileDIMESvc");
	string strPortName("EchoFileDIMERpcPort");
	string strActor("");
	string strUserAgent("White Mesa echoDataFile Client/1.0");
	list<wstring> listHeaderProcProgIDs;
	bool bUseExplicitTyping = false;

	WMDIMEClient client;
	
	if (client.Initialize(  string((LPCSTR)m_strWSDLURL),
							strServiceName,
							strPortName,
							strActor,
							strUserAgent,
							listHeaderProcProgIDs,
							bUseExplicitTyping,
                            false) == false)
	{
		AfxMessageBox("Failed to initialize client!");
		return;
	}
	// method
	wstring wstrMethodName = L"echoDataFile";

	// set up call
	_variant_t Args[1];
	wstring Names[1];
	_variant_t varReturn;
	

	_variant_t varTemp;
	VARIANT_BOOL bResult;
	BSTR bstrMember1 = SysAllocString(L"fileName");
	BSTR bstrMember2 = SysAllocString(L"fileData");

	// instantiate struct object
	Iwmsoapstruct *pIwmsoapstructReq;
	HRESULT res = CoCreateInstance( CLSID_wmsoapstruct,
									NULL, CLSCTX_SERVER,
									IID_Iwmsoapstruct,
									(void **) &pIwmsoapstructReq);
	if (FAILED(res))
	{
		AfxMessageBox("Failed to instantiate struct object!");
		return;
	}
	// filename
	varTemp = (LPCSTR)m_strFilePath;
	pIwmsoapstructReq->SetMember(bstrMember1, varTemp, &bResult);
	// data
	varTemp.Clear();
	if (m_nTransferMode == 0)
	{
		IStorage *pIStorageReq = CreatePayload(m_strFilePath);
		if (pIStorageReq == NULL)
		{
			AfxMessageBox("Failed to create request message payload object!");
			return;
		}
		varTemp.punkVal = pIStorageReq;
		varTemp.vt = VT_UNKNOWN;
	}
	else
	{
		SAFEARRAY *pArray = LoadFileToByteArray(m_strFilePath);
		if (pArray == NULL)
		{
			AfxMessageBox("Failed to create request message byte array!");
			return;
		}
		varTemp.vt = VT_ARRAY | VT_UI1;
		varTemp.parray = pArray;
	}
	pIwmsoapstructReq->SetMember(bstrMember2, varTemp, &bResult);
	// now get IDispatch pointer
	IDispatch *pIDispatchReq;
	res = pIwmsoapstructReq->QueryInterface(IID_IDispatch, (void **)&pIDispatchReq);
	if (FAILED(res))
	{
		AfxMessageBox("Failed to instantiate struct object!");
		SysFreeString(bstrMember1);
		SysFreeString(bstrMember2);
		pIwmsoapstructReq->Release();
		return;
	}
	// release
	pIwmsoapstructReq->Release();

	Args[0].vt = VT_DISPATCH;
	Args[0].pdispVal = pIDispatchReq;
	Names[0] = L"inputDataFile";
	int nNumArgs = 1;

	// attempt method invocation
	if (client.InvokeSOAPMethod(wstrMethodName,
								Names,
								Args,
								nNumArgs,
								varReturn) == false)
	{
		SysFreeString(bstrMember1);
		SysFreeString(bstrMember2);
		AfxMessageBox("Invocation failed!");
		return;
	}
	if (client.IsSOAPFault() == true)
	{
		SysFreeString(bstrMember1);
		SysFreeString(bstrMember2);
		AfxMessageBox("Fault Message returned!");
		return;
	}
	// get pointer to struct object
	IDispatch *pIDispatchResp = varReturn.pdispVal;
	Iwmsoapstruct *pIwmsoapstructResp;
	res = pIDispatchResp->QueryInterface(IID_Iwmsoapstruct, (void **)&pIwmsoapstructResp);
	if (FAILED(res))
	{
		SysFreeString(bstrMember1);
		SysFreeString(bstrMember2);
		AfxMessageBox("QueryInterface() on return value failed!");
		return;
	}
	// now get response struct members
	pIwmsoapstructResp->GetMember(bstrMember1, &varTemp, &bResult);
	if (bResult == VARIANT_FALSE)
	{
		SysFreeString(bstrMember1);
		SysFreeString(bstrMember2);
		AfxMessageBox("Failed to find filename from response!");
		return;
	}
	CString strFilenameEchoed(varTemp.bstrVal);
	varTemp.Clear();
	pIwmsoapstructResp->GetMember(bstrMember2, &varTemp, &bResult);
	if (bResult == VARIANT_FALSE)
	{
		SysFreeString(bstrMember1);
		SysFreeString(bstrMember2);
		AfxMessageBox("Failed to find echoed file in response!");
		return;
	}
	// release interface ptr
	pIwmsoapstructResp->Release();
	SysFreeString(bstrMember1);
	SysFreeString(bstrMember2);
	if (m_nTransferMode == 0)
	{
		// DIME attachment expected
		IUnknown *pIUnknownResp = varTemp.punkVal;
		IStorage *pIStorageResp;
		HRESULT hRes = pIUnknownResp->QueryInterface(IID_IStorage, (void **)&pIStorageResp);
		if (FAILED(hRes))
		{
			AfxMessageBox("QueryInterface() call on DIME attachment pointer failed!");
			return;
		}
		DIMEPayload *pPayloadResp = new DIMEPayload(pIStorageResp,
													string(""),
													string(""),
													DIME_TYPENAME_FORMAT_UNKNOWN);
		pIStorageResp->Release();
		// present save-as dialog box
		CString strSaveFilePath = PromptSaveFileAs(strFilenameEchoed);
		if (strSaveFilePath == "")
		{
			return;
		}
		pPayloadResp->OpenForRead();
		char szTemp[4096];
		unsigned long ulNumBytes = 0;
		ofstream Log;
		Log.open((LPCSTR)strSaveFilePath, ios_base::out | ios_base::binary);
		do
		{
			pPayloadResp->Read(szTemp, 4096, &ulNumBytes);
			Log.write(szTemp, ulNumBytes);
		} while (ulNumBytes == 4096);
		pPayloadResp->Close();
		delete pPayloadResp;
		Log.close();
	}
	else
	{
		// base64 binary expected
		// present save-as dialog box
		CString strSaveFilePath = PromptSaveFileAs(strFilenameEchoed);
		if (strSaveFilePath == "")
		{
			return;
		}
		// extract info from SAFEARRAY
		SAFEARRAY *pArray = varTemp.parray;
		void *pData = NULL;
		SafeArrayAccessData(pArray, &pData);
		ofstream Log;
		Log.open((LPCSTR)strSaveFilePath, ios_base::out | ios_base::binary);
		Log.write((const char *)pData, pArray->rgsabound[0].cElements);
		SafeArrayUnaccessData(pArray);
		Log.close();
	}
}

