package fillin.date;

/**
 * A utility class that adjusts date format strings to match a specified component order, replicating OpenEdge
 * ABL's format adjustment behavior as a pure function.
 */
public class DateFormatAdjuster
{
   /**
    * Left-rotate elements in a 3-item array (e.g., [A,B,C] becomes [B,C,A]).
    * 
    * @param a
    *        the array to rotate
    */
   private static void rotateLeft(final String[] a)
   {
      swap(a, 0, 2);
      swap(a, 0, 1);
   }

   /**
    * Right-rotate elements in a 3-item array (e.g., [A,B,C] → [C,A,B]).
    * 
    * @param a
    *        the array to rotate
    */
   private static void rotateRight(final String[] a)
   {
      swap(a, 1, 2);
      swap(a, 0, 1);
   }

   /**
    * Swap two components in array
    * 
    * @param a
    *        the array to modify
    * @param i1
    *        the first valid array index to swap
    * @param i2
    *        the second valid array index to swap
    */
   private static final void swap(final String[] a, final int i1, final int i2)
   {
      final String t = a[i1];
      a[i1] = a[i2];
      a[i2] = t;
   }

   /**
    * Reorders components and separators in a date format string for date format assignment based on the
    * specified component order.
    * <p>
    * This method adjusts the format string to align with the given order, but reordering occurs only when
    * the year component has fewer than 4 digits and exactly one other component has exactly 4 digits. In
    * such cases, the components are rearranged so the year component has 4 digits. Depending on the format
    * and order, reordering may involve swapping two components or rotating all components left or right.
    * </p>
    * <p>
    * When reordering is required, separators are adjusted based on the specified order:
    * </p>
    * <ul>
    *   <li><b>Month in first position ("mdy", "myd"):</b> Separators remain in their original order.</li>
    *   <li><b>Month in middle position ("dmy", "ymd"):</b> Separators are swapped.</li>
    *   <li><b>Month in last position ("dym", "ydm"):</b> The second separator is copied to the first position.</li>
    * </ul>
    *
    * @param format   a valid date format string (e.g., "9/99.9999", "999-99-99") containing two
    *                 separators (dot, slash, or dash)
    * @param order    the desired component order ("mdy", "dmy", "ymd", "myd", "dym", "ydm"), representing
    *                 the SESSION:DATE-FORMAT setting in OpenEdge ABL
    * @param expected the expected output string for visual inspection during development; this parameter
    *                 is temporary and must be removed in production
    *                 
    * @return         the adjusted format string matching the specified order for date format assignment,
    *                 or the original format if no reordering is required
    */
   public static String reorderForDateFormatAssignment(final String format, final String order,
            final String expected)
   {
      /*
       * 1. Test if the component re-ordering is required
       */
      final String[] comps = format.split("[./-]");
      assert comps.length == 3 : "We accept only formats containing separators";

      // Find a components with 4 digits
      int longComponentIdx = -1;
      for (int i = 0; i < 3; i++)
      {
         final String comp = comps[i];
         if (comp.length() == 4)
         {
            if (longComponentIdx >= 0)
            {
               // There can be only one
               return format;
            }
            longComponentIdx = i;
         }
      }

      if (longComponentIdx == -1)
      {
         // No long format
         return format;
      }

      final int yearIndex = order.indexOf('y');
      assert yearIndex >= 0;
      if (yearIndex == longComponentIdx)
      {
         // Year is already the long one
         return format;
      }

      // Extract separators
      final int leftSeparatorPos = comps[0].length();
      final char leftSeparatorInitial = format.charAt(leftSeparatorPos);
      final int rightSeparatorPos = leftSeparatorPos + 1 + comps[1].length();
      final char rightSeparatorInitial = format.charAt(rightSeparatorPos);

      switch (order)
      {
         case "mdy":
            if (longComponentIdx == 0)
            {
               rotateLeft(comps);
            }
            else
            {
               swap(comps, 1, 2);
            }

            break;

         case "dmy":
            if (longComponentIdx == 0)
            {
               swap(comps, 0, 2);
            }
            else
            {
               rotateRight(comps);
            }

            break;

         case "ymd":
            if (longComponentIdx == 1)
            {
               swap(comps, 0, 1);
            }
            else
            {
               rotateRight(comps);
            }
            
            break;

         case "myd":
            if (longComponentIdx == 0)
            {
               swap(comps, 0, 1);
            }
            else
            {
               swap(comps, 1, 2);
            }
            
            break;

         case "dym":
            if (longComponentIdx == 0)
            {
               rotateRight(comps);
            }
            else
            {
               rotateLeft(comps);
            }

            break;

         case "ydm":
            if (longComponentIdx == 1)
            {
               rotateLeft(comps);
            }
            else
            {
               swap(comps, 0, 2);
            }

            break;

         default:
            throw new IllegalArgumentException("Unexpected date order");
      }

      final char leftSeparator;
      final char rightSeparator;

      /*
       * 2. Compute separators
       */
      switch (order.indexOf('m'))
      {
         case 0:
         {
            // Separators order preserved
            leftSeparator = leftSeparatorInitial;
            rightSeparator = rightSeparatorInitial;

            break;
         }
         case 1:
         {
            // Separators are swapped
            leftSeparator = rightSeparatorInitial;
            rightSeparator = leftSeparatorInitial;

            break;
         }
         case 2:
         {
            // Right separator is copied to the left 
            leftSeparator = rightSeparator = rightSeparatorInitial;

            break;
         }
         default:
            throw new IllegalArgumentException("Unexpected SESSION:DATE-FORMAT value: " + order);
      }

      // Build the result
      final StringBuilder result = new StringBuilder(10);

      result.append(comps[0]);
      result.append(leftSeparator);
      result.append(comps[1]);
      result.append(rightSeparator);
      result.append(comps[2]);

      return result.toString();
   }
}