using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Collections.ObjectModel; using System.ComponentModel; using System.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media.Imaging; using BackgroundBuilder.Models; using BackgroundBuilder.Repositories; using BackgroundBuilder.Services; using BackgroundBuilder.Utils; using ClosedXML.Excel; using Microsoft.Win32; namespace BackgroundBuilder.ViewModels { public class MainWindowViewModel : ObservableObject { private readonly IContatoRepository _repo; private readonly IImageService _imageService; private readonly ITaskbarService _taskbarService; public ObservableCollection RawContatos { get; } = []; public ObservableCollection Comando { get; } = []; public ObservableCollection Aniversarios { get; } = []; public ObservableCollection ContatosSemCMDFirstHalf { get; } = []; public ObservableCollection ContatosSemCMDSecondHalf { get; } = []; private Contato? _selectedContato; public Contato? SelectedContato{ get => _selectedContato; set { _selectedContato = value; OnPropertyChanged(); DeleteCommand.RaiseCanExecuteChanged(); } } private ObservableCollection? _selectedContatos; public ObservableCollection SelectedContatos { get => _selectedContatos ?? RawContatos; set { _selectedContatos = value; OnPropertyChanged(); UpdateCommand.RaiseCanExecuteChanged(); } } private string? _caminhoBGImage; public string? CaminhoBGImage{ get => _caminhoBGImage; set { _caminhoBGImage = value; OnPropertyChanged(); }} private BitmapImage? _backgroundImage; public BitmapImage? BackgroundImage{ get => _backgroundImage; private set { _backgroundImage = value; OnPropertyChanged(); }} private int _taskbarSize; public int TaskbarSize{ get => _taskbarSize; private set { _taskbarSize = value; OnPropertyChanged(); }} private string _orientation = "Horizontal"; public string Orientation{ get => _orientation; private set { _orientation = value; OnPropertyChanged(); }} public FrameworkElement? OverlayElement { get; set; } // Commands public RelayCommand DeleteCommand { get; } public RelayCommand AddCommand { get; } public RelayCommand SelectImageCommand { get; } public ICommand RefreshCommand { get; } public RelayCommand UpdateCommand { get; } public RelayCommand ExportImageCommand { get; } public RelayCommand ImportExcelCommand { get; } public MainWindowViewModel( IContatoRepository repo, IImageService imageService, ITaskbarService taskbarService) { _repo = repo; _imageService = imageService; _taskbarService = taskbarService; RefreshTaskbarInfo(); DeleteCommand = new RelayCommand(async _ => await DeleteAsync(), _ => SelectedContato != null); AddCommand = new RelayCommand(async _ => await AddContactAsync()); SelectImageCommand = new RelayCommand(async _ => await SelectBackground()); RefreshCommand = new RelayCommand(async _ => await LoadRawAsync()); UpdateCommand = new RelayCommand(async _ => await UpdateAsync(), _ => SelectedContatos != null); ExportImageCommand = new RelayCommand(async _ => await RenderImageAsync(), _ => BackgroundImage != null && OverlayElement != null); ImportExcelCommand = new RelayCommand(async _ => await ImportExcelAsync()); } public void RefreshTaskbarInfo() { TaskbarSize = _taskbarService.GetTaskbarSize(); Orientation = _taskbarService.IsHorizontal() ? "Horizontal" : "Vertical"; } private async Task DeleteAsync() { if (SelectedContato == null) return; var result = MessageBox.Show( "Delete selected record?", "Confirm Delete", MessageBoxButton.YesNo, MessageBoxImage.Question); if (result != MessageBoxResult.Yes) return; try { await _repo.DeleteAsync(SelectedContato.Ramal); RawContatos.Remove(SelectedContato); ApplyFilters(); } catch { MessageBox.Show( "Error deleting record.", "Delete Failed", MessageBoxButton.OK, MessageBoxImage.Error); } } private async Task AddContactAsync() { var novo = new Contato { Ramal = "", Nome = "" }; RawContatos.Add(novo); SelectedContato = novo; // Set the newly added contact as the selected one await Task.CompletedTask; // preserve async signature } private async Task SelectBackground() { var dlg = new OpenFileDialog { Filter = "Image Files|*.png;*.jpg;*.jpeg;*.bmp" }; if (dlg.ShowDialog() == true) { CaminhoBGImage = dlg.FileName; BackgroundImage = await _imageService.LoadAsync(dlg.FileName); if (!string.IsNullOrEmpty(CaminhoBGImage)) { ExportImageCommand.RaiseCanExecuteChanged(); } } } public async Task LoadRawAsync() { var all = await _repo.GetAllAsync(); RawContatos.Clear(); foreach (var c in all.Where(x => x.IsComando)) RawContatos.Add(c); foreach (var c in all.Where(x => !x.IsComando).OrderBy(x => x.Nome)) RawContatos.Add(c); ApplyFilters(); } private async Task RenderImageAsync() { var dlg = new SaveFileDialog { Filter = "PNG Image|*.png" }; if (dlg.ShowDialog() == true && OverlayElement is FrameworkElement overlay && BackgroundImage is BitmapImage bg) { await _imageService.SaveAsync(overlay, bg, dlg.FileName, dlg.FileName.Replace(".png", "_1.png")); MessageBox.Show( "Concluído!", "Confirm Save", MessageBoxButton.OK, MessageBoxImage.Information); //Open recently generated image if (File.Exists(dlg.FileName)) { System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo { FileName = dlg.FileName, UseShellExecute = true }); } } } public async Task UpdateAsync() { if (SelectedContatos == null) return; var result = MessageBox.Show( "Save selected records?", "Confirm Save", MessageBoxButton.YesNo, MessageBoxImage.Question); if (result != MessageBoxResult.Yes) return; foreach (var contato in SelectedContatos) { if (string.IsNullOrWhiteSpace(contato.Ramal) || string.IsNullOrWhiteSpace(contato.Nome)) { MessageBox.Show( "Ramal and Nome are required.", "Validation Error", MessageBoxButton.OK, MessageBoxImage.Warning); return; } try { await _repo.InsertUpdateAsync(contato); } catch { MessageBox.Show( "Error saving record.", "Save Failed", MessageBoxButton.OK, MessageBoxImage.Error); } } await LoadRawAsync(); MessageBox.Show( "Concluído!", "Confirm Save", MessageBoxButton.OK, MessageBoxImage.Information); } private void ApplyFilters() { Comando.Clear(); Aniversarios.Clear(); foreach (var item in RawContatos.Where(x => x.IsComando)) Comando.Add(item); foreach (var item in RawContatos.Where(x => !x.IsComando).OrderBy(x => (x.Aniversario ?? DateTime.MinValue).Day).OrderBy(x => (x.Aniversario ?? DateTime.MinValue).Month)) Aniversarios.Add(item); LoadContatosSemCMD(RawContatos.Where(x => !x.IsComando).OrderBy(x => x.Nome)); } private void LoadContatosSemCMD(IEnumerable contatos) { ContatosSemCMDFirstHalf.Clear(); ContatosSemCMDSecondHalf.Clear(); var list = contatos.ToList(); int half = (list.Count + 1) / 2; for (int i = 0; i < list.Count; i++) { if (i < half) ContatosSemCMDFirstHalf.Add(list[i]); else ContatosSemCMDSecondHalf.Add(list[i]); } } private async Task ImportExcelAsync() { var openFileDialog = new OpenFileDialog { Filter = "Excel Files|*.xlsx;*.xls" }; if (openFileDialog.ShowDialog() != true) return; var filePath = openFileDialog.FileName; var ramais = new List(); using (var workbook = new XLWorkbook(filePath)) { var ws = workbook.Worksheets.First(); var table = ws.Cell("B2").CurrentRegion; var nomeCol = table.Row(1).Search("Nome").First().Address.ColumnNumber; foreach (var row in table.Rows().Skip(1)) // Skip header { var contato = new Contato { Ramal = row.Cell(4).GetString(), Nome = row.Cell(2).GetString(), Email = row.Cell(3).GetString(), Area = row.Cell(5).GetString(), IsComando = false }; ramais.Add(contato); } table = ws.Cell("H2").CurrentRegion; foreach (var row in table.Rows().Skip(1)) // Skip header { var name = row.Cell(1).GetString(); var match = ramais.Find(x => x.Nome == name); if (match is not null) { match.Aniversario = row.Cell(2).GetDateTime(); } } } foreach (var contato in ramais) { await _repo.InsertUpdateAsync(contato); } await LoadRawAsync(); // Refresh UI MessageBox.Show( "Concluído!", "Confirm Save", MessageBoxButton.OK, MessageBoxImage.Information); } } }