/*=====================================================================
  File:     MEDriver.cpp

  Summary:  The Managed Extension Driver sample shows how to use COM events in .NET world.
---------------------------------------------------------------------
  This file is part of the Microsoft VC++ Code Samples.

  Copyright (c) Microsoft Corporation.  All rights reserved.

This source code is intended only as a supplement to Microsoft
Development Tools and/or on-line documentation.  See these other
materials for detailed information regarding Microsoft code samples.

THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
=====================================================================*/

// This is the main project file for VC++ application project 
// generated using an Application Wizard.

#include "stdafx.h"

#using <mscorlib.dll>
#using "CONNECTLib.dll" // generated by tlbimp from the output bits of the connect sample found in the ATL/COM sample section

#using <system.dll>
#using <system.windows.forms.dll>
#using <system.drawing.dll>
using namespace System;
using namespace System::Drawing;
using namespace System::Collections;
using namespace System::Threading;
using namespace System::Windows::Forms;
using namespace CONNECTLib;

#define null 0L

public
ref class HashElem
{
public:
	HashElem() {nPos = 0;nDir=1;}
	Int32 nPos;
	Int32 nDir;
};

public
ref class MEDriverForm : public Form
{
private:
	Button^		btnOK;
	Button^		btnStart;
	Button^		btnStop;
	Button^		btnStopAll;
	Button^		btnAdvise;
	Button^		btnUnAdvise;

	CoRandomClass^	m_comObj;
public:
	PictureBox^	pictureBox1;
	static const Int32		nMaxThreads = 10;
	Int32		nThreads, m_nAdvisedObjs;
	array<Int32>^	m_arrID;

	MEDriverForm()
	{
		InitializeComponent();

		nThreads = 1;
		m_nAdvisedObjs = 0;
		m_arrID  = gcnew array<Int32>(nMaxThreads);
		m_comObj = gcnew CoRandomClass();
	}

	ref class CMEDriver
	{
	public:
		CMEDriver(MEDriverForm^ pDriverForm) : m_cs(gcnew Mutex), m_mapPos(gcnew Hashtable), m_pDr(pDriverForm) {}
		void Fire(int l);
	private:
		Mutex^		m_cs;
		Hashtable^	m_mapPos;
		MEDriverForm^ m_pDr;

	};

private:
	void InitializeComponent()
	{
		btnAdvise = gcnew Button();
		btnOK = gcnew Button();
		btnStop = gcnew Button();
		btnStopAll = gcnew Button();
		btnUnAdvise = gcnew Button();
		pictureBox1 = gcnew PictureBox();
		btnStart = gcnew Button();
		btnAdvise->Location = Point(400, 184);
		btnAdvise->Size = System::Drawing::Size(88, 24);
		btnAdvise->TabIndex = 1;
		btnAdvise->Text = "&Advise";
		btnAdvise->Click += gcnew EventHandler(this, &MEDriverForm::OnAdvise);
		btnOK->Location = Point(400, 8);
		btnOK->Size = System::Drawing::Size(88, 24);
		btnOK->TabIndex = 1;
		btnOK->Text = "OK";
		btnOK->Click += gcnew EventHandler(this, &MEDriverForm::OnClose);
		btnStop->Location = Point(400, 104);
		btnStop->Size = System::Drawing::Size(88, 24);
		btnStop->TabIndex = 1;
		btnStop->Text = "S&top";
		btnStop->Click += gcnew EventHandler(this, &MEDriverForm::OnStop);
		btnStopAll->Location = Point(400, 136);
		btnStopAll->Size = System::Drawing::Size(88, 24);
		btnStopAll->TabIndex = 1;
		btnStopAll->Text = "Stop A&ll";
		btnStopAll->Click += gcnew EventHandler(this, &MEDriverForm::OnStopAll);
		btnUnAdvise->Location = Point(400, 216);
		btnUnAdvise->Size = System::Drawing::Size(88, 24);
		btnUnAdvise->TabIndex = 1;
		btnUnAdvise->Text = "&UnAdvise";
		btnUnAdvise->Click += gcnew EventHandler(this, &MEDriverForm::OnUnAdvise);
		pictureBox1->Location = Point(32, 16);
		pictureBox1->Size = System::Drawing::Size(352, 224);
		pictureBox1->TabIndex = 0;
		pictureBox1->TabStop = false;
		btnStart->Location = Point(400, 72);
		btnStart->Size = System::Drawing::Size(88, 24);
		btnStart->TabIndex = 1;
		btnStart->Text = "&Start";
		btnStart->Click += gcnew EventHandler(this, &MEDriverForm::OnStart);
		AutoScaleBaseSize = System::Drawing::Size(5, 13);
		ClientSize = System::Drawing::Size(507, 256);
		array<Control ^>^ temp = gcnew array<Control ^>(7);
		temp[0] = pictureBox1;
		temp[1] = btnUnAdvise;
		temp[2] = btnAdvise;
		temp[3] = btnStopAll;
		temp[4] = btnStop;
		temp[5] = btnStart;
		temp[6] = btnOK;

		Controls->AddRange(temp);
		Text = "COM Events sample:";
	}

	void OnClose(Object^, EventArgs^)
	{
		m_comObj->StopAll();
		while(0 < m_nAdvisedObjs)
			OnUnAdvise(nullptr, nullptr);

		Close();
	}

	void OnStart(Object^, EventArgs^)
	{
		try
		{
			if(nThreads < nMaxThreads)
			{
				m_comObj->Start(m_arrID[nThreads - 1]);
				++nThreads;
			}
		}
		catch(Exception^ ex)
		{
			MessageBox::Show("Error starting a new thread:", ex->ToString());
		}
	}

	void OnStop(Object^, EventArgs^)
	{
		if(nThreads > 0)
		{
			m_comObj->Stop(m_arrID[nThreads - 1]);
			--nThreads;
		}
	}

	void OnStopAll(Object^, EventArgs^)
	{
		m_comObj->StopAll();
	}

	void OnAdvise(Object^, EventArgs^)
	{
		m_comObj->Fire += gcnew IRandomEvent_FireEventHandler(gcnew CMEDriver(this), &MEDriverForm::CMEDriver::Fire);
		++m_nAdvisedObjs;
	}

	void OnUnAdvise(Object^, EventArgs^)
	{
		m_comObj->Fire -= gcnew IRandomEvent_FireEventHandler(gcnew CMEDriver(this), &MEDriverForm::CMEDriver::Fire);
		--m_nAdvisedObjs;
	}
};

