// Copyright (c) 2024 Synty Studios Limited. All rights reserved.
//
// Use of this software is subject to the terms and conditions of the Synty Studios End User Licence Agreement (EULA)
// available at: https://syntystore.com/pages/end-user-licence-agreement
//
// For additional details, see the LICENSE.MD file bundled with this software.
using SQLite;
using Synty.SidekickCharacters.Enums;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
namespace Synty.SidekickCharacters.Database.DTO
{
[Table("sk_part")]
public class SidekickPart
{
private SidekickSpecies _species;
[PrimaryKey, AutoIncrement, Column("id")]
public int ID { get; set; }
[Column("ptr_species")]
public int PtrSpecies { get; set; }
[Column("type")]
public CharacterPartType Type { get; set; }
[Column("part_group")]
public PartGroup PartGroup { get; set; }
[Column("name")]
public string Name { get; set; }
[Column("part_file_name")]
public string FileName { get; set; }
[Column("part_location")]
public string Location { get; set; }
[Column("uses_wrap")]
public bool UsesWrap { get; set; }
[Column("file_exists")]
public bool FileExists { get; set; }
[Ignore]
public SidekickSpecies Species
{
get => _species;
set
{
_species = value;
PtrSpecies = value.ID;
}
}
///
/// Gets a specific Preset Part by its database ID.
///
/// The Database Manager to use.
/// The id of the required Preset Part.
/// The specific Preset Part if it exists; otherwise null.
public static SidekickPart GetByID(DatabaseManager dbManager, int id)
{
SidekickPart part = dbManager.GetCurrentDbConnection().Find(id);
Decorate(dbManager, part);
return part;
}
///
/// Gets a list of all the parts in the database.
///
/// The Database Manager to use.
/// A list of all parts in the database.
public static List GetAll(DatabaseManager dbManager)
{
List parts = dbManager.GetCurrentDbConnection().Table().ToList();
foreach (SidekickPart part in parts)
{
Decorate(dbManager, part);
}
return parts;
}
///
/// Gets a list of all the parts in the database for a given Character Part Type.
///
/// The Database Manager to use.
/// The Character Part Type to get all the parts for.
/// A list of all parts for the given Character Part Type in the database.
public static List GetAllForPartType(DatabaseManager dbManager, CharacterPartType partType)
{
List parts = dbManager.GetCurrentDbConnection().Table().Where(part => part.Type == partType).ToList();
foreach (SidekickPart part in parts)
{
Decorate(dbManager, part);
}
return parts;
}
///
/// Gets a list of all the parts in the database for a given Character Part Type.
///
/// The Database Manager to use.
/// The Character Part Type to get all the parts for.
/// Whether to include parts that have a file in the project or not.
/// A list of all parts for the given Character Part Type in the database.
public static List GetAllForSpecies(DatabaseManager dbManager, SidekickSpecies species, bool onlyPartsWithFile = true)
{
List parts = new List();
if (onlyPartsWithFile)
{
parts = dbManager.GetCurrentDbConnection().Table().Where(part => part.PtrSpecies == species.ID && part.FileExists == onlyPartsWithFile).ToList();
}
else
{
parts = dbManager.GetCurrentDbConnection().Table().Where(part => part.PtrSpecies == species.ID).ToList();
}
foreach (SidekickPart part in parts)
{
Decorate(dbManager, part);
}
return parts;
}
///
/// Get the part in the database for the given part file name.
///
/// The Database Manager to use.
/// The file name to get the part for.
/// A part in the database for the given part file name if it exists; otherwise null
public static SidekickPart GetByPartFileName(DatabaseManager dbManager, string fileName)
{
SidekickPart part = dbManager.GetCurrentDbConnection().Table().FirstOrDefault(part => part.FileName == fileName);
Decorate(dbManager, part);
return part;
}
///
/// Gets all the base parts from the database.
///
/// The Database Manager to use.
/// A list of all the base parts from the database.
public static List GetBaseParts(DatabaseManager dbManager)
{
List parts = dbManager.GetCurrentDbConnection().Table().Where(part => part.FileName.Contains("_BASE_")).ToList();
foreach (SidekickPart part in parts)
{
Decorate(dbManager, part);
}
return parts;
}
///
/// Search for a part in the database where the part name or filename match the given term.
///
/// The Database Manager to use.
/// The term to search for the part with.
/// A part in the database for that matches the search term if it exists; otherwise null
public static SidekickPart SearchForByName(DatabaseManager dbManager, string partName)
{
SidekickPart part = dbManager.GetCurrentDbConnection().Table().FirstOrDefault(part => part.Name == partName || part
.FileName.Contains(partName));
Decorate(dbManager, part);
return part;
}
///
/// Checks to see if the provided part name is unique.
///
/// The Database Manager to use.
/// The part name to check and see if it is unique.
/// True if no part exists in the database with given name; otherwise false.
public static bool IsPartNameUnique(DatabaseManager dbManager, string partName)
{
SidekickPart part = dbManager.GetCurrentDbConnection().Table().FirstOrDefault(part => part.Name == partName);
return part == null;
}
///
/// Gets the species for a specific part.
/// TODO: get from the DB when data is populated.
///
/// The list of all species to return a populated species object from.
/// The name of the part to get the species for.
/// The species the part belongs to.
public static SidekickSpecies GetSpeciesForPart(List allSpecies, string partName)
{
string shortcode = partName.Split('_').Last().Substring(0, 2);
SidekickSpecies selectedSpecies = allSpecies[0];
foreach (SidekickSpecies species in allSpecies)
{
if (string.Equals(shortcode, species.Code, StringComparison.CurrentCultureIgnoreCase))
{
selectedSpecies = species;
}
}
return selectedSpecies;
}
///
/// Updates all of the given parts in the DB. This is an Update only, and will not insert new objects.
///
/// The database manager to use.
/// The parts to update
public static void UpdateAll(DatabaseManager dbManager, List parts)
{
dbManager.GetCurrentDbConnection().UpdateAll(parts, false);
}
///
/// Ensures that the given part has its nice DTO class properties set
///
/// The Database Manager to use.
/// The part to decorate
private static void Decorate(DatabaseManager dbManager, SidekickPart part)
{
if (part != null)
{
if (part.Species == null && part.PtrSpecies >= 0)
{
part.Species = SidekickSpecies.GetByID(dbManager, part.PtrSpecies);
}
}
}
///
/// Updates or Inserts this item in the Database.
///
/// The database manager to use.
public int Save(DatabaseManager dbManager)
{
if (ID <= 0)
{
dbManager.GetCurrentDbConnection().Insert(this);
// in theory this could return a different ID, but in practice it's highly unlikely
ID = (int) SQLite3.LastInsertRowid(dbManager.GetCurrentDbConnection().Handle);
}
dbManager.GetCurrentDbConnection().Update(this);
return ID;
}
///
/// Gets the image associated with this part.
///
/// The database manager to use.
/// The image associated with this part.
public SidekickPartImage GetImageForPart(DatabaseManager dbManager)
{
return SidekickPartImage.GetByPart(dbManager, this);
}
///
/// Deletes this item from the database
///
/// The database manager to use.
public void Delete(DatabaseManager dbManager)
{
foreach (SidekickPartFilterRow row in SidekickPartFilterRow.GetAllForPart(dbManager, this))
{
row.Delete(dbManager);
}
foreach (SidekickPartPresetRow row in SidekickPartPresetRow.GetAllByPart(dbManager, this))
{
row.Delete(dbManager);
}
foreach (SidekickPartSpeciesLink link in SidekickPartSpeciesLink.GetAllForPart(dbManager, this))
{
link.Delete(dbManager);
}
SidekickPartImage image = SidekickPartImage.GetByPart(dbManager, this);
image?.Delete(dbManager);
dbManager.GetCurrentDbConnection().Delete(ID);
}
///
/// Gets the GameObject model of this part.
///
/// A GameObject with the part model
public GameObject GetPartModel()
{
string resource = GetResourcePath(Location);
return Resources.Load(resource);
}
///
/// Checks if the file for this part exists.
///
/// True if the file is available; otherwise false
public bool IsFileAvailable()
{
FileExists = File.Exists(Location);
return FileExists;
}
///
/// Gets a resource path for using with Resources.Load() from a full path.
///
/// The full path to get the resource path from.
/// The resource path.
private string GetResourcePath(string fullPath)
{
int startIndex = fullPath.IndexOf("Resources", StringComparison.Ordinal) + 10;
string resourcePath = fullPath.Substring(startIndex, fullPath.Length - startIndex);
return Path.Combine(Path.GetDirectoryName(resourcePath)?? "", Path.GetFileNameWithoutExtension(resourcePath));
}
///
public override bool Equals(object obj)
{
SidekickPart part = (SidekickPart) obj;
if (ID > 0 && part?.ID > 0)
{
return ID == part?.ID;
}
return Name.Equals(part?.Name);
}
///
public override int GetHashCode()
{
HashCode hashCode = new HashCode();
hashCode.Add(_species);
hashCode.Add(ID);
hashCode.Add(PtrSpecies);
hashCode.Add((int) Type);
hashCode.Add((int) PartGroup);
hashCode.Add(Name);
hashCode.Add(FileName);
hashCode.Add(Location);
hashCode.Add(UsesWrap);
hashCode.Add(FileExists);
return hashCode.ToHashCode();
}
}
}