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 or replaces a template (upsert by Name).
public void Upsert(ItemTemplate item)
{
using var conn = Open();
using var cmd = conn.CreateCommand();
cmd.CommandText = @"
INSERT INTO items (Name, Width, Height, Depth)
VALUES ($n, $w, $h, $d)
ON CONFLICT(Name) DO UPDATE SET
Width = excluded.Width,
Height = excluded.Height,
Depth = excluded.Depth;";
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;
}
}
}