void MEDriverForm::CMEDriver::Fire(Int32 l)
{
	m_cs->WaitOne();

	Graphics^ gr = m_pDr->pictureBox1->CreateGraphics();
	Rectangle rcl = m_pDr->pictureBox1->ClientRectangle;


	Pen^ p = gcnew Pen( Color::Black );
	gr->DrawLine( p, rcl.Left + 1, rcl.Bottom - 1, rcl.Right - 1, rcl.Bottom - 1 );
	gr->DrawLine( p, rcl.Right - 1, rcl.Bottom - 1, rcl.Right - 1, rcl.Top + 1 );
	gr->DrawLine( p, rcl.Left + 1, rcl.Bottom - 1, rcl.Left + 1, rcl.Top + 1 );
	gr->DrawLine( p, rcl.Left + 1, rcl.Top + 1, rcl.Right - 1, rcl.Top + 1 );

	HashElem^ pos;
	if( (pos = dynamic_cast<HashElem^>(m_mapPos[l])) == nullptr )
	{
		m_mapPos[l] = gcnew HashElem();
		pos = dynamic_cast<HashElem^>(m_mapPos[l]);
	}
	Color cr = Color::Black;
	switch (l)
	{
		case 0:
			cr = Color::FromArgb(255,0,0);
			break;
		case 1:
			cr = Color::FromArgb(0,255,0);
			break;
		case 2:
			cr = Color::FromArgb(0,0,255);
			break;
		case 3:
			cr = Color::FromArgb(255,255,0);
			break;
		case 4:
			cr = Color::FromArgb(255,0,255);
			break;
		case 5:
			cr = Color::FromArgb(0,255,255);
			break;
		case 6:
			cr = Color::FromArgb(64,64,64);
			break;
		case 7:
			cr = Color::FromArgb(128,128,128);
			break;
		case 8:
			cr = Color::FromArgb(192,192,192);
			break;
		case 9:
			cr = Color::FromArgb(0,0,0);
			break;
	}

	int nH = rcl.Height/m_pDr->nThreads;

//	if(rcl.Height - nH*l+pos->nPos);

	if(1 == pos->nDir)
		gr->DrawLine( gcnew Pen(cr), m_pDr->m_nAdvisedObjs - 1, nH*l+pos->nPos, m_pDr->m_nAdvisedObjs, nH*l+pos->nPos);
	else
		gr->DrawLine( gcnew Pen(Color::FromArgb(0,0,0)), m_pDr->m_nAdvisedObjs - 1, nH*l+pos->nPos, m_pDr->m_nAdvisedObjs, nH*l+pos->nPos);

	pos->nPos += pos->nDir;
	if (pos->nPos >= nH)
	{
		pos->nDir = -1;
		pos->nPos = nH-1;
	}
	if (pos->nPos <= -1)
	{
		pos->nDir = 1;
		pos->nPos = 0;
	}
	m_mapPos[l] = pos;
	m_cs->ReleaseMutex();
}

// This is the entry point for this application
void main(void)
{
	Thread::CurrentThread->ApartmentState = System::Threading::ApartmentState::STA;
	Application::Run(gcnew MEDriverForm());
}
