// DemoStartVM.cpp 

#include <iostream>
#include <string>

using namespace std;

#include <COMUtils.h>
#include "VirtualBox.h"

SmartCOMPtr<IMachine> FindMachine(SmartCOMPtr<IVirtualBox> vb, WCHAR* nomMachine)
{
	HRESULT hr;
	SmartCOMPtr<IMachine> machine;

	BSTR nom =::SysAllocString(nomMachine);
	if(nom == 0)
		throw COMException(E_OUTOFMEMORY);

	hr = vb->FindMachine(nom, &machine);
	::SysFreeString(nom);
	COMHelper::TestOk(hr);

	return machine;
}

wstring GetMachineGUID(SmartCOMPtr<IMachine> machine)
{
	HRESULT hr;
	wstring result;
	BSTR machineGUID;
	
	hr = machine->get_Id(&machineGUID);
	COMHelper::TestOk(hr);
	result = machineGUID;
	::SysFreeString(machineGUID);
	return result;
}

void DoSettings(SmartCOMPtr<IVirtualBox> vb, wstring machineGUID)
{
	HRESULT hr;
	SmartCOMPtr<ISession> session;
	SmartCOMPtr<IMachine> machine;

	// instancier un objet COM Session 
	hr = ::CoCreateInstance(CLSID_Session,       
		NULL,
		CLSCTX_INPROC_SERVER,
		IID_ISession,
		(void**)&session);
	COMHelper::TestOk(hr);

	BSTR id = ::SysAllocString(machineGUID.c_str());
	if(id == 0)
		throw COMException(E_OUTOFMEMORY);
	
	hr = vb->OpenSession(&(*session), id);
	::SysFreeString(id);
	COMHelper::TestOk(hr);

	// Bidouiller les réglages de la VM
	// D'abord obtenir son interface
	hr = session->get_Machine(&machine);
	COMHelper::TestOk(hr);
	// Biddouiller :-)
	hr = machine->put_MemorySize(1024);
	COMHelper::TestOk(hr);
	// Sauvegarder les réglages
	hr = machine->SaveSettings();
	COMHelper::TestOk(hr);
	// Fermer la session
	hr = session->Close();
	COMHelper::TestOk(hr);
}

void StartVM(SmartCOMPtr<IVirtualBox> vb,
			 wstring machineGUID,
			 SmartCOMPtr<IMachine> machine)
{
	HRESULT hr;
	SmartCOMPtr<ISession> session;
	SmartCOMPtr<IConsole> console;
	SmartCOMPtr<IProgress> progress;

	// instancier un objet COM Session 
	hr = ::CoCreateInstance(CLSID_Session,       
		NULL,
		CLSCTX_INPROC_SERVER,
		IID_ISession,
		(void**)&session);
	COMHelper::TestOk(hr);

	BSTR id = ::SysAllocString(machineGUID.c_str());
	if(id == 0)
		throw COMException(E_OUTOFMEMORY);
	BSTR typesession = ::SysAllocString(L"gui");
	if(typesession == 0)
	{
		::SysFreeString(id);
		throw COMException(E_OUTOFMEMORY);
	}
	// Ouvrir une RemoteSession = Lancer une VM
	hr = vb->OpenRemoteSession(&(*session),
		id,
		typesession,
		NULL,
		&progress);

	::SysFreeString(id);
	::SysFreeString(typesession);
	COMHelper::TestOk(hr);

	cout << "C'est parti mon kiki" << endl;
    hr = progress->WaitForCompletion(-1);
	COMHelper::TestOk(hr);
	cout << "Machine virtuelle lancee" << endl;
	// La machine virtuelle ayant démarré, on
	// la montre fièrement au premier plan
	machine->ShowConsoleWindow(0);
	// Obtenir la console de contrôle
	// sur la machine virtuelle
	hr = session->get_Console(&console);
	COMHelper::TestOk(hr);

	// Attendre une action de l'administrateur
	// pour arrêter la machine
	cout << "Appuyer sur [RETURN] pour arreter la machine virtuelle" << endl;
	cin.get();
	// Commander l'arret de la machine virtuelle
	// Mais en toute rigueur, il faudrait
	// d'abord tester la propriété State
	// de l'objet machine
    hr = console->PowerDown(&progress);
	COMHelper::TestOk(hr);
	// Ne pas descendre avant l'arrêt
	// complet du train
    cout << "Ne descendez pas en marche" << endl;
	cout << "Attendez l'arret complet de la machine" << endl;
	// Attendre...
	hr = progress->WaitForCompletion(-1);
	COMHelper::TestOk(hr);
	cout << "Machine virtuelle stoppee :-)" << endl;
	// Fermer la session, c'est terminé
    hr = session->Close();
	COMHelper::TestOk(hr);
}


int main(int argc, char* argv[])
{
	try
	{
		COMInitializer ci;
		wstring machineGUID;
		HRESULT hr;
		WCHAR* nomMachine = L"v_ubuntu";
		SmartCOMPtr<IVirtualBox> virtualBox;
		SmartCOMPtr<IMachine> machine;


		// Crer une instance COM VirtualBox
		hr = ::CoCreateInstance(CLSID_VirtualBox,
                          NULL,
                          CLSCTX_LOCAL_SERVER,
                          IID_IVirtualBox,
                          (void **)&virtualBox);
		COMHelper::TestOk(hr);

		machine = FindMachine(virtualBox, nomMachine);
		machineGUID = GetMachineGUID(machine);
		DoSettings(virtualBox, machineGUID);
		StartVM(virtualBox, machineGUID, machine);
	}
	catch(COMException ce)
	{
		std::cout << ce.raison() << std::endl;
		return EXIT_FAILURE;
	}

	return EXIT_SUCCESS;
}
