using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using ACE.Database;
using ACE.Entity.Enum;
using ACE.Entity.Models;
using ACE.Server.Entity;
using ACE.Server.Factories;
using ACE.Server.WorldObjects;
namespace ACE.Server.Network.Structure
{
///
/// Contains information about a house purchase or maintenance item
///
public class HousePayment
{
public int Num; // The quantity required
public int Paid; // The quantity paid
public uint WeenieID; // The item weenie class ID
public string Name; // The item name
public string PluralName; // The pluralized item name (if not specified, use followed by 's' or 'es')
///
/// The quantity remaining to be paid for this item
///
public int Remaining => Math.Max(0, Num - Paid);
public HousePayment() { }
public HousePayment(string weenieName, ushort stackSize = 1)
{
var weenie = DatabaseManager.World.GetCachedWeenie(weenieName);
Init(weenie, stackSize);
}
public HousePayment(Weenie weenie, ushort stackSize = 1)
{
Init(weenie, stackSize);
}
public HousePayment(WorldObject wo)
{
Init(wo);
}
public void Init(Weenie weenie, ushort stackSize = 1)
{
var wo = WorldObjectFactory.CreateNewWorldObject(weenie.ClassName);
wo.SetStackSize(stackSize);
WeenieID = weenie.WeenieClassId;
Init(wo);
}
public void Init(WorldObject wo)
{
WeenieID = wo.WeenieClassId;
Name = wo.Name;
PluralName = wo.GetPluralName();
Num = wo.StackSize ?? 1;
}
///
/// Returns the amount of items to consume for a particular HousePayment item
///
public List> GetConsumeItems(List items)
{
if (Remaining == 0) return new List>();
// filter to payment tab items that apply to this HousePayment wcid
// consume from smallest stacks first, to clear out the clutter
var wcidItems = items.Where(i => i.WeenieClassId == WeenieID).OrderBy(i => i.StackSize ?? 1).ToList();
// house monetary payments have pyreal wcid, and can be also be paid with trade notes
if (WeenieID == 273)
{
// append trade notes
// consume from smallest denomination stacks first, with multiple stacks of the same denomination sorted by stack size
// note that this does not produce an optimal solution for minimizing the amount of trade notes consumed
// a slightly better solution would be to iteratively consume the highest denomination trade note that is <= the remaining amount,
// but there are still some sets where even that wouldn't be optimized..
var tradeNotes = items.Where(i => i.ItemType == ItemType.PromissoryNote).OrderBy(i => i.StackSize ?? 1).OrderBy(i => i.StackUnitValue).ToList();
var coinsAndTradeNotes = wcidItems.Concat(tradeNotes).ToList();
return GetConsumeItems_Inner(coinsAndTradeNotes);
}
else
{
return GetConsumeItems_Inner(wcidItems);
}
}
///
/// Returns the amount of items to consume for a particular HousePayment item
///
public List> GetConsumeItems_Inner(List items)
{
var consumeItems = new List>();
var remaining = Remaining;
foreach (var item in items)
{
var stackSize = item.StackSize ?? 1;
var stackValue = item.Value ?? 0;
var baseValue = item.StackUnitValue ?? 0;
var amount = item.IsTradeNote ? stackValue : stackSize;
if (amount <= remaining)
{
consumeItems.Add(new WorldObjectInfo(item, stackSize));
remaining -= amount;
}
else
{
var consumeAmount = item.IsTradeNote ? (int)Math.Ceiling((float)remaining / baseValue) : remaining;
consumeItems.Add(new WorldObjectInfo(item, consumeAmount));
remaining = 0;
}
if (remaining <= 0)
break;
}
return consumeItems;
}
}
public static class HousePaymentExtensions
{
public static void Write(this BinaryWriter writer, HousePayment payment)
{
writer.Write(payment.Num);
writer.Write(payment.Paid);
writer.Write(payment.WeenieID);
writer.WriteString16L(payment.Name);
writer.WriteString16L(payment.PluralName);
}
public static void Write(this BinaryWriter writer, List payments)
{
if (payments == null)
payments = new List();
writer.Write(payments.Count);
foreach (var payment in payments)
writer.Write(payment);
}
}
}