using System;
using System.Linq;
using System.Windows.Forms;

namespace Phidget22.ExampleUtils {
	public partial class RescaleHelper : Form {
		Mode mode;
		RescaleType type;
		Phidget deviceOpened;
		Form parent;

		public RescaleHelper() {
			InitializeComponent();
		}

		public RescaleHelper(Form owner, Phidget device) {
			InitializeComponent();
			this.Owner = owner;
			parent = owner;
			deviceOpened = device;
			IntPtr dummy = this.Handle; //Force the form to load before it's shown so we can call invoke on it

			this.MinimizeBox = false;
			this.MaximizeBox = false;
		}

		private void RescaleHelper_Load(object sender, EventArgs e) {
			if (deviceOpened.ChannelClass == ChannelClass.Stepper) {
				stepperBox.Visible = true;
				mode = Mode.STEPPER;
				if (((Stepper)deviceOpened).ControlMode == StepperControlMode.Step) {
					type = RescaleType.POSITION;
					positionPanel.Visible = true;
				} else {
					type = RescaleType.VELOCITY;
					velocityPanel.Visible = true;
				}
			} else if (deviceOpened.ChannelClass == ChannelClass.BLDCMotor) {
				bldcBox.Visible = true;
				mode = Mode.BLDCMOTOR;
				type = RescaleType.POSITION;
			} else if (deviceOpened.ChannelClass == ChannelClass.MotorPositionController) {
				try {
					var temp = ((MotorPositionController)deviceOpened).PositionType;
					bldcBox.Visible = true;
					if (temp == PositionType.Encoder)
						bldcEncoderPanel.Visible = true;
					mode = Mode.BLDCMOTOR;
					type = RescaleType.POSITION;
				} catch { dcBox.Visible = true; mode = Mode.DCMOTOR; type = RescaleType.POSITION; }
			} else if (deviceOpened.ChannelClass == ChannelClass.MotorVelocityController) {
				try {
					var temp = ((MotorVelocityController)deviceOpened).PositionType;
					bldcBox.Visible = true;
					if (temp == PositionType.Encoder)
						bldcEncoderPanel.Visible = true;
					mode = Mode.BLDCMOTOR;
					type = RescaleType.VELOCITY;
				} catch { dcBox.Visible = true; mode = Mode.DCMOTOR; type = RescaleType.VELOCITY; }
			}

			if (type == RescaleType.POSITION)
				positionPanel.Visible = true;
			else if (type == RescaleType.VELOCITY)
				velocityPanel.Visible = true;

			liveUpdateTimer.Enabled = true;
		}

		private void RescaleHelper_FormClosing(object sender, FormClosingEventArgs e) {
			clearTextBoxes();

			stepperBox.Visible = false;
			dcBox.Visible = false;
			bldcBox.Visible = false;
			bldcEncoderPanel.Visible = false;
			positionPanel.Visible = false;
			velocityPanel.Visible = false;
			stepAngle = double.NaN;
			encoderCPR = double.NaN;
			poleCount = double.NaN;

			liveUpdateTimer.Enabled = false;
		}

		private double stepAngle = double.NaN;
		private double encoderCPR = double.NaN;
		private double poleCount = double.NaN;

		public double rescaleFactor = 1;

		private void calcBtn_Click(object sender, EventArgs e) {
			if (rescaleFactor.Equals(double.NaN) || double.IsInfinity(rescaleFactor)) {
				this.DialogResult = DialogResult.Abort;
				this.Close();
				return;
			}
			this.DialogResult = DialogResult.OK;
			this.Close();

		}

		private double parseGearRatio(TextBox field) {
			string[] tokens;
			double wholePart, fractionalPart;

			try {
				if (field.Text.Contains(' ')) {
					tokens = field.Text.Split(' ');
					wholePart = double.Parse(tokens[0]);
					string[] fractionalTokens = tokens[1].Split('/');
					if (tokens.Length > 2 || fractionalTokens.Length > 2) {
						return double.NaN;
					}
					fractionalPart = double.Parse(fractionalTokens[0]) / double.Parse(fractionalTokens[1]);

					return wholePart + fractionalPart;

				} else if (field.Text.Contains('+')) {
					tokens = field.Text.Split('+');
					wholePart = double.Parse(tokens[0]);
					string[] fractionalTokens = tokens[1].Split('/');
					if (tokens.Length > 2 || fractionalTokens.Length > 2) {
						return double.NaN;
					}
					fractionalPart = double.Parse(fractionalTokens[0]) / double.Parse(fractionalTokens[1]);

					return wholePart + fractionalPart;

				} else {
					return double.Parse(field.Text);
				}
			} catch { return double.NaN; }
		}

