// InputDialog.xaml.cs
using System.Windows;

namespace TxtProcessorApp
{
    /// <summary>
    /// Логика взаимодействия для InputDialog.xaml
    /// </summary>
    public partial class InputDialog : Window
    {
        public string ResponseText { get; private set; }

        public InputDialog(string question, string defaultAnswer = "")
        {
            InitializeComponent();
            PromptTextBlock.Text = question;
            ResponseTextBox.Text = defaultAnswer;
            ResponseTextBox.SelectAll();
            ResponseTextBox.Focus();
        }

        private void OK_Click(object sender, RoutedEventArgs e)
        {
            ResponseText = ResponseTextBox.Text;
            DialogResult = true;
        }
    }
}

<!-- EditWindow.xaml -->
<Window x:Class="TxtProcessorApp.EditWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Редактирование данных" Height="600" Width="900">
    <Grid Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <!-- DataGrid для отображения и редактирования данных -->
        <DataGrid x:Name="EditDataGrid" Grid.Row="0" AutoGenerateColumns="True" 
                  CanUserAddRows="True" CanUserDeleteRows="True" 
                  IsReadOnly="False" 
                  SelectionUnit="CellOrRowHeader" 
                  SelectedCellsChanged="EditDataGrid_SelectedCellsChanged"
                  FontFamily="Consolas" FontSize="12">
            <!-- Контекстное меню для вставки строк и столбцов -->
            <DataGrid.ContextMenu>
                <ContextMenu>
                    <MenuItem Header="Вставить строку выше" Click="InsertRowAbove_Click"/>
                    <MenuItem Header="Вставить строку ниже" Click="InsertRowBelow_Click"/>
                    <Separator/>
                    <MenuItem Header="Вставить столбец слева" Click="InsertColumnLeft_Click"/>
                    <MenuItem Header="Вставить столбец справа" Click="InsertColumnRight_Click"/>
                </ContextMenu>
            </DataGrid.ContextMenu>
        </DataGrid>

        <!-- Панель кнопок ниже DataGrid -->
        <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,10,0,0">
            <!-- Кнопки Undo и Redo -->
            <Button x:Name="UndoButton" Content="Отменить" Width="100" Height="30" Margin="0,0,10,0" Click="UndoButton_Click" IsEnabled="False"/>
            <Button x:Name="RedoButton" Content="Повторить" Width="100" Height="30" Margin="0,0,10,0" Click="RedoButton_Click" IsEnabled="False"/>

            <!-- Кнопка Применить -->
            <Button x:Name="ApplyButton" Content="Применить" Width="100" Height="30" Margin="0,0,10,0" Click="ApplyButton_Click"/>
            <Button x:Name="CancelButton" Content="Отмена" Width="100" Height="30" Margin="0,0,10,0" Click="CancelButton_Click"/>

            <!-- Элемент управления для выбора разделителя -->
            <ComboBox x:Name="DelimiterComboBox" Width="100" Height="30" SelectedIndex="0">
                <ComboBoxItem Content="Пробел"/>
                <ComboBoxItem Content="Табуляция"/>
                <ComboBoxItem Content="Запятая"/>
            </ComboBox>
            <Button Content="Экспортировать файл" Width="150" Height="30" Margin="10,0,0,0" Click="ExportButton_Click"/>
        </StackPanel>
    </Grid>
</Window>


// EditWindow.xaml.cs
using Microsoft.Win32;
using System;
using System.Data;
using System.Linq;
using System.IO;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Collections.Generic;
using System.Windows.Data;
using System.Threading.Tasks;

namespace TxtProcessorApp
{
    public partial class EditWindow : Window
    {
        private DataTable _originalDataTable;
        private DataTable _dataTableCopy;

        // Стек для операций Undo/Redo
        private readonly Stack<IUndoRedoAction> undoStack = new();
        private readonly Stack<IUndoRedoAction> redoStack = new();

        public EditWindow(DataTable dataTable)
        {
            InitializeComponent();

            // Создаём копию исходного DataTable
            _originalDataTable = dataTable;
            _dataTableCopy = dataTable.Copy();

            EditDataGrid.ItemsSource = _dataTableCopy.DefaultView;

            // Разрешаем добавление и удаление строк через интерфейс DataGrid
            EditDataGrid.CanUserAddRows = true;
            EditDataGrid.CanUserDeleteRows = true;

            // Подписываемся на события изменений данных
            _dataTableCopy.RowChanged += DataTable_RowChanged;
            _dataTableCopy.RowDeleting += DataTable_RowDeleting; // Используем RowDeleting вместо RowDeleted
            _dataTableCopy.ColumnChanged += DataTable_ColumnChanged;

            UpdateUndoRedoButtons();
        }

