A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

207 lines
7.4 KiB

using System;
using System.Globalization;
using System.IO;
using System.Reactive;
using Calc.Models;
using ReactiveUI;
namespace Calc.ViewModels
{
public class MainWindowViewModel : ViewModelBase
{
private string _shownString = string.Empty;
private string _shownResult = string.Empty;
private int _numberOfOpeningParentheses;
private int _numberOfClosingParentheses;
// Commands
public ReactiveCommand<Unit, Unit> AddDecimalSeparatorCommand { get; }
public ReactiveCommand<int, Unit> AddNumberCommand { get; }
public ReactiveCommand<Operator, Unit> AddOperatorCommand { get; }
public ReactiveCommand<Unit, Unit> AddParenthesisCommand { get; }
public ReactiveCommand<Unit, Unit> AlternateNegativePositiveCommand { get; }
public ReactiveCommand<Unit, Unit> ClearScreenCommand { get; }
public ReactiveCommand<Unit, Unit> DeleteLastCommand { get; }
public ReactiveCommand<Unit, Unit> PickResultCommand { get; }
public MainWindowViewModel()
{
AddDecimalSeparatorCommand = ReactiveCommand.Create(AddDecimalSeparator);
AddNumberCommand = ReactiveCommand.Create<int>(AddNumber);
AddOperatorCommand = ReactiveCommand.Create<Operator>(AddOperator);
AddParenthesisCommand = ReactiveCommand.Create(AddParenthesis);
AlternateNegativePositiveCommand = ReactiveCommand.Create(AlternateNegativePositive);
ClearScreenCommand = ReactiveCommand.Create(ClearScreen);
DeleteLastCommand = ReactiveCommand.Create(DeleteLast);
PickResultCommand = ReactiveCommand.Create(PickResult);
}
public string ShownString
{
get => _shownString;
set => this.RaiseAndSetIfChanged(ref _shownString, value);
}
public string ShownResult
{
get => _shownResult;
set => this.RaiseAndSetIfChanged(ref _shownResult, value);
}
private void AddDecimalSeparator()
{
if (CanDecimalSeparatorBePlaced())
ShownString += CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
}
private void AddNumber(int value)
{
ShownString += value;
Calculate(ShownString);
}
private void AddOperator(Operator @operator)
{
if (ShownString[^1].Equals('('))
return;
if (IsLastInputAnOperator())
ShownString = ShownString[..^1];
ShownString += @operator switch
{
Operator.Add => OperatorChar.Add,
Operator.Subtract => OperatorChar.Subtract,
Operator.Multiply => OperatorChar.Multiply,
Operator.Divide => OperatorChar.Divide,
_ => throw new InvalidDataException("Operator not allowed")
};
}
private void AddParenthesis()
{
if (ShownString.Length == 0 || IsLastInputAnOperator() || ShownString[^1].Equals('('))
{
ShownString += "(";
_numberOfOpeningParentheses++;
}
else if (_numberOfClosingParentheses < _numberOfOpeningParentheses)
{
ShownString += ")";
_numberOfClosingParentheses++;
Calculate(ShownString);
}
}
private void AlternateNegativePositive()
{
var indexWhereSetOrUnsetSign = SetIndexWhereToSetOrUnsetSign();
if (ShownString.Length == 0 || ShownString[^1].Equals('('))
ShownString += OperatorChar.Subtract;
else
{
switch (ShownString[indexWhereSetOrUnsetSign])
{
case OperatorChar.Subtract:
if (indexWhereSetOrUnsetSign == 0 ||
ShownString[indexWhereSetOrUnsetSign - 1].Equals('(') ||
OperatorChar.IsAnOperator(ShownString[indexWhereSetOrUnsetSign - 1]))
ShownString = ShownString.Remove(indexWhereSetOrUnsetSign, 1);
else
// Add -
ShownString = ShownString[..indexWhereSetOrUnsetSign] +
OperatorChar.Subtract +
ShownString[indexWhereSetOrUnsetSign..];
break;
default:
// Add -
ShownString = ShownString[..indexWhereSetOrUnsetSign] +
OperatorChar.Subtract +
ShownString[indexWhereSetOrUnsetSign..];
break;
}
Calculate(ShownString);
}
}
private void Calculate(string calc)
{
ShownResult = Calculator.Calculate(calc);
}
private bool CanDecimalSeparatorBePlaced()
{
var indexLastDecimalSeparator = ShownString.LastIndexOf(
CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator,
StringComparison.Ordinal);
var indexLastOperator = ShownString.LastIndexOfAny(OperatorChar.Operators);
if (indexLastDecimalSeparator == -1 && indexLastOperator == -1)
return true;
return indexLastOperator > indexLastDecimalSeparator;
}
private void ClearScreen()
{
ShownString = string.Empty;
ShownResult = string.Empty;
_numberOfOpeningParentheses = 0;
_numberOfClosingParentheses = 0;
}
private void DeleteLast()
{
if (ShownString.Length == 1)
{
ClearScreen();
return;
}
// Update number of parentheses
switch (ShownString[^1])
{
case '(':
_numberOfOpeningParentheses--;
break;
case ')':
_numberOfClosingParentheses--;
break;
}
ShownString = ShownString[..^1];
Calculate(IsLastInputAnOperator() ? ShownString[..^1] : ShownString);
}
private bool IsLastInputAnOperator()
{
return OperatorChar.IsAnOperator(ShownString[^1]);
}
private static int MaxOf(int number1, int number2)
{
return number1 > number2 ? number1 : number2;
}
private void PickResult()
{
ShownString = ShownResult;
ShownResult = string.Empty;
}
private int SetIndexWhereToSetOrUnsetSign()
{
char[] nonSubstractOperators = { OperatorChar.Add, OperatorChar.Multiply, OperatorChar.Divide };
var indexAfterLastNonSubstractOperator = ShownString.LastIndexOfAny(nonSubstractOperators) + 1;
var indexOfLastSubstractOperator = ShownString.LastIndexOf(OperatorChar.Subtract);
var indexAfterLastParenthesis = ShownString.LastIndexOf('(') + 1;
var indexLastOperator = MaxOf(indexAfterLastNonSubstractOperator, indexOfLastSubstractOperator);
return MaxOf(indexAfterLastParenthesis, indexLastOperator);
}
}
}