using RAYLASE.SPICE3;
using RAYLASE.SPICE3.ClientLib;
using System;
using System.Diagnostics;
using System.Drawing;

namespace LongBitmap
{
	internal class Program
	{
		// insert the IP of your SP-ICE-3
		private readonly string _cardIp = "192.168.2.158";
		private readonly ClientAPI _client = new ClientAPI();

		private static void Main()
		{
			Program program = new Program();

			program._client.Connect( program._cardIp );
			// Reset the card to its default settings:
			// This restores the following settings to the values that were last saved on the card:
			// - scanner configuration (such as field size, field correction, etc.)
			// - laser configuration (such as gate/LM timing parameters, power target, etc.)
			// - process variables (such as jump speed, settling times, mark speed, etc.)
			program._client.System.ResetToDefaults();
			program._client.Laser.ArmLaser( true );
			LaserConfig lc = program._client.Laser.GetConfig();
			lc.PowerScale = 1.0;
			program._client.Laser.SetConfig( lc );

			program.DetectPartThenMarkLongBitmap();
		}

		/// <summary>
		///   Mark a long bitmap, every time the part sensor is triggered. Take into account the field size.
		/// </summary>
		private void DetectPartThenMarkLongBitmap()
		{
			// Configure laser.

			LaserConfig laserConfig = _client.Laser.GetConfig();
			laserConfig.HotPowerTarget = PowerTarget.LmWidth;
			_client.Laser.SetConfig( laserConfig );

			// Configure scanner head.

			ScannerAPI scannerApi = _client.Scanner;
			ScannerConfig scannerConfig = scannerApi.GetConfig();
			scannerConfig.Heads[0].Axes[0].TrackingError = 230;
			scannerConfig.Heads[0].Axes[1].TrackingError = 230;
			scannerConfig.Heads[0].Axes[2].TrackingError = 0;
			scannerConfig.Heads[0].Axes[0].AccelerationTime = 383;
			scannerConfig.Heads[0].Axes[1].AccelerationTime = 383;
			scannerConfig.Heads[0].Axes[2].AccelerationTime = 0;
			_client.Scanner.SetConfig( scannerConfig );

			// Configure MOTF (or its simulator).

			const int encoderID = 0;
			const bool beltMovesPositive = false;
			const int beltSign = beltMovesPositive ? 1 : -1;
			const double encoderRadius = 78000;
			const int encoderCountPerRotation = 4 * 5000;
			const int distancePerEncoderCount = (int)( 2 * Math.PI * encoderRadius / encoderCountPerRotation );
			const int partDistance = 50000;
			const int debounceTime = 200000;

			// Remove comment for encoder simulation
			//_client.Motf.SetSimulationSpeed( encoderID, beltSpeed / distancePerEncoderCount, 0 );
			//_client.Motf.SimulateEncoder( encoderID, true );

			_client.Motf.QuadratureDecoder.ResetEncoderPosition( encoderID );
			_client.Motf.ResetState();

			QuadratureDecoderConfig qd = new QuadratureDecoderConfig();
			qd.Modules[encoderID].InvertDirection = true;
			qd.Modules[encoderID].DistancePerCount = distancePerEncoderCount;
			_client.Motf.QuadratureDecoder.SetConfig( qd );

			MotfConfig motfConfig = new MotfConfig
			{
				MainSource = { SourceType = MotfSourceType.QuadratureDecoder, ModuleID = encoderID },
				PartDistance = partDistance,

				// set debounce if the sensor is sending multiple signals
				EnableDebounce = true,
				DebounceTime = debounceTime
			};
			motfConfig.SetDirectionFromAngle( Math.PI / 2 + ( beltMovesPositive ? 0 : Math.PI ) );
			_client.Motf.SetConfig( motfConfig );

			// Build the command list.

			const int listID = 13;
			const double jumpSpeed = 5;
			const double markSpeed = .7;
			// Pulse width needs to be somewhat large here because bitmaps need more time to render the darker gray values.
			const int pulseWidth = 200;

			CommandList list = new CommandList();
			list.AppendJumpSpeed( jumpSpeed );
			list.AppendJumpDelay( 0 );
			list.AppendMarkSpeed( markSpeed );
			list.AppendPolyDelay( 0 );
			list.AppendMarkDelay( 0 );
			list.AppendLmFrequency( 1.0 / pulseWidth );
			// Utilize the whole pulse to be in active state.
			list.AppendLmWidth( pulseWidth );

			// Load a test image, and mark it on the 20% of the field

			const string filename = "LongBitmap-01.jpg";
			Bitmap image = new Bitmap( filename );
			int imageHeight = image.Height;
			int imageWidth = image.Width;

			const double coverageX = 0.2;
			double sizeX = coverageX * scannerConfig.FieldSize.X;
			double halfSizeX = sizeX / 2;
			double sizeY = sizeX * imageHeight / imageWidth;
			double deltaY = imageHeight > 1 ? sizeY / ( imageHeight - 1 ) : 0;

			list.AppendJumpAbs( -beltSign * halfSizeX, 0 );
			// wait for the part sensor
			list.AppendMotfWaitPart();

			double yPosition = 0;
			var powerValues = new ushort[image.Width];

			for ( int row = imageHeight; --row >= 0; )
			{
				for ( int column = 0; column < imageWidth; column++ )
					powerValues[column] = (ushort)( ushort.MaxValue * image.GetPixel( column, row ).GetBrightness() );

				list.AppendBitmapLineAbs( -halfSizeX, yPosition, halfSizeX, yPosition, powerValues );
				yPosition += deltaY;
			}

			_client.List.Set( listID, list );

			// Run the list. Give an extra time to wait.

			double unitMarkTime = sizeX / markSpeed;
			double unitJumpTime = Math.Sqrt( sizeX * sizeX + deltaY * deltaY ) / jumpSpeed;
			double unitProcessTime = unitMarkTime + unitJumpTime;
			// No jump after the last line.
			double totalTime = ( imageHeight - 1 ) * unitProcessTime - unitJumpTime;

			Console.WriteLine( "Belt speed should be {0} m/s", deltaY / unitProcessTime );

			Console.Out.WriteLine( "Executing..." );
			Stopwatch stopwatch = Stopwatch.StartNew();

			_client.List.Execute( listID );

			try
			{
				int? doneID;
				bool done = _client.List.WaitForListDone( out doneID, (int)( totalTime / 1000 + 10000 ) );

				if ( !done )
					Console.Out.WriteLine( "List did not finish within the timeout period" );
			}
			catch ( TimeoutException exception )
			{
				Console.Out.WriteLine( "Could not finish the list. Has the part sensor been triggered within the time limit?" );
				Console.WriteLine( exception );
			}
			finally
			{
				stopwatch.Stop();
				Console.WriteLine( "Expected to finish in {0} ms", totalTime / 1000 );
				Console.WriteLine( "Done in {0} ms", stopwatch.ElapsedMilliseconds );
			}
		}
	}
}