        #region Обработчики изменений данных

        private void DataTable_RowChanged(object sender, DataRowChangeEventArgs e)
        {
            if (e.Action == DataRowAction.Add)
            {
                var action = new DataRowUndoRedoAction(e.Row, DataRowAction.Add, _dataTableCopy);
                undoStack.Push(action);
                redoStack.Clear();
                UpdateUndoRedoButtons();
            }
        }

        private void DataTable_RowDeleting(object sender, DataRowChangeEventArgs e)
        {
            var action = new DataRowUndoRedoAction(e.Row, DataRowAction.Delete, _dataTableCopy);
            undoStack.Push(action);
            redoStack.Clear();
            UpdateUndoRedoButtons();
        }

        private void DataTable_ColumnChanged(object sender, DataColumnChangeEventArgs e)
        {
            object oldValue;

            if (e.Row.HasVersion(DataRowVersion.Original))
            {
                oldValue = e.Row[e.Column, DataRowVersion.Original];
            }
            else if (e.Row.HasVersion(DataRowVersion.Current))
            {
                oldValue = e.Row[e.Column, DataRowVersion.Current];
            }
            else
            {
                oldValue = null; // Or set a default value if appropriate
            }

            var action = new DataCellChangeAction(e.Row, e.Column, oldValue, e.ProposedValue);
            undoStack.Push(action);
            redoStack.Clear();
            UpdateUndoRedoButtons();
        }


        #endregion

        #region Вставка строк и столбцов

        // Вставка строки выше выбранной
        private void InsertRowAbove_Click(object sender, RoutedEventArgs e)
        {
            int rowIndex = GetSelectedRowIndex();
            if (rowIndex != -1)
            {
                var newRow = _dataTableCopy.NewRow();
                _dataTableCopy.Rows.InsertAt(newRow, rowIndex);
                var action = new DataRowUndoRedoAction(newRow, DataRowAction.Add, _dataTableCopy);
                undoStack.Push(action);
                redoStack.Clear();
                UpdateUndoRedoButtons();
            }
            else
            {
                var newRow = _dataTableCopy.NewRow();
                _dataTableCopy.Rows.Add(newRow);
                var action = new DataRowUndoRedoAction(newRow, DataRowAction.Add, _dataTableCopy);
                undoStack.Push(action);
                redoStack.Clear();
                UpdateUndoRedoButtons();
            }
            RefreshDataGrid();
        }

        // Вставка строки ниже выбранной
        private void InsertRowBelow_Click(object sender, RoutedEventArgs e)
        {
            int rowIndex = GetSelectedRowIndex();
            if (rowIndex != -1)
            {
                var newRow = _dataTableCopy.NewRow();
                _dataTableCopy.Rows.InsertAt(newRow, rowIndex + 1);
                var action = new DataRowUndoRedoAction(newRow, DataRowAction.Add, _dataTableCopy);
                undoStack.Push(action);
                redoStack.Clear();
                UpdateUndoRedoButtons();
            }
            else
            {
                var newRow = _dataTableCopy.NewRow();
                _dataTableCopy.Rows.Add(newRow);
                var action = new DataRowUndoRedoAction(newRow, DataRowAction.Add, _dataTableCopy);
                undoStack.Push(action);
                redoStack.Clear();
                UpdateUndoRedoButtons();
            }
            RefreshDataGrid();
        }

        // Вставка столбца слева от выбранного
        private void InsertColumnLeft_Click(object sender, RoutedEventArgs e)
        {
            int columnIndex = GetSelectedColumnIndex();
            InsertNewColumn(columnIndex);
            RefreshDataGrid();
        }

        // Вставка столбца справа от выбранного
        private void InsertColumnRight_Click(object sender, RoutedEventArgs e)
        {
            int columnIndex = GetSelectedColumnIndex();
            InsertNewColumn(columnIndex + 1);
            RefreshDataGrid();
        }

        private void InsertNewColumn(int index)
        {
            string columnName = GenerateUniqueColumnName("NewColumn");
            DataColumn newColumn = new DataColumn(columnName, typeof(string));
            _dataTableCopy.Columns.Add(newColumn);
            newColumn.SetOrdinal(Math.Min(index, _dataTableCopy.Columns.Count - 1));

            foreach (DataRow row in _dataTableCopy.Rows)
            {
                row[newColumn] = string.Empty;
            }

            var action = new DataColumnAction(newColumn, DataColumnActionType.Add, _dataTableCopy);
            undoStack.Push(action);
            redoStack.Clear();
            UpdateUndoRedoButtons();
        }