		private void clearTextBoxes() {
			stepperGearRatioTxt.Clear();
			stepAngleTxt.Clear();

			dcGearRatioTxt.Clear();
			dcEncoderTxt.Clear();

			bldcGearRatioTxt.Clear();
			poleCountTxt.Clear();
			bldcEncoderTxt.Clear();
		}

		private void liveUpdateTimer_Tick(object sender, EventArgs e) {
			//first step is to parse the gear ratio string
			double gearRatio;

			if (mode == Mode.STEPPER)
				gearRatio = parseGearRatio(stepperGearRatioTxt);
			else if (mode == Mode.DCMOTOR)
				gearRatio = parseGearRatio(dcGearRatioTxt);
			else if (mode == Mode.BLDCMOTOR)
				gearRatio = parseGearRatio(bldcGearRatioTxt);
			else
				gearRatio = double.NaN;

			if (gearRatio == double.NaN)
				return;

			//next crunch the numbers
			bool result;
			switch (mode) {
			case Mode.STEPPER:
				result = double.TryParse(stepAngleTxt.Text, out stepAngle);
				if (type == RescaleType.POSITION) {
					if (rotationRadio.Checked)
						rescaleFactor = stepAngle / (360 * 16 * gearRatio);
					else if (degreeRadio.Checked)
						rescaleFactor = stepAngle / (16 * gearRatio);
				} else if (type == RescaleType.VELOCITY) {
					if (rpsRadio.Checked)
						rescaleFactor = stepAngle / (360 * 16 * gearRatio);
					else if (rpmRadio.Checked)
						rescaleFactor = (stepAngle * 60) / (360 * 16 * gearRatio);
				}
				break;
			case Mode.DCMOTOR:
				result = double.TryParse(dcEncoderTxt.Text, out encoderCPR);
				if (type == RescaleType.POSITION) {
					if (rotationRadio.Checked)
						rescaleFactor = 1 / (encoderCPR * 4 * gearRatio);
					else if (degreeRadio.Checked)
						rescaleFactor = (360) / (encoderCPR * 4 * gearRatio);
				} else if (type == RescaleType.VELOCITY) {
					if (rpsRadio.Checked)
						rescaleFactor = 1 / (encoderCPR * 4 * gearRatio);
					else if (rpmRadio.Checked)
						rescaleFactor = 60 / (encoderCPR * 4 * gearRatio);
				}
				break;
			case Mode.BLDCMOTOR:
				if (bldcEncoderPanel.Visible)
					result = double.TryParse(bldcEncoderTxt.Text, out encoderCPR);
				else
					result = double.TryParse(poleCountTxt.Text, out poleCount);

				if (type == RescaleType.POSITION) {
					if (poleCount.Equals(double.NaN)) {
						if (rotationRadio.Checked)
							rescaleFactor = 1 / (encoderCPR * 4 * gearRatio);
						else if (degreeRadio.Checked)
							rescaleFactor = 360 / (encoderCPR * 4 * gearRatio);
					} else if (encoderCPR.Equals(double.NaN)) {
						if (rotationRadio.Checked)
							rescaleFactor = 1 / (poleCount * 3 * gearRatio);
						else if (degreeRadio.Checked)
							rescaleFactor = 360 / (poleCount * 3 * gearRatio);
					}
				} else if (type == RescaleType.VELOCITY) {
					if (poleCount.Equals(double.NaN)) {
						if (rpsRadio.Checked)
							rescaleFactor = 1 / (encoderCPR * 4 * gearRatio);
						else if (rpmRadio.Checked)
							rescaleFactor = 60 / (encoderCPR * 4 * gearRatio);
					} else if (encoderCPR.Equals(double.NaN)) {
						if (rpsRadio.Checked)
							rescaleFactor = 1 / (poleCount * 3 * gearRatio);
						else if (rpmRadio.Checked)
							rescaleFactor = 60 / (poleCount * 3 * gearRatio);
					}
				}
				break;
			default:
				rescaleFactor = 1;
				break;
			}

			try {
				liveRescaleTxt.Text = rescaleFactor.ToString();
			} catch { liveRescaleTxt.Text = "?"; }
		}
	}

	public enum Mode { STEPPER, DCMOTOR, BLDCMOTOR }
	public enum RescaleType { POSITION, VELOCITY }
}
