using uCalcSoftware;

var uc = new uCalc();

static void ConvertUnits(uCalc.Callback cb) {
   var  value = cb.Arg(1);
   var fromUnit = cb.ArgStr(2);
   var toUnit = cb.ArgStr(3);

   // Construct the variable names for direct and inverse factors
   var factorName = fromUnit + "_to_" + toUnit;
   var inverseFactorName = toUnit + "_to_" + fromUnit;

   var uc_instance = cb.uCalc;

   // Try to find the direct conversion factor
   var factorItem = uc_instance.ItemOf(factorName);
   if (factorItem.NotEmpty()) {
      cb.Return(Math.Round(value * factorItem.Value(), 4));
      return;
   }

   // If not found, try to find the inverse factor and use its reciprocal
   var inverseFactorItem = uc_instance.ItemOf(inverseFactorName);
   if (inverseFactorItem.NotEmpty()) {
      cb.Return(value / inverseFactorItem.Value());
      return;
   }

   // If no factor is found, raise an error
   cb.Error.Raise("Conversion factor not found for " + fromUnit + " to " + toUnit);
}

// Define the conversion factors as variables
uc.DefineVariable("in_to_cm = 2.54");
uc.DefineVariable("km_to_miles = 0.621371");

// Define a custom function for temperature, since it's not a simple multiplication
uc.DefineFunction("ConvertTempFToC(val) = (val - 32) * 5.0/9.0");

// Register our generic conversion callback
uc.DefineFunction("Convert(val, fromUnit As String, toUnit As String)", ConvertUnits);

// Create generic rules in the expression transformer
var t = uc.ExpressionTransformer;
t.FromTo("{@Number:val} {@Alpha:from} to {@Alpha:to}", "Convert({val}, '{from}', '{to}')");
// A specific rule for Fahrenheit to Celsius since it's more complex (higher precedence because it's defined last)
t.FromTo("{@Number:val} F to C", "ConvertTempFToC({val})");

Console.WriteLine($"10 in to cm = {uc.Eval("10 in to cm")}");
Console.WriteLine($"100 km to miles = {uc.Eval("100 km to miles")}");

// Test the inverse conversion, which the callback handles automatically
Console.WriteLine($"254 cm to in = {uc.Eval("254 cm to in")}");
Console.WriteLine($"98.6 F to C = {uc.Eval("98.6 F to C")}");