Imports System
Imports uCalcSoftware
Public Module Program
   
   Public Sub ConvertUnits(ByVal cb As uCalc.Callback)
      Dim  value = cb.Arg(1)
      Dim fromUnit = cb.ArgStr(2)
      Dim toUnit = cb.ArgStr(3)
      
      '// Construct the variable names for direct and inverse factors
      Dim factorName = fromUnit + "_to_" + toUnit
      Dim inverseFactorName = toUnit + "_to_" + fromUnit
      
      Dim uc_instance = cb.uCalc
      
      '// Try to find the direct conversion factor
      Dim factorItem = uc_instance.ItemOf(factorName)
      If factorItem.NotEmpty() Then
         cb.Return(Math.Round(value * factorItem.Value(), 4))
         return
      End If
      
      '// If not found, try to find the inverse factor and use its reciprocal
      Dim inverseFactorItem = uc_instance.ItemOf(inverseFactorName)
      If inverseFactorItem.NotEmpty() Then
         cb.Return(value / inverseFactorItem.Value())
         return
      End If
      
      '// If no factor is found, raise an error
      cb.Error.Raise("Conversion factor not found for " + fromUnit + " to " + toUnit)
   End Sub
   Public Sub Main()
      Dim uc As New uCalc()
      '// 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)", AddressOf ConvertUnits)
      
      '// Create generic rules in the expression transformer
      Dim 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")}")
   End Sub
End Module