        private string GenerateUniqueColumnName(string baseName)
        {
            int suffix = 1;
            string newName = baseName;
            while (_dataTableCopy.Columns.Contains(newName))
            {
                newName = $"{baseName}_{suffix}";
                suffix++;
            }
            return newName;
        }

        private int GetSelectedRowIndex()
        {
            if (EditDataGrid.SelectedItem is DataRowView selectedRow)
            {
                return _dataTableCopy.Rows.IndexOf(selectedRow.Row);
            }
            return -1;
        }

        private int GetSelectedColumnIndex()
        {
            if (EditDataGrid.SelectedCells.Count > 0)
            {
                var selectedCell = EditDataGrid.SelectedCells[0];
                var column = selectedCell.Column;
                if (column != null)
                {
                    return column.DisplayIndex;
                }
            }
            return -1;
        }

        #endregion

        private void RefreshDataGrid()
        {
            var view = CollectionViewSource.GetDefaultView(EditDataGrid.ItemsSource);
            if (view != null)
            {
                view.Refresh();
            }
        }

        private void EditDataGrid_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
        {
            // Дополнительная логика при изменении выбора (если требуется)
        }

        #region Экспорт данных

        private async void ExportButton_Click(object sender, RoutedEventArgs e)
        {
            string delimiter = GetSelectedDelimiter();

            SaveFileDialog sfd = new SaveFileDialog
            {
                Filter = delimiter == "," ? "CSV файлы (*.csv)|*.csv" : "Текстовые файлы (*.txt)|*.txt",
                FileName = delimiter == "," ? "EditedCombinedFile.csv" : "EditedCombinedFile.txt"
            };

            if (sfd.ShowDialog() == true)
            {
                try
                {
                    using (StreamWriter sw = new StreamWriter(sfd.FileName, false, Encoding.UTF8))
                    {
                        await ExportWithDelimiterAsync(sw, delimiter);
                    }

                    MessageBox.Show($"Файл успешно экспортирован по адресу:\n{sfd.FileName}", "Успех", MessageBoxButton.OK, MessageBoxImage.Information);
                }
                catch (Exception ex)
                {
                    MessageBox.Show($"Ошибка при сохранении файла:\n{ex.Message}", "Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }

        private async Task ExportWithDelimiterAsync(StreamWriter sw, string delimiter)
        {
            string[] headers = _dataTableCopy.Columns
                .Cast<DataColumn>()
                .Select(column => $"\"{column.ColumnName}\"")
                .ToArray();
            await sw.WriteLineAsync(string.Join(delimiter, headers));

            foreach (DataRow row in _dataTableCopy.Rows)
            {
                string[] fields = row.ItemArray.Select(field =>
                {
                    string fieldStr = field?.ToString() ?? string.Empty;
                    if (fieldStr.Contains(delimiter) || fieldStr.Contains("\""))
                    {
                        fieldStr = $"\"{fieldStr.Replace("\"", "\"\"")}\"";
                    }
                    return fieldStr;
                }).ToArray();
                await sw.WriteLineAsync(string.Join(delimiter, fields));
            }
        }

        private string GetSelectedDelimiter()
        {
            switch (DelimiterComboBox.SelectedIndex)
            {
                case 0:
                    return " "; // Пробел
                case 1:
                    return "\t"; // Табуляция
                case 2:
                    return ","; // Запятая
                default:
                    return "\t"; // По умолчанию табуляция
            }
        }

        #endregion

        #region Undo/Redo Functionality

        private void UndoButton_Click(object sender, RoutedEventArgs e)
        {
            if (undoStack.Any())
            {
                var action = undoStack.Pop();
                action.Undo();
                redoStack.Push(action);
                UpdateUndoRedoButtons();
                RefreshDataGrid();
            }
        }


        private void RedoButton_Click(object sender, RoutedEventArgs e)
        {
            if (redoStack.Any())
            {
                var action = redoStack.Pop();
                action.Redo();
                undoStack.Push(action);
                UpdateUndoRedoButtons();
            }
        }

        private void UpdateUndoRedoButtons()
        {
            UndoButton.IsEnabled = undoStack.Any();
            RedoButton.IsEnabled = redoStack.Any();
        }

        #endregion

        #region Кнопки Применить и Отмена

        private void ApplyButton_Click(object sender, RoutedEventArgs e)
        {
            // Применяем изменения к исходному DataTable
            _originalDataTable.Clear();
            foreach (DataRow row in _dataTableCopy.Rows)
            {
                _originalDataTable.ImportRow(row);
            }
            _originalDataTable.AcceptChanges();

            // Закрываем окно после применения изменений
            this.DialogResult = true;
            this.Close();
        }

        private void CancelButton_Click(object sender, RoutedEventArgs e)
        {
            // Закрываем окно без сохранения изменений
            this.DialogResult = false;
            this.Close();
        }

        #endregion

        #region Undo/Redo Actions

        private interface IUndoRedoAction
        {
            void Undo();
            void Redo();
        }

        private class DataRowUndoRedoAction : IUndoRedoAction
        {
            private readonly DataTable _table;
            private readonly DataRowAction _actionType;
            private readonly object[] _data;
            private readonly int _rowIndex;

            public DataRowUndoRedoAction(DataRow row, DataRowAction actionType, DataTable table)
            {
                _table = table;
                _actionType = actionType;
                _data = row.ItemArray.Clone() as object[];
                _rowIndex = _table.Rows.IndexOf(row);
            }

            public void Undo()
            {
                if (_actionType == DataRowAction.Add)
                {
                    // Undo adding the row: remove it
                    _table.Rows.RemoveAt(_rowIndex);
                }
                else if (_actionType == DataRowAction.Delete)
                {
                    // Undo deleting the row: insert it back
                    DataRow newRow = _table.NewRow();
                    newRow.ItemArray = _data;
                    _table.Rows.InsertAt(newRow, _rowIndex);
                }
            }

            public void Redo()
            {
                if (_actionType == DataRowAction.Add)
                {
                    // Redo adding the row: insert it back
                    DataRow newRow = _table.NewRow();
                    newRow.ItemArray = _data;
                    _table.Rows.InsertAt(newRow, _rowIndex);
                }
                else if (_actionType == DataRowAction.Delete)
                {
                    // Redo deleting the row: remove it
                    _table.Rows.RemoveAt(_rowIndex);
                }
            }
        }

        private class DataCellChangeAction : IUndoRedoAction
        {
            private readonly DataRow _row;
            private readonly DataColumn _column;
            private readonly object _oldValue;
            private readonly object _newValue;

            public DataCellChangeAction(DataRow row, DataColumn column, object oldValue, object newValue)
            {
                _row = row;
                _column = column;
                _oldValue = oldValue;
                _newValue = newValue;
            }

            public void Undo()
            {
                _row[_column] = _oldValue ?? DBNull.Value;
            }

            public void Redo()
            {
                _row[_column] = _newValue ?? DBNull.Value;
            }
        }

        private enum DataColumnActionType
        {
            Add,
            Remove
        }

        private class DataColumnAction : IUndoRedoAction
        {
            private readonly DataColumn _column;
            private readonly DataColumnActionType _actionType;
            private readonly DataTable _table;

            public DataColumnAction(DataColumn column, DataColumnActionType actionType, DataTable table)
            {
                _column = column;
                _actionType = actionType;
                _table = table;
            }

            public void Undo()
            {
                if (_actionType == DataColumnActionType.Add)
                {
                    _table.Columns.Remove(_column);
                }
                else if (_actionType == DataColumnActionType.Remove)
                {
                    _table.Columns.Add(_column);
                }
            }

            public void Redo()
            {
                if (_actionType == DataColumnActionType.Add)
                {
                    _table.Columns.Add(_column);
                }
                else if (_actionType == DataColumnActionType.Remove)
                {
                    _table.Columns.Remove(_column);
                }
            }
        }

        #endregion
    }
}


<!-- InputDialog.xaml -->
<Window x:Class="TxtProcessorApp.InputDialog"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Ввод" Height="150" Width="400" WindowStartupLocation="CenterOwner" ResizeMode="NoResize">
    <Grid Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <TextBlock x:Name="PromptTextBlock" Grid.Row="0" Text="Prompt:" Margin="0,0,0,10" TextWrapping="Wrap"/>

        <TextBox x:Name="ResponseTextBox" Grid.Row="1" Height="25"/>

        <StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,10,0,0">
            <Button Content="OK" Width="75" Margin="0,0,10,0" IsDefault="True" Click="OK_Click"/>
            <Button Content="Отмена" Width="75" IsCancel="True"/>
        </StackPanel>
    </Grid>
</Window>

// InputDialog.xaml.cs
using System.Windows;

namespace TxtProcessorApp
{
    /// <summary>
    /// Логика взаимодействия для InputDialog.xaml
    /// </summary>
    public partial class InputDialog : Window
    {
        public string ResponseText { get; private set; }

        public InputDialog(string question, string defaultAnswer = "")
        {
            InitializeComponent();
            PromptTextBlock.Text = question;
            ResponseTextBox.Text = defaultAnswer;
            ResponseTextBox.SelectAll();
            ResponseTextBox.Focus();
        }

        private void OK_Click(object sender, RoutedEventArgs e)
        {
            ResponseText = ResponseTextBox.Text;
            DialogResult = true;
        }
    }
}