using WPF.Common.WPF;
using WPF.Common;
using WPF.Model;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using Visibility = System.Windows.Visibility;
using TheoremAuto = HyperGraphModel.Theorem.TheoremAuto;
using static WPF.Model.Calculate;
namespace WPF.ViewModel
{
/// <summary>
/// Класс работы главного окна приложения
/// </summary>
public class MainViewModel : ModifyViewModel
{
public MainViewModel() : base()
{
Current = this;
Verticies = new ObservableCollection<VertViewModel>();
Verticies.NotifyPropertyChanged((obj, notifyPropertyChangedEventArgs) =>
{
SetResultDefault();
});
EnumTheorems = Enum.GetValues(typeof(EnumTheorem)).Cast<EnumTheorem>().ToList();
SelectedTheorem = EnumTheorems.First();
EdgesNum = 1;
SameVertNum = 1;
KoeffForMinSameVert = 1;
PathToSave = DefaultPathToSave;
CalculateCommand = new RelayCommand(OnCalculate);
SaveResultCommand = new RelayCommand(OnSaveResult, () => CanSave && IsTheoremCorrect);
// Так выполнял отладку, чтобы руками значения не вводить
#if DEBUG
//EdgesNum = 6;
//SameVertNum = 2;
//Verticies[0].Value = 4;
//Verticies[1].Value = 5;
//Verticies[2].Value = 6;
//Verticies[3].Value = 6;
//Verticies[4].Value = 2;
//Verticies[5].Value = 7;
EdgesNum = 4;
SameVertNum = 2;
Verticies[0].Value = 7;
Verticies[1].Value = 5;
Verticies[2].Value = 6;
Verticies[3].Value = 8;
SelectedTheorem = EnumTheorem.Forth;
#endif
}
#region Поля и свойства
// Статичный указатель на текущее окно
public static MainViewModel Current { get; private set; }
// Стандартный путь к месту сохранения файла экспорта
private readonly string DefaultPathToSave = $"C:\\Users\\{Environment.UserName}\\Documents\\";
// Путь сохранения к файлу экспорта, используемый в runtime
private string PathToSave { get; set; }
public static int MaxValue { get; } = 99;
public static int MinValue { get; } = 0;
// Список вариантов типа теорем
public List<EnumTheorem> EnumTheorems { get; }
// Текущий выбранный тип теоремы
private EnumTheorem _selectedTheorem;
public EnumTheorem SelectedTheorem
{
set
{
_selectedTheorem = value;
SetResultDefault();
OnSelectedTheoremChange();
OnPropertyChanged(() => SelectedTheorem);
}
get => _selectedTheorem;
}
// Можно ли выполнить экспорт результатов
private bool _canSave;
public bool CanSave
{
get => _canSave;
set
{
_canSave = value;
OnPropertyChanged(() => CanSave);
}
}
// Число ребер
private int _edgesNum;
public int EdgesNum
{
get => _edgesNum;
set
{
_edgesNum = value;
SetResultDefault();
OnPropertyChanged(() => EdgesNum);
UpdateVerticies();
OnPropertyChanged(() => VisibilityVerticies);
}
}
// Число общих или пересекающихся вершин
private int _sameVertNum;
public int SameVertNum
{
get => _sameVertNum;
set
{
_sameVertNum = value;
SetResultDefault();
OnPropertyChanged(() => SameVertNum);
UpdateVerticiesValue();
OnPropertyChanged(() => VisibilityVerticies);
}
}
// Параметр показа блока ввода вершин для каждого ребра
public Visibility VisibilityVerticies
{
get
{
return EdgesNum > 0 && SameVertNum > 0 ? Visibility.Visible : Visibility.Collapsed;
}
}
// Коллекция числа вершин для каждого ребра
public ObservableCollection<VertViewModel> Verticies { get; }
// Параметр отображаемого текста для "Общих" или "Пересекающихся" вершин
private string _sameVertText;
public string SameVertText
{
get => _sameVertText;
set
{
_sameVertText = value;
OnPropertyChanged(() => SameVertText);
}
}
// Параметр текста подсказки для "Общих" или "Пересекающихся" вершин
private string _sameVertTextTooltip;
public string SameVertTextTooltip
{
get => _sameVertTextTooltip;
set
{
_sameVertTextTooltip = value;
OnPropertyChanged(() => SameVertTextTooltip);
}
}
#region Блок отображаемого текста
public string TitleText { get => "Расчет гиперграфов"; }
public string EdgesText { get => "Количество ребер"; }
public string EdgesTextTooltip { get => "Подсказка. Количество ребер"; }
public string VerticiesText { get => "Номер ребра"; }
public string VerticiesTextTooltip { get => "Подсказка. Номер ребра"; }
public string VerticiesValueText { get => "Число вершин"; }
public string VerticiesValueTooltip { get => "Подсказка. Число вершин в каждом ребре"; }
public string TheoremOKText { get => "Удовлетворяет условиям теоремы"; }
public string TheoremOKTooltip { get => "Подсказка. Удовлетворяет условиям теоремы"; }
public string AutomorphismText { get => "Число автоморфизмов"; }
public string AutomorphismTooltip { get => "Подсказка. Число автоморфизмов"; }
#endregion
// Параметр, выполняются условия теоремы
private bool _isTheoremCorrect;
public bool IsTheoremCorrect
{
get => _isTheoremCorrect;
set
{
_isTheoremCorrect = value;
OnPropertyChanged(() => IsTheoremCorrect);
}
}
// Параметр числа автоморфизмов
private long _automorphismCount;
public long AutomorphismCount
{
get => _automorphismCount;
set
{
_automorphismCount = value;
OnPropertyChanged(() => AutomorphismCount);
}
}
// Параметр результата расчета теоремы
private TheoremAuto _result;
public TheoremAuto Result
{
get => _result;
set
{
_result = value;
IsTheoremCorrect = value.isSatisfyTheorem;
AutomorphismCount = value.CountAutomorphism;
OnPropertyChanged(() => Result);
}
}
// Параметр статуса выполнения приложения
// Влияет на цвет
private Status _status;
public Status Status
{
get { return _status; }
set
{
_status = value;
OnPropertyChanged(() => Status);
}
}
// Параметр текста сообщения в строке статуса
private string _statusMessage;
public string StatusMessage
{
get { return _statusMessage; }
set
{
_statusMessage = value;
OnPropertyChanged(() => StatusMessage);
}
}
// Параметр, отвечающий за коэффициент минимального числа общих или пересекающихся вершин
// Т.к. в случае "общих" вершин, минимальным является само число общих вершин
// а в случае "пересекающихся", минимальным является число пересекающихся вершин * 2
private int _koeffForMinSameVert;
public int KoeffForMinSameVert
{
get => _koeffForMinSameVert;
set
{
_koeffForMinSameVert = value >= 1 ? value : 1;
UpdateVerticiesValue();
OnPropertyChanged(() => KoeffForMinSameVert);
}
}
#endregion
#region Методы и команды
// Что происходит, когда пользователь меняет выбранную теорему
private void OnSelectedTheoremChange()
{
switch (SelectedTheorem)
{
case EnumTheorem.Second:
case EnumTheorem.Third:
{
CanSave = true;
KoeffForMinSameVert = 1;
SameVertText = "Количество общих вершин";
SameVertTextTooltip = "Подсказка. Количество общих вершин";
break;
}
case EnumTheorem.Forth:
{
CanSave = true;
KoeffForMinSameVert = 2;
SameVertText = "Количество пересекающихся вершин";
SameVertTextTooltip = "Подсказка. Количество вершин, являющихся общими между соседними ребрами";
break;
}
case EnumTheorem.Fifth:
{
CanSave = false;
KoeffForMinSameVert = 2;
SameVertText = "Количество пересекающихся вершин";
SameVertTextTooltip = "Подсказка. Количество вершин, являющихся общими между соседними ребрами";
break;
}
}
}
// Что происходит, когда пользователь меняет число ребер
// Нужно изменить размер коллекции числа вершин для каждого ребра
private void UpdateVerticies()
{
if (EdgesNum > Verticies.Count)
{
for (int i = Verticies.Count; i < EdgesNum; i++)
Verticies.Add(new VertViewModel(SameVertNum, (i + 1).ToString()));
}
else if (EdgesNum < Verticies.Count)
{
var skip = Verticies.Skip(EdgesNum).ToList();
foreach (var vert in skip)
{
Verticies.Remove(vert);
}
}
UpdateVerticiesValue();
}
// Когда пользователь меняет число общих или пересекающихся вершин
// нужно изменить минимальное число вершин для каждого ребра
// А еще, когда пользователь меняет тип теоремы, меняется коэффициент, и также нужно пересчитать минимальное число вершин
// Вместе с минимальным числом меняем и текущее, если оно меньше минимального
private void UpdateVerticiesValue()
{
var val = SameVertNum * KoeffForMinSameVert;
foreach (var vert in Verticies)
{
if (vert.Value < val)
vert.Value = val;
vert.MinValue = val;
}
}
// Обработка команды нажатия на кнопку вычисления
public RelayCommand CalculateCommand { get; }
private void OnCalculate()
{
// Создаем граф
CreateGraph(EdgesNum, SameVertNum, Verticies.Select(x => x.Value).ToList(), SelectedTheorem);
// Обнуляем результат и панель статуса
SetResultDefault();
switch (SelectedTheorem)
{
case EnumTheorem.Second:
{
Result = GetTheoremResult(SelectedTheorem);
Status = Status.OperationSuccess;
StatusMessage = "Расчет успешно выполнен";
if (!Result.isSatisfyTheorem && Graph.IsEqualNumberOfVertices)
{
Status = Status.SpecialWarning;
StatusMessage = "Не удовлетворяет условиям теоремы. Используйте теорему три";
}
break;
}
case EnumTheorem.Third:
{
Result = GetTheoremResult(SelectedTheorem);
Status = Status.OperationSuccess;
StatusMessage = "Расчет успешно выполнен";
if (!Result.isSatisfyTheorem && !Graph.IsEqualNumberOfVertices)
{
Status = Status.SpecialWarning;
StatusMessage = "Не удовлетворяет условиям теоремы. Используйте теорему два";
}
break;
}
case EnumTheorem.Forth:
{
Result = GetTheoremResult(SelectedTheorem);
Status = Status.OperationSuccess;
StatusMessage = "Расчет успешно выполнен";
if (!Result.isSatisfyTheorem && Graph.IsEqualNumberOfVertices)
{
Status = Status.SpecialWarning;
StatusMessage = "Не удовлетворяет условиям теоремы. Используйте теорему пять";
}
break;
}
case EnumTheorem.Fifth:
{
Result = GetTheoremResult(SelectedTheorem);
Status = Status.OperationSuccess;
StatusMessage = "Расчет успешно выполнен";
if (!Result.isSatisfyTheorem && !Graph.IsEqualNumberOfVertices)
{
Status = Status.SpecialWarning;
StatusMessage = "Не удовлетворяет условиям теоремы. Используйте теорему четыре";
}
break;
}
}
}
// Обработка команды нажатия на экспорт результатов
public RelayCommand SaveResultCommand { get; }
private void OnSaveResult()
{
if (!Directory.Exists(PathToSave))
PathToSave = DefaultPathToSave;
var streamResult = Export(Result, SelectedTheorem);
if (streamResult.CanRead && streamResult.Length == 0)
{
Status = Status.Error;
StatusMessage = "Ошибка экспорта. Поток экспорта пустой.";
streamResult.Close();
return;
}
using (SaveFileDialog saveFileDialog = new SaveFileDialog()
{
Filter = "Text file (*.txt)|*.txt",/*"Text file (*.txt)|*.txt|C# file (*.cs)|*.cs",*/
InitialDirectory = PathToSave
})
{
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
PathToSave = Path.GetDirectoryName(saveFileDialog.FileName);
File.WriteAllBytes(saveFileDialog.FileName, streamResult.ToArray());
Status = Status.OperationSuccess;
StatusMessage = $"Файл успешно сохранен: {saveFileDialog.FileName}";
}
}
}
// Сбрасывает текущее состояние и результат
private void SetResultDefault()
{
Result = default;
Status = Status.Default;
StatusMessage = "Результат успешно сброшен";
}
#endregion
}
/// <summary>
/// Класс отображения числа вершин в каждом ребре
/// </summary>
public class VertViewModel : BaseViewModel
{
public VertViewModel(int startValue, string text)
{
Value = startValue;
MinValue = startValue;
VertText = text;
}
// Текущее значение числа вершин
private int _value;
public int Value
{
get => _value;
set
{
_value = value;
OnPropertyChanged(() => Value);
}
}
// Минимальное значение число вершин
private int _minValue;
public int MinValue
{
get => _minValue;
set
{
_minValue = value;
OnPropertyChanged(() => MinValue);
}
}
// Отображаемое название вершины
public string VertText { get; }
}
}