//------------------------------------------------------------------------------
//  TOPPERS/ASP Windows Debug Environment
//  Copyright (C) 2010 長島 宏明
//------------------------------------------------------------------------------
// $Id: Program.cs 60 2010-09-27 10:50:44Z nagasima $
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
using System.IO;
using System.Diagnostics;

namespace Console
{
	class Program
	{
		IUnitSim m_Sample1;
		Thread m_Thread;
		AutoResetEvent m_Event;
		bool m_Terminated;
		FileStream m_TraceLog;

		static void Main(string[] args)
		{
			Program Program = new Program();

			Program.Init();

			Program.Start();

			for (; ; )
			{
				ConsoleKeyInfo info = System.Console.ReadKey();

				if (Program.m_Terminated)
					break;

				Program.m_Sample1.Input(0, new byte[] { (byte)info.KeyChar });
			}

			Program.Terminate();
		}

		public Program()
		{
			m_Thread = new Thread(new ThreadStart(TimerProc));
			m_Thread.Name = "TimerProc";
			m_Event = new AutoResetEvent(false);
		}

		public void Init()
		{
			try
			{
				m_TraceLog = new FileStream("trace" + DateTime.Now.ToString("yyyyMMddhhmmss") + ".log", FileMode.CreateNew);
			}
			catch (Exception)
			{
				m_TraceLog = null;
			}

			m_Sample1 = new Sample1();
			m_Sample1.UnitExit += new UnitEventHandler(UnitSim_UnitExit);
			m_Sample1.UnitOutput += new UnitOutputEventHandler(UnitSim_Log);
			m_Sample1.UnitSetEvent += new UnitEventHandler(Unit_UnitSetEvent);
			m_Sample1.UnitGetSystemTime += new UnitGetSystemTimeEventHandler(Sample1_UnitGetSystemTime);

			m_Thread.Start();
		}

		public void Start()
		{
			m_Sample1.Start();
		}

		public void Terminate()
		{
			if (m_TraceLog != null)
			{
				m_TraceLog.Close();
				m_TraceLog.Dispose();
				m_TraceLog = null;
			}
			//m_Sample1.Exit();
		}

		public void UnitSim_UnitExit(IUnitSim sender)
		{
			if (m_Sample1 == sender)
			{
				m_Sample1 = null;
				m_Terminated = true;

				// DLLアンロードは少し遅らせて実行
				UnitEventHandler handler = new UnitEventHandler(delegate(IUnitSim unit)
				{
					IDisposable dsp = unit as IDisposable;
					if (dsp != null)
						dsp.Dispose();
				});
				handler.BeginInvoke(sender, null, this);
			}
		}

		public void UnitSim_Log(IUnitSim sender, int Kind, byte[] Data)
		{
			switch (Kind)
			{
				case 1:
					m_TraceLog.Write(Data, 0, Data.Length);
					break;
			}
		}

		Stopwatch m_Stopwatch = new Stopwatch();

		void TimerProc()
		{
			long prev, now, frequency, sim, esc;

			m_Stopwatch.Start();

			frequency = Stopwatch.Frequency;
			now = m_Stopwatch.ElapsedTicks;
			sim = now;
			esc = -frequency / 1000;

			while (!m_Terminated)
			{
				//タイムアウト処理があればを実行する
				m_Sample1.CallTimeOut(frequency);

				// タイマーの最小値を取得
				long timer = m_Sample1.GetTimer();
				int msec;

				if (timer != -1)
				{
					// タイムアウト後の時刻から、タイマー時間を計算
					long target = sim + timer;
					long temp = m_Stopwatch.ElapsedTicks;
					long diff = target - temp;
					if (diff < 0)
					{
						// 差が拡がり過ぎたらシミュレーション時刻を現在時刻にする
						if (diff < esc)
							sim = temp;
						msec = 0;
					}
					else
					{
						// タイムアウト値の差が２倍以上ある場合は、元に戻す。
						if (diff > 2 * timer)
							diff = timer;
						// WaitHandle.WaitOneは[ms]単位なので、秒単位から変換する。
						msec = Convert.ToInt32((1000 * diff) / frequency);
					}
				}
				else
					msec = -1;

				m_Event.WaitOne(msec, false);

				prev = now;
				now = m_Stopwatch.ElapsedTicks;

				// 永遠待ちの設定で待った場合、測定値を設定
				long interval;
				if (timer == -1)
					interval = now - prev;
				else
					interval = timer;

				sim += interval;

				// 時間が進んだことをメッセージ処理に知らせる
				m_Sample1.Progress(interval);

				// イベントを処理する（ここでm_Terminatedがtrueになることがある）
				m_Sample1.ProcessEvent();
			}

			m_Stopwatch.Stop();
		}

		void Unit_UnitSetEvent(IUnitSim sender)
		{
			m_Event.Set();
		}

		void Sample1_UnitGetSystemTime(IUnitSim sender, ref long now, ref long frequency)
		{
			now = m_Stopwatch.ElapsedTicks;
			frequency = Stopwatch.Frequency;
		}
	}
}
