СДЕЛАЙ ЧТОБЫ МОЖНО БЫЛО ВЫБРАТЬ НЕСКОЛЬКО ФАЙЛОВ И В ПРЕДПРОСМОТРЕ МОЖНО БЫЛО ПЕРЕКЛЮЧАТЬ ЧЕРЕЗ ВЫПАДАЮЩИЙ СПИСОК МЕЖДУ НИМИ, КОЛИЧЕСТВО ЛИНИЙ КОТОРЫЕ ПРОПУСКАЮТСЯ ДЛЯ КАЖДОГО ФАЙЛА РАЗНЫЕ
КАЖДЫЙ ФАЙЛ ДОЛЖЕН НАРИСОВАТЬ 
ТО ЕСТЬ ЕСЛИ 2 ФАЙЛА ТО ДВЕ КАРТЫ РЯДОМ

<Window x:Class="ContourMapVisualizer.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Contour Map Visualizer" Height="600" Width="788" Icon="/Screenshot 2024-11-06 152658.png" ResizeMode="CanResizeWithGrip" WindowStyle="ThreeDBorderWindow">
    <Window.ContextMenu>
        <ContextMenu/>
    </Window.ContextMenu>
    <Window.Effect>
        <DropShadowEffect/>
    </Window.Effect>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="30"/>
        </Grid.RowDefinitions>

        <StackPanel Grid.Row="0" Orientation="Horizontal" Margin="10">
            <Button Content="Open DAT File" Click="OpenFile_Click" Width="100" Margin="0,0,10,0"/>
            <ComboBox x:Name="PaletteSelector" Width="120" Margin="0,0,10,0" SelectionChanged="PaletteSelector_SelectionChanged">
                <ComboBoxItem Content="Rainbow"/>
                <ComboBoxItem Content="Hot"/>
                <ComboBoxItem Content="Cool"/>
                <ComboBoxItem Content="Gray Scale"/>
            </ComboBox>
            <TextBlock Text="Min:" VerticalAlignment="Center" Margin="0,0,5,0"/>
            <TextBox x:Name="MinValueBox" Width="60" Margin="0,0,10,0" TextChanged="MinValue_TextChanged"/>
            <TextBlock Text="Max:" VerticalAlignment="Center" Margin="0,0,5,0"/>
            <TextBox x:Name="MaxValueBox" Width="60" Margin="0,0,10,0" TextChanged="MaxValue_TextChanged"/>
            <Button Content="Auto Range" Click="AutoRange_Click" Width="80" Margin="0,0,10,0"/>
            <CheckBox x:Name="ReverseScale" Content="Reverse Scale" Checked="ReverseScale_CheckedChanged" 
                     Unchecked="ReverseScale_CheckedChanged" VerticalAlignment="Center"/>
        </StackPanel>

        <Image x:Name="ContourImage" Grid.Row="1" Margin="10"/>

        <StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Center" 
                    Visibility="Collapsed" x:Name="ColorScalePanel">
            <TextBlock x:Name="MinScaleLabel" Text="Min" Margin="5,0" VerticalAlignment="Center"/>
            <Image x:Name="ColorScale" Height="20" Width="256"/>
            <TextBlock x:Name="MaxScaleLabel" Text="Max" Margin="5,0" VerticalAlignment="Center"/>
        </StackPanel>
    </Grid>
</Window>

