using System;
using System.Linq;
using System.Windows.Forms;
namespace BasicCalculator
{
///
/// A basic calculator
///
public partial class Form1 : Form
{
#region Constructor
///
/// Default constructor
///
public Form1()
{
InitializeComponent();
}
#endregion
#region Clearing Methods
///
/// Clears the user input text
///
/// The event sender
/// The event arguments
private void CEButton_Click(object sender, EventArgs e)
{
// Clears the text from the user input text box
this.UserInputText.Text = string.Empty;
// Focus the user input text
FocusInputText();
}
///
/// Deletes the first character in front of the cursor
///
/// The event sender
/// The event arguments
private void DelButton_Click(object sender, EventArgs e)
{
// Delete the value after the selected position
DeleteTextValue();
// Focus the user input text
FocusInputText();
}
#endregion
#region Operator Methods
///
/// Adds the Divide character to the text at the currently selection position
///
/// The event sender
/// The event arguments
private void DivideButton_Click(object sender, EventArgs e)
{
// Insert the value in the user input text box at the currently selected position
InsertTextValue("/");
// Focus the user input text
FocusInputText();
}
///
/// Adds the Times character to the text at the currently selection position
///
/// The event sender
/// The event arguments
private void TimesButton_Click(object sender, EventArgs e)
{
// Insert the value in the user input text box at the currently selected position
InsertTextValue("*");
// Focus the user input text
FocusInputText();
}
///
/// Adds the Minus character to the text at the currently selection position
///
/// The event sender
/// The event arguments
private void MinusButton_Click(object sender, EventArgs e)
{
// Insert the value in the user input text box at the currently selected position
InsertTextValue("-");
// Focus the user input text
FocusInputText();
}
///
/// Adds the Plus character to the text at the currently selection position
///
/// The event sender
/// The event arguments
private void PlusButton_Click(object sender, EventArgs e)
{
// Insert the value in the user input text box at the currently selected position
InsertTextValue("+");
// Focus the user input text
FocusInputText();
}
///
/// Calculates the given equation in the user input text
///
/// The event sender
/// The event arguments
private void EqualsButton_Click(object sender, EventArgs e)
{
// Calculate the equation
CalculateEquation();
// Focus the user input text
FocusInputText();
}
#endregion
#region Number Methods
///
/// Adds the . character to the text at the currently selection position
///
/// The event sender
/// The event arguments
private void DotButton_Click(object sender, EventArgs e)
{
// Insert the value in the user input text box at the currently selected position
InsertTextValue(".");
// Focus the user input text
FocusInputText();
}
///
/// Adds the 0 character to the text at the currently selection position
///
/// The event sender
/// The event arguments
private void ZeroButton_Click(object sender, EventArgs e)
{
// Insert the value in the user input text box at the currently selected position
InsertTextValue("0");
// Focus the user input text
FocusInputText();
}
///
/// Adds the 1 character to the text at the currently selection position
///
/// The event sender
/// The event arguments
private void OneButton_Click(object sender, EventArgs e)
{
// Insert the value in the user input text box at the currently selected position
InsertTextValue("1");
// Focus the user input text
FocusInputText();
}
///
/// Adds the 2 character to the text at the currently selection position
///
/// The event sender
/// The event arguments
private void TwoButton_Click(object sender, EventArgs e)
{
// Insert the value in the user input text box at the currently selected position
InsertTextValue("2");
// Focus the user input text
FocusInputText();
}
///
/// Adds the 3 character to the text at the currently selection position
///
/// The event sender
/// The event arguments
private void ThreeButton_Click(object sender, EventArgs e)
{
// Insert the value in the user input text box at the currently selected position
InsertTextValue("3");
// Focus the user input text
FocusInputText();
}
///
/// Adds the 4 character to the text at the currently selection position
///
/// The event sender
/// The event arguments
private void FourButton_Click(object sender, EventArgs e)
{
// Insert the value in the user input text box at the currently selected position
InsertTextValue("4");
// Focus the user input text
FocusInputText();
}
///
/// Adds the 5 character to the text at the currently selection position
///
/// The event sender
/// The event arguments
private void FiveButton_Click(object sender, EventArgs e)
{
// Insert the value in the user input text box at the currently selected position
InsertTextValue("5");
// Focus the user input text
FocusInputText();
}
///
/// Adds the 6 character to the text at the currently selection position
///
/// The event sender
/// The event arguments
private void SixButton_Click(object sender, EventArgs e)
{
// Insert the value in the user input text box at the currently selected position
InsertTextValue("6");
// Focus the user input text
FocusInputText();
}
///
/// Adds the 7 character to the text at the currently selection position
///
/// The event sender
/// The event arguments
private void SevenButton_Click(object sender, EventArgs e)
{
// Insert the value in the user input text box at the currently selected position
InsertTextValue("7");
// Focus the user input text
FocusInputText();
}
///
/// Adds the 8 character to the text at the currently selection position
///
/// The event sender
/// The event arguments
private void EightButton_Click(object sender, EventArgs e)
{
// Insert the value in the user input text box at the currently selected position
InsertTextValue("8");
// Focus the user input text
FocusInputText();
}
///
/// Adds the 9 character to the text at the currently selection position
///
/// The event sender
/// The event arguments
private void NineButton_Click(object sender, EventArgs e)
{
// Insert the value in the user input text box at the currently selected position
InsertTextValue("9");
// Focus the user input text
FocusInputText();
}
#endregion
///
/// Calculates the given equation and outputs the answer to the user label
///
private void CalculateEquation()
{
// 1. Enums
// 2. New classes
// 3. For loops
// 4. try/catch
// 5. Recursive functions
// 6. Switch statements
this.CalculationResultText.Text = ParseOperation();
// Focus the user input text
FocusInputText();
}
///
/// Parses the users equation and calculates the result
///
///
private string ParseOperation()
{
try
{
// Get the users equation input
var input = this.UserInputText.Text;
// Remove all spaces
input = input.Replace(" ", "");
// Create a new top-level operation
var operation = new Operation();
var leftSide = true;
// Loop through each character of the input
// starting from the left working to the right
for (int i = 0; i < input.Length; i++)
{
// TODO: Handle order priority
// 4.2 + 5.7 * 3
// It should calculate 5 * 3 first, then 4 + the result (so 4 + 15)
// Check if the current character is a number
if ("0123456789.".Any(c => input[i] == c))
{
if (leftSide)
operation.LeftSide = AddNumberPart(operation.LeftSide, input[i]);
else
operation.RightSide = AddNumberPart(operation.RightSide, input[i]);
}
// If it is an operator ( + - * / ) set the operator type
else if ("+-*/".Any(c => input[i] == c))
{
// If we are on the right side already, we now need to calculate our current operation
// and set the result to the left side of the next operation
if (!leftSide)
{
// Get the operator type
var operatorType = GetOperationType(input[i]);
// Check if we actually have a right side number
if (operation.RightSide.Length == 0)
{
// Check the operator is not a minus (as they could be creating a negative number)
if (operatorType != OperationType.Minus)
throw new InvalidOperationException($"Operator (+ * / or more than one -) specified without an right side number");
// If we got here, the operator type is a minus, and there is no left number currently, so add the minus to the number
operation.RightSide += input[i];
}
else
{
// Calculate previous equation and set to the left side
operation.LeftSide = CalculateOperation(operation);
// Set new operator
operation.OperationType = operatorType;
// Clear the previous right number
operation.RightSide = string.Empty;
}
}
else
{
// Get the operator type
var operatorType = GetOperationType(input[i]);
// Check if we actually have a left side number
if (operation.LeftSide.Length == 0)
{
// Check the operator is not a minus (as they could be creating a negative number)
if (operatorType != OperationType.Minus)
throw new InvalidOperationException($"Operator (+ * / or more than one -) specified without an left side number");
// If we got here, the operator type is a minus, and there is no left number currently, so add the minus to the number
operation.LeftSide += input[i];
}
else
{
// If we get here, we have a left number and now an operator, so we want to move to the right side
// Set the operation type
operation.OperationType = operatorType;
// Move to the right side
leftSide = false;
}
}
}
}
// If we are done parsing, and there were no exceptions
// calculate the current operation
return CalculateOperation(operation);
}
catch (Exception ex)
{
return $"Invalid equation. {ex.Message}";
}
}
///
/// Calculates an and returns the result
///
/// The operation to calculate
private string CalculateOperation(Operation operation)
{
// Store the number values of the string representations
decimal left = 0;
decimal right = 0;
// Check if we have a valid left side number
if (string.IsNullOrEmpty(operation.LeftSide) || !decimal.TryParse(operation.LeftSide, out left))
throw new InvalidOperationException($"Left side of the operation was not a number. {operation.LeftSide}");
// Check if we have a valid right side number
if (string.IsNullOrEmpty(operation.RightSide) || !decimal.TryParse(operation.RightSide, out right))
throw new InvalidOperationException($"Right side of the operation was not a number. {operation.RightSide}");
try
{
switch (operation.OperationType)
{
case OperationType.Add:
return (left + right).ToString();
case OperationType.Minus:
return (left - right).ToString();
case OperationType.Divide:
return (left / right).ToString();
case OperationType.Multiply:
return (left * right).ToString();
default:
throw new InvalidOperationException($"Unknown operator type when calculating operation. { operation.OperationType }");
}
}
catch (Exception ex)
{
throw new InvalidOperationException($"Failed to calculate operation {operation.LeftSide} {operation.OperationType} {operation.RightSide}. {ex.Message}");
}
}
///
/// Accepts a character and returns the known
///
/// The character to parse
///
private OperationType GetOperationType(char character)
{
switch (character)
{
case '+':
return OperationType.Add;
case '-':
return OperationType.Minus;
case '/':
return OperationType.Divide;
case '*':
return OperationType.Multiply;
default:
throw new InvalidOperationException($"Unknown operator type { character }");
}
}
///
/// Attempts to add a new character to the current number, checking for valid characters as it goes
///
/// The current number string
/// The new character to append to the string
///
private string AddNumberPart(string currentNumber, char newCharacter)
{
// Check if there is already a . in the number
if (newCharacter == '.' && currentNumber.Contains('.'))
throw new InvalidOperationException($"Number {currentNumber} already contains a . and another cannot be added");
return currentNumber + newCharacter;
}
#region Private Helpers
///
/// Focuses the user input text
///
private void FocusInputText()
{
this.UserInputText.Focus();
}
///
/// Inserts the given text into the user input text box
///
/// The value to insert
private void InsertTextValue(string value)
{
// Remember selection start
var selectionStart = this.UserInputText.SelectionStart;
// Set new text
this.UserInputText.Text = this.UserInputText.Text.Insert(this.UserInputText.SelectionStart, value);
// Restore the selection start
this.UserInputText.SelectionStart = selectionStart + value.Length;
// Set selection length to zero
this.UserInputText.SelectionLength = 0;
}
///
/// Deletes the character to the right of the selection start of the user input text box
///
private void DeleteTextValue()
{
// If we don't have a value to delete, return
if (this.UserInputText.Text.Length < this.UserInputText.SelectionStart + 1)
return;
// Remember selection start
var selectionStart = this.UserInputText.SelectionStart;
// Delete the character to the right of the selection
this.UserInputText.Text = this.UserInputText.Text.Remove(this.UserInputText.SelectionStart, 1);
// Restore the selection start
this.UserInputText.SelectionStart = selectionStart;
// Set selection length to zero
this.UserInputText.SelectionLength = 0;
}
#endregion
}
}