MainViewModel.cs

495 lines | 18.586 kB Blame History Raw Download
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;
            EdgesNum = 4;
            SameVertNum = 2;
            Verticies[0].Value = 6;
            Verticies[1].Value = 6;
            Verticies[2].Value = 6;
            Verticies[3].Value = 6;
            SelectedTheorem = EnumTheorem.Fifth;
#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:
                case EnumTheorem.Fifth:
                    {
                        CanSave = true;
                        KoeffForMinSameVert = 2;
                        SameVertText = "Количество k-вершин";
                        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; }
    }
}