using System; using System.Collections.Generic; using System.IO; using Microsoft.Data.Sqlite; namespace BoxPacker3D { /// /// Persistent template of an item that can be re-used across packing sessions. /// Stored in an on-disk SQLite database. /// public class ItemTemplate { public int Id { get; set; } public string Name { get; set; } = ""; public double Width { get; set; } public double Height { get; set; } public double Depth { get; set; } public override string ToString() => $"{Name} ({Width}×{Height}×{Depth} mm)"; } /// /// Simple SQLite-backed repository for s. /// Database schema is created on first use. /// public class ItemDatabase { private readonly string _connectionString; public ItemDatabase(string dbPath) { // Ensure directory exists var dir = Path.GetDirectoryName(dbPath); if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir)) Directory.CreateDirectory(dir); _connectionString = new SqliteConnectionStringBuilder { DataSource = dbPath, Mode = SqliteOpenMode.ReadWriteCreate }.ToString(); EnsureSchema(); } private void EnsureSchema() { using var conn = Open(); using var cmd = conn.CreateCommand(); cmd.CommandText = @" CREATE TABLE IF NOT EXISTS items ( Id INTEGER PRIMARY KEY AUTOINCREMENT, Name TEXT NOT NULL UNIQUE COLLATE NOCASE, Width REAL NOT NULL, Height REAL NOT NULL, Depth REAL NOT NULL );"; cmd.ExecuteNonQuery(); } private SqliteConnection Open() { var conn = new SqliteConnection(_connectionString); conn.Open(); return conn; } /// Returns all templates ordered alphabetically by name. public List GetAll() { var list = new List(); using var conn = Open(); using var cmd = conn.CreateCommand(); cmd.CommandText = "SELECT Id, Name, Width, Height, Depth FROM items ORDER BY Name COLLATE NOCASE;"; using var rdr = cmd.ExecuteReader(); while (rdr.Read()) list.Add(new ItemTemplate { Id = rdr.GetInt32(0), Name = rdr.GetString(1), Width = rdr.GetDouble(2), Height = rdr.GetDouble(3), Depth = rdr.GetDouble(4) }); return list; } /// Returns the template with the given name or null if none exists. public ItemTemplate? FindByName(string name) { using var conn = Open(); using var cmd = conn.CreateCommand(); cmd.CommandText = "SELECT Id, Name, Width, Height, Depth FROM items WHERE Name = $n COLLATE NOCASE LIMIT 1;"; cmd.Parameters.AddWithValue("$n", name); using var rdr = cmd.ExecuteReader(); if (rdr.Read()) return new ItemTemplate { Id = rdr.GetInt32(0), Name = rdr.GetString(1), Width = rdr.GetDouble(2), Height = rdr.GetDouble(3), Depth = rdr.GetDouble(4) }; return null; } /// /// Inserts a new template (Id == 0) or updates the existing row by Id (Id > 0). /// Using Id rather than Name for the update key means renaming an existing item /// during edit correctly updates that same row instead of leaving the old-named /// row behind and inserting a new one. /// public void Upsert(ItemTemplate item) { using var conn = Open(); using var cmd = conn.CreateCommand(); if (item.Id > 0) { cmd.CommandText = @" UPDATE items SET Name = $n, Width = $w, Height = $h, Depth = $d WHERE Id = $id;"; cmd.Parameters.AddWithValue("$id", item.Id); } else { cmd.CommandText = @" INSERT INTO items (Name, Width, Height, Depth) VALUES ($n, $w, $h, $d);"; } cmd.Parameters.AddWithValue("$n", item.Name); cmd.Parameters.AddWithValue("$w", item.Width); cmd.Parameters.AddWithValue("$h", item.Height); cmd.Parameters.AddWithValue("$d", item.Depth); cmd.ExecuteNonQuery(); } public void DeleteByName(string name) { using var conn = Open(); using var cmd = conn.CreateCommand(); cmd.CommandText = "DELETE FROM items WHERE Name = $n COLLATE NOCASE;"; cmd.Parameters.AddWithValue("$n", name); cmd.ExecuteNonQuery(); } /// /// Checks if a template with the same name or exact dimensions already exists. /// Returns the existing template if found, null otherwise. /// public ItemTemplate? FindDuplicate(ItemTemplate item) { using var conn = Open(); using var cmd = conn.CreateCommand(); // Check for same name (case-insensitive) cmd.CommandText = "SELECT Id, Name, Width, Height, Depth FROM items WHERE Name = $n COLLATE NOCASE LIMIT 1;"; cmd.Parameters.AddWithValue("$n", item.Name); using (var rdr = cmd.ExecuteReader()) { if (rdr.Read()) { return new ItemTemplate { Id = rdr.GetInt32(0), Name = rdr.GetString(1), Width = rdr.GetDouble(2), Height = rdr.GetDouble(3), Depth = rdr.GetDouble(4) }; } } // Check for same dimensions (exact match) cmd.CommandText = @" SELECT Id, Name, Width, Height, Depth FROM items WHERE Width = $w AND Height = $h AND Depth = $d LIMIT 1;"; cmd.Parameters.Clear(); cmd.Parameters.AddWithValue("$w", item.Width); cmd.Parameters.AddWithValue("$h", item.Height); cmd.Parameters.AddWithValue("$d", item.Depth); using (var rdr = cmd.ExecuteReader()) { if (rdr.Read()) { return new ItemTemplate { Id = rdr.GetInt32(0), Name = rdr.GetString(1), Width = rdr.GetDouble(2), Height = rdr.GetDouble(3), Depth = rdr.GetDouble(4) }; } } return null; } } }