using uCalcSoftware;

var uc = new uCalc();

static void GetCurrentDate(uCalc.Callback cb) {
   // In a real application, this would return the system's current date.
   // For this example, we'll use a fixed date for consistent output.
   // Let's pretend today is January 15, 2026 (a Thursday).
   cb.Return(46036); // Using Excel-style date serial number for simplicity
}

static void AddDuration(uCalc.Callback cb) {
   var startDate = cb.Arg(1);
   var number = cb.Arg(2);
   var unit = cb.ArgStr(3);
   var result = startDate;

   if (unit == "day" || unit == "days") {
      result = startDate + number;
   } else if (unit == "week" || unit == "weeks") {
      result = startDate + (number * 7);
   } else if (unit == "month" || unit == "months") {
      result = startDate + (number * 30); // Approximation for example
   }
   cb.Return(result);
}

static void GetNextDayOfWeek(uCalc.Callback cb) {
   var dayName = cb.ArgStr(1);
   var today = 46036; // Thursday, Jan 15, 2026
   var todayDayOfWeek = 5; // 1=Sun, 2=Mon, ..., 5=Thu

   var targetDay = 0;
   if (dayName == "Sunday") targetDay = 1;
   if (dayName == "Monday") targetDay = 2;
   if (dayName == "Tuesday") targetDay = 3;
   if (dayName == "Wednesday") targetDay = 4;
   if (dayName == "Thursday") targetDay = 5;
   if (dayName == "Friday") targetDay = 6;
   if (dayName == "Saturday") targetDay = 7;

   var daysToAdd = (targetDay - todayDayOfWeek + 7) % 7;
   // Always get the *next* week's day
   if (daysToAdd == 0) daysToAdd = 7;

   cb.Return(today + daysToAdd);
}

static void FormatDate(uCalc.Callback cb) {
   // This is a simplified formatter for the example.
   // A real implementation would be more robust.
   var dateSerial = cb.Arg(1);
   if (dateSerial == 46036) cb.ReturnStr("2026-01-15");
   if (dateSerial == 46037) cb.ReturnStr("2026-01-16");
   if (dateSerial == 46039) cb.ReturnStr("2026-01-18");
   if (dateSerial == 46043) cb.ReturnStr("2026-01-22");
   if (dateSerial == 46050) cb.ReturnStr("2026-01-29");
   if (dateSerial == 46096) cb.ReturnStr("2026-03-16");
}


// 1. Define the helper functions in the uCalc engine
uc.DefineFunction("GetCurrentDate()", GetCurrentDate);
uc.DefineFunction("AddDuration(date, num, unit As String)", AddDuration);
uc.DefineFunction("GetNextDayOfWeek(dayName As String)", GetNextDayOfWeek);
uc.DefineFunction("FormatDate(date) As String", FormatDate);

// 2. Create the transformer and define the DSL rules
using (var t = new uCalc.Transformer(uc)) {
   // Set case-insensitivity for all rules
   t.DefaultRuleSet.CaseSensitive = false;

   // Define the rules
   t.FromTo("today", "{@Eval: FormatDate(GetCurrentDate())}");
   t.FromTo("tomorrow", "{@Eval: FormatDate(AddDuration(GetCurrentDate(), 1, 'day'))}");
   t.FromTo("next {@Alpha:day}", "{@Eval: FormatDate(GetNextDayOfWeek(day))}");
   t.FromTo("in {@Number:num} {@Alpha:unit}", "{@Eval: FormatDate(AddDuration(GetCurrentDate(), Double(num), unit))}");

   // 3. Process the input strings
   Console.WriteLine($"Input: 'today' -> Output: {t.Transform("today")}");
   Console.WriteLine($"Input: 'tomorrow' -> Output: {t.Transform("tomorrow")}");
   Console.WriteLine($"Input: 'next Sunday' -> Output: {t.Transform("next Sunday")}");
   Console.WriteLine($"Input: 'in 2 weeks' -> Output: {t.Transform("in 2 weeks")}");
   Console.WriteLine($"Input: 'in 60 days' -> Output: {t.Transform("in 60 days")}");
}