236 lines
8.4 KiB
C#
236 lines
8.4 KiB
C#
using System;
|
|
using CarManagerV3.Util;
|
|
|
|
namespace CarManagerV3
|
|
{
|
|
/// <summary>
|
|
/// Class <c>Car</c> represents a car with various attributes such as make, model, year, color, mileage, and price.
|
|
/// </summary>
|
|
public class Car
|
|
{
|
|
private string id;
|
|
private string make;
|
|
private string model;
|
|
private int year;
|
|
private string color;
|
|
private int mileage;
|
|
private decimal price;
|
|
private int order;
|
|
|
|
public string Id { get => id; }
|
|
|
|
public string Make
|
|
{
|
|
get => make;
|
|
set
|
|
{
|
|
if (string.IsNullOrWhiteSpace(value)) throw new ArgumentException("Make cannot be empty.");
|
|
make = value;
|
|
}
|
|
}
|
|
|
|
public string Model
|
|
{
|
|
get => model;
|
|
set
|
|
{
|
|
if (string.IsNullOrWhiteSpace(value)) throw new ArgumentException("Model cannot be empty.");
|
|
model = value;
|
|
}
|
|
}
|
|
|
|
public int Year
|
|
{
|
|
get => year;
|
|
set
|
|
{
|
|
if (value < 1886 || value > DateTime.Now.Year + 1) throw new ArgumentException("Year must be between 1886 and next year.");
|
|
year = value;
|
|
}
|
|
}
|
|
|
|
public string Color
|
|
{
|
|
get => color;
|
|
set
|
|
{
|
|
if (string.IsNullOrWhiteSpace(value)) throw new ArgumentException("Color cannot be empty.");
|
|
color = value;
|
|
}
|
|
}
|
|
|
|
public int Mileage
|
|
{
|
|
get => mileage;
|
|
set
|
|
{
|
|
if (value < 0) throw new ArgumentException("Mileage cannot be negative.");
|
|
mileage = value;
|
|
}
|
|
}
|
|
|
|
public decimal Price
|
|
{
|
|
get => price;
|
|
set
|
|
{
|
|
if (value < 0) throw new ArgumentException("Price cannot be negative.");
|
|
price = value;
|
|
}
|
|
}
|
|
|
|
public int Order { get => order; set => order = value; }
|
|
|
|
public int Age { get => DateTime.Now.Year - year; }
|
|
public string AgeString
|
|
{
|
|
get
|
|
{
|
|
int age = this.Age;
|
|
if (age == 0) return "New";
|
|
else if (age == 1) return "1 year";
|
|
else if (age < 0) return "From the future!";
|
|
else return $"{age} years";
|
|
}
|
|
}
|
|
|
|
//TODO: Make Id read-only CUID. Allows for better integrity, especially when merging.
|
|
//TODO: Add buying price and automatic calculation of profit/loss with selling price suggestion.
|
|
//TODO: Add Buying Date.
|
|
//TODO: Add sold boolean and Sold Date.
|
|
//TODO: Add "hidden" attribute for cars that are not for sale anymore but should be kept in the database for records.
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="Car"/> class.
|
|
/// </summary>
|
|
/// <param name="id">The unique identifier as an Integer.</param>
|
|
/// <param name="make">The make / manufacturer of the car.</param>
|
|
/// <param name="model">The model.</param>
|
|
/// <param name="year">The year the car was built.</param>
|
|
/// <param name="color">The color of the car.</param>
|
|
/// <param name="mileage">The current mileage on the car.</param>
|
|
/// <param name="price">The selling-price of the car.</param>
|
|
/// <param name="order">The order.</param>
|
|
public Car(string id, string make, string model, int year, string color, int mileage, decimal price, int order = 0)
|
|
{
|
|
// is ID just a number? Then it is legacy and needs a new ID string.
|
|
int numericId = 0;
|
|
if ((string.IsNullOrWhiteSpace(id) || int.TryParse(id, out numericId)) && id != "0")
|
|
{
|
|
id = CUID.NewCUID().ToString();
|
|
if (numericId > 0)
|
|
{
|
|
order = numericId + order;
|
|
}
|
|
}
|
|
if(id.Length > 8)
|
|
{
|
|
id = CUID.NewCUID().ToString();
|
|
}
|
|
// Sets the properties using the setters to ensure validation is applied.
|
|
this.id = id;
|
|
this.Make = make;
|
|
this.Model = model;
|
|
this.Year = year;
|
|
this.Color = color;
|
|
this.Mileage = mileage;
|
|
this.Price = price;
|
|
this.Order = order;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Converts to string in a custom readable format.
|
|
/// <example>
|
|
/// For Example:
|
|
/// Skoda Fabia (2015)
|
|
/// </example>
|
|
/// </summary>
|
|
/// <returns>
|
|
/// A <see cref="System.String" /> that represents this instance.
|
|
/// </returns>
|
|
public override string ToString()
|
|
{
|
|
return $"{this.Make} {this.Model} ({this.Year})";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts to default CSV savable string.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public string ToCsvString()
|
|
{
|
|
return $"{this.Id};{this.Make};{this.Model};{this.Year};{this.Color};{this.Mileage};{this.Price};{this.Order}";
|
|
}
|
|
|
|
//TODO: Add error handling for malformed CSV strings and detection for missing fields.
|
|
//TODO: Add support for different CSV formats (e.g., different delimiters, quoted fields, etc.).
|
|
//TODO: Add support for nil or optional fields.
|
|
//TODO: Add detectin for invalid data / nonsensical values (e.g., negative mileage or year in the future). / Validate it actually is a car.
|
|
/// <summary>
|
|
/// Creates a <see cref="Car"/> instance from a CSV string in the default format.
|
|
/// </summary>
|
|
/// <param name="csv">The CSV Line.</param>
|
|
/// <returns>
|
|
/// A new <see cref="Car"/> instance.
|
|
/// </returns>
|
|
public static Car FromCsvString(string csv)
|
|
{
|
|
try
|
|
{
|
|
string[] parts = csv.Split(';');
|
|
// is part 7 a valid int? if not set it to 0 and log a warning.
|
|
if (parts.Length == 7)
|
|
{
|
|
Console.Error.WriteLine($"Warning: CSV string has only 7 fields, expected 8. Setting Order to 0. CSV: {csv}");
|
|
if (!StateManager.askForMigration())
|
|
{
|
|
throw new Exception("User declined migration. Cannot parse CSV string with missing Order field.");
|
|
}
|
|
Array.Resize(ref parts, 8);
|
|
parts[7] = "0";
|
|
}
|
|
else if (parts.Length != 8)
|
|
{
|
|
throw new FormatException($"CSV string has {parts.Length} fields, expected 8. CSV: {csv}");
|
|
}
|
|
|
|
Car temp = new Car(parts[0], parts[1], parts[2], int.Parse(parts[3]), parts[4], int.Parse(parts[5]), decimal.Parse(parts[6]), int.Parse(parts[7]));
|
|
return temp;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.Error.WriteLine($"Error parsing CSV Car string: {ex.Message}");
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines whether this <see cref="Car"/> is any different from the provided <see cref="Car" />.
|
|
/// </summary>
|
|
/// <param name="other">The <see cref="Car"/> to check against</param>
|
|
/// <returns>
|
|
/// <c>true</c> if the specified other is changed; otherwise, <c>false</c>.
|
|
/// </returns>
|
|
public bool IsChanged(Car other)
|
|
{
|
|
return this.Make != other.Make || this.Model != other.Model || this.Year != other.Year || this.Color != other.Color || this.Mileage != other.Mileage || this.Price != other.Price || this.Color != other.Color || this.Order != other.Order;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clones this instance.
|
|
/// </summary>
|
|
/// <returns>An identical but seperate <see cref="Car"/></returns>
|
|
public Car Clone()
|
|
{
|
|
return new Car(this.Id, this.Make, this.Model, this.Year, this.Color, this.Mileage, this.Price, this.Order);
|
|
}
|
|
|
|
|
|
public static bool isLegacyCsvString(string csv)
|
|
{
|
|
string[] parts = csv.Split(';');
|
|
return parts.Length == 7; // Legacy format has 7 fields, new format has 8 fields (with Order).
|
|
}
|
|
}
|
|
}
|