// PPJ concept guide sample. Add to a project referencing your installed PPJ runtime.
using System;
using PPJ.Runtime;

public static class LineTotalSample
{
    public static SalBoolean TryLineTotal(SalNumber quantity, SalNumber unitPrice,
        SalNumber discountPercent, ref SalNumber total)
    {
        total = SalNumber.Null;
        if (quantity.IsNull || unitPrice.IsNull || discountPercent.IsNull)
            return false;
        if (quantity < 0 || unitPrice < 0 || discountPercent < 0 || discountPercent > 100)
            return false;
        total = quantity * unitPrice * (100 - discountPercent) / 100;
        return true;
    }

    public static void RunChecks()
    {
        Check("ordinary", 2, 12.50m, 0, true, 25m);
        Check("discount", 3, 19.95m, 10, true, 53.865m);
        Check("zero quantity", 0, 12.50m, 0, true, 0m);
        Check("full discount", 2, 12.50m, 100, true, 0m);
        Check("negative quantity", -1, 12.50m, 0, false, SalNumber.Null);
        Check("negative price", 1, -1, 0, false, SalNumber.Null);
        Check("negative discount", 1, 12.50m, -1, false, SalNumber.Null);
        Check("discount over 100", 1, 12.50m, 101, false, SalNumber.Null);
        Check("null quantity", SalNumber.Null, 12.50m, 0, false, SalNumber.Null);
        Check("null price", 1, SalNumber.Null, 0, false, SalNumber.Null);
        Check("null discount", 1, 12.50m, SalNumber.Null, false, SalNumber.Null);
    }

    private static void Check(string name, SalNumber quantity, SalNumber unitPrice,
        SalNumber discount, bool expectedSuccess, SalNumber expectedTotal)
    {
        SalNumber actual = 999; // Detect stale receive values on failure.
        bool succeeded = TryLineTotal(quantity, unitPrice, discount, ref actual);
        bool sameValue = expectedTotal.IsNull ? actual.IsNull : !actual.IsNull && actual == expectedTotal;
        if (succeeded != expectedSuccess || !sameValue)
            throw new InvalidOperationException("Line total case failed: " + name);
    }
}