using Microsoft.Win32;
using System;
using System.IO;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace ContourMapVisualizer
{
    public partial class MainWindow : Window
    {
        private double[,] data;
        private double minValue = double.MaxValue;
        private double maxValue = double.MinValue;
        private string currentPalette = "Rainbow";
        private bool isScaleReversed = false;

        public MainWindow()
        {
            InitializeComponent();
            PaletteSelector.SelectedIndex = 0;
        }

        private void OpenFile_Click(object sender, RoutedEventArgs e)
        {
            var openFileDialog = new OpenFileDialog
            {
                Filter = "DAT files (*.dat)|*.dat|All files (*.*)|*.*"
            };

            if (openFileDialog.ShowDialog() == true)
            {
                var previewWindow = new FilePreviewWindow(openFileDialog.FileName);
                previewWindow.Owner = this;



                if (previewWindow.ShowDialog() == true)
                {
                    try
                    {
                        LoadDataFromFile(openFileDialog.FileName, previewWindow.SkipLines);

                        // Проверка успешной загрузки данных
                        MessageBox.Show("Data loaded successfully.");

                        UpdateContourMap();
                        UpdateColorScale();
                        ColorScalePanel.Visibility = Visibility.Visible;
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show($"Error processing file: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                    }
                }
            }
        }

        private void LoadDataFromFile(string filename, int skipLines)
        {
            var allLines = File.ReadAllLines(filename);
            var lines = allLines.Skip(skipLines).ToArray();

            if (lines.Length == 0)
            {
                throw new Exception("No data to display after skipping lines");
            }

            int rows = lines.Length;
            int cols = lines[0].Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries).Length;

            data = new double[rows, cols];
            minValue = double.MaxValue;
            maxValue = double.MinValue;

            for (int i = 0; i < rows; i++)
            {
                var values = lines[i].Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
                if (values.Length != cols)
                {
                    throw new Exception($"Invalid data format at line {i + skipLines + 1}");
                }

                for (int j = 0; j < cols; j++)
                {
                    if (!double.TryParse(values[j], out double value))
                    {
                        throw new Exception($"Invalid number at line {i + skipLines + 1}, position {j + 1}");
                    }
                    data[i, j] = value;
                    minValue = Math.Min(minValue, data[i, j]);
                    maxValue = Math.Max(maxValue, data[i, j]);
                }
            }



            MinValueBox.Text = minValue.ToString("F2");
            MaxValueBox.Text = maxValue.ToString("F2");
            MinScaleLabel.Text = minValue.ToString("F2");
            MaxScaleLabel.Text = maxValue.ToString("F2");
            MinValueBox.Text = minValue.ToString("F2");
            MaxValueBox.Text = maxValue.ToString("F2");
        }
        private void UpdateContourMap()
        {
            if (data == null) return;
            if (string.IsNullOrEmpty(MinValueBox.Text) || string.IsNullOrEmpty(MaxValueBox.Text)) return;

            if (!double.TryParse(MinValueBox.Text, out double minVal) || !double.TryParse(MaxValueBox.Text, out double maxVal))
            {
                MessageBox.Show("Please enter correct numerical values for min and max",
                              "Error", MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            int width = data.GetLength(1);
            int height = data.GetLength(0);

            var bitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Bgr32, null);
            int stride = bitmap.BackBufferStride;
            int[] pixels = new int[width * height];

            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    double normalizedValue = (data[i, j] - minVal) / (maxVal - minVal);
                    normalizedValue = Math.Max(0, Math.Min(1, normalizedValue));

                    if (isScaleReversed)
                    {
                        normalizedValue = 1 - normalizedValue;
                    }

                    Color color = GetColorFromPalette(normalizedValue);
                    pixels[i * width + j] = (color.A << 24) | (color.R << 16) | (color.G << 8) | color.B;
                }
            }

            bitmap.WritePixels(new Int32Rect(0, 0, width, height), pixels, stride, 0);
            ContourImage.Source = bitmap;
}
        private void DumpDataSample()
        {
            if (data == null) return;

            int sampleSize = Math.Min(5, data.GetLength(0));
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("Data sample (first 5 rows x 5 columns):");

            for (int i = 0; i < sampleSize; i++)
            {
                for (int j = 0; j < Math.Min(5, data.GetLength(1)); j++)
                {
                    sb.Append($"{data[i, j]:F2}\t");
                }
                sb.AppendLine();
            }

       
        }
        private void UpdateColorScale()
        {
            if (double.TryParse(MinValueBox.Text, out double minVal) && double.TryParse(MaxValueBox.Text, out double maxVal))
            {
                MinScaleLabel.Text = minVal.ToString("F2");
                MaxScaleLabel.Text = maxVal.ToString("F2");

                int scaleWidth = 256;
                int scaleHeight = 20;

                var bitmap = new WriteableBitmap(scaleWidth, scaleHeight, 96, 96, PixelFormats.Bgr32, null);
                int stride = bitmap.BackBufferStride;
                int[] pixels = new int[scaleWidth * scaleHeight];

                for (int x = 0; x < scaleWidth; x++)
                {
                    double value = (double)x / (scaleWidth - 1);
                    if (isScaleReversed)
                    {
                        value = 1 - value;
                    }
                    Color color = GetColorFromPalette(value);

                    for (int y = 0; y < scaleHeight; y++)
                    {
                        pixels[y * scaleWidth + x] = (color.A << 24) | (color.R << 16) | (color.G << 8) | color.B;
                    }
                }

                bitmap.WritePixels(new Int32Rect(0, 0, scaleWidth, scaleHeight), pixels, stride, 0);
                ColorScale.Source = bitmap;
            }
        }
        private void ReverseScale_CheckedChanged(object sender, RoutedEventArgs e)
        {
            isScaleReversed = ReverseScale.IsChecked ?? false;
            UpdateContourMap();
            UpdateColorScale();
        }

        private Color GetColorFromPalette(double value)
        {
            return currentPalette switch
            {
                "Rainbow" => GetRainbowColor(value),
                "Hot" => GetHotColor(value),
                "Cool" => GetCoolColor(value),
                "Gray Scale" => GetGrayScaleColor(value),
                _ => GetRainbowColor(value),
            };
        }

        private Color GetGrayScaleColor(double value)
        {
            byte intensity = (byte)(value * 255);
            return Color.FromRgb(intensity, intensity, intensity);
        }

        private Color GetRainbowColor(double value)
        {
            double hue = (1 - value) * 240;
            return ColorFromHSV(hue, 1, 1);
        }

        private Color GetHotColor(double value)
        {
            if (value < 0.33)
                return Color.FromRgb((byte)(value * 3 * 255), 0, 0);
            else if (value < 0.66)
                return Color.FromRgb(255, (byte)((value - 0.33) * 3 * 255), 0);
            else
                return Color.FromRgb(255, 255, (byte)((value - 0.66) * 3 * 255));
        }

        private Color GetCoolColor(double value)
        {
            return Color.FromRgb((byte)(value * 255), (byte)((1 - value) * 255), 255);
        }

        private Color ColorFromHSV(double hue, double saturation, double value)
        {
            int hi = (int)(hue / 60) % 6;
            double f = hue / 60 - hi;
            byte v = (byte)(value * 255);
            byte p = (byte)(value * (1 - saturation) * 255);
            byte q = (byte)(value * (1 - f * saturation) * 255);
            byte t = (byte)(value * (1 - (1 - f) * saturation) * 255);

            return hi switch
            {
                0 => Color.FromRgb(v, t, p),
                1 => Color.FromRgb(q, v, p),
                2 => Color.FromRgb(p, v, t),
                3 => Color.FromRgb(p, q, v),
                4 => Color.FromRgb(t, p, v),
                _ => Color.FromRgb(v, p, q),
            };
        }

        private void PaletteSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (PaletteSelector.SelectedItem != null)
            {
                currentPalette = (PaletteSelector.SelectedItem as ComboBoxItem).Content.ToString();
                UpdateContourMap();
                UpdateColorScale();
            }
        }

        private void MinValue_TextChanged(object sender, TextChangedEventArgs e)
        {
            UpdateContourMap();
            UpdateColorScale();
        }

        private void MaxValue_TextChanged(object sender, TextChangedEventArgs e)
        {
            UpdateContourMap();
            UpdateColorScale();
        }

        private void AutoRange_Click(object sender, RoutedEventArgs e)
        {
            MinValueBox.Text = minValue.ToString("F2");
            MaxValueBox.Text = maxValue.ToString("F2");
        }
    }
}


<Window x:Class="ContourMapVisualizer.FilePreviewWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="File Preview" Height="450" Width="600" WindowStartupLocation="CenterOwner">
    <Grid Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <TextBox x:Name="PreviewTextBox" Grid.Row="0" 
                 IsReadOnly="True" 
                 VerticalScrollBarVisibility="Auto" 
                 HorizontalScrollBarVisibility="Auto"
                 FontFamily="Consolas"/>

        <StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,10">
            <Label Content="Skip lines:"/>
            <TextBox x:Name="SkipLinesTextBox" Width="60" Text="0"/>
            <Label x:Name="TotalLinesLabel" Content="Total lines: 0" Margin="20,0,0,0"/>
        </StackPanel>

        <StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right">
            <Button Content="OK" Click="ConfirmButton_Click" Width="75" Margin="0,0,10,0"/>
            <Button Content="Cancel" Click="CancelButton_Click" Width="75"/>
        </StackPanel>
    </Grid>
</Window>

using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;

namespace ContourMapVisualizer
{
    public partial class FilePreviewWindow : Window
    {
        private string[] fileLines;
        public int SkipLines { get; private set; }

        public FilePreviewWindow(string filePath)
        {
            InitializeComponent();
            LoadFilePreview(filePath);
        }

        private void LoadFilePreview(string filePath)
        {
            try
            {
                fileLines = File.ReadAllLines(filePath);
                UpdatePreview();
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Error loading file: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                Close();
            }
        }

        private void UpdatePreview()
        {
            PreviewTextBox.Clear();
            int linesToShow = Math.Min(fileLines.Length, 10); // Показываем первые 10 строк
            for (int i = 0; i < linesToShow; i++)
            {
                PreviewTextBox.AppendText($"Line {i + 1}: {fileLines[i]}\n");
            }

            if (fileLines.Length > 10)
            {
                PreviewTextBox.AppendText("...\n");
            }

            TotalLinesLabel.Content = $"Total lines: {fileLines.Length}";
        }

        private void ConfirmButton_Click(object sender, RoutedEventArgs e)
        {
            if (int.TryParse(SkipLinesTextBox.Text, out int skipLines))
            {
                if (skipLines >= 0 && skipLines < fileLines.Length)
                {
                    SkipLines = skipLines;
                    this.DialogResult = true; // Устанавливаем встроенное свойство
                    Close();
                }
                else
                {
                    MessageBox.Show("Invalid number of lines to skip", "Error", MessageBoxButton.OK, MessageBoxImage.Warning);
                }
            }
            else
            {
                MessageBox.Show("Please enter a valid number", "Error", MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        private void CancelButton_Click(object sender, RoutedEventArgs e)
        {
            this.DialogResult = false; // Устанавливаем встроенное свойство
            Close();
        }
    }
}