content: package better.anticheat.core.player.tracker.impl.confirmation.cookie.allocator;
  
  import better.anticheat.core.player.tracker.impl.confirmation.cookie.CookieIdAllocator;
  import better.anticheat.core.player.tracker.impl.confirmation.cookie.CookieSequenceData;
  import lombok.Getter;
  import lombok.extern.slf4j.Slf4j;
  
  import java.util.concurrent.atomic.AtomicInteger;
  
  /**
   * A cookie ID allocator that uses pre-loaded cookie sequence data from files.
   * The allocator cycles through the cookie IDs sequentially, returning to the beginning when it reaches the end.
   * Each instance maintains its own position in the sequence for per-player isolation.
   * 
   * <h2>Features</h2>
   * <ul>
   *   <li><b>Variable Length Support</b>: Cookie IDs can have different lengths - shorter ones are automatically padded with zeros to match the longest one</li>
   *   <li><b>Flexible File Formats</b>: Supports multiple input formats in the same file</li>
   *   <li><b>Resource and File System Loading</b>: First checks resources, then falls back to file system</li>
   *   <li><b>Thread-Safe</b>: Uses {@code AtomicInteger} for safe concurrent access</li>
   *   <li><b>Automatic Directory Creation</b>: Creates the {@code cookiesequence} directory if it doesn't exist</li>
   *   <li><b>Per-Player Isolation</b>: Each player gets their own allocator instance with independent state</li>
   * </ul>
   * 
   * <h2>File Loading Strategy</h2>
   * <ol>
   *   <li><b>Resources</b>: First tries to load from {@code src/main/resources/{filename}}</li>
   *   <li><b>File System</b>: If not found in resources, checks {@code {BetterAnticheat.directory}/cookiesequence/{filename}}</li>
   *   <li><b>Directory Creation</b>: Automatically creates the {@code cookiesequence} directory if it doesn't exist</li>
   * </ol>
   * 
   * <h2>Supported File Formats</h2>
   * The allocator supports multiple formats in the same file:
   * 
   * <h3>Hex Strings</h3>
   * <pre>{@code
   * # Plain hex (recommended)
   * deadbeef
   * 0123456789abcdef
   * 
   * # With separators (also supported)
   * de:ad:be:ef
   * 01 23 45 67 89 ab cd ef
   * de-ad-be-ef
   * }</pre>
   * 
   * <h3>Decimal Bytes (comma-separated)</h3>
   * <pre>{@code
   * 222,173,190,239
   * 1,35,69,103,137,171,205,239
   * }</pre>
   * 
   * <h3>Base64 Strings</h3>
   * <pre>{@code
   * 3q2+7w==
   * ASNFZ4mrze8=
   * }</pre>
   * 
   * <h3>Comments and Empty Lines</h3>
   * <pre>{@code
   * # This is a comment and will be ignored
   * # Empty lines are also ignored
   * 
   * deadbeef  # Inline comments are not supported
   * }</pre>
   * 
   * <h2>Variable Length Example</h2>
   * <pre>{@code
   * # Variable length cookie IDs - all will be padded to 12 bytes (longest)
   * deadbeef                    # 4 bytes -> padded to 12 bytes
   * 0123456789abcdef            # 8 bytes -> padded to 12 bytes  
   * fedcba9876543210aa          # 9 bytes -> padded to 12 bytes
   * 1111                        # 2 bytes -> padded to 12 bytes
   * ffffffffffffffffffffffff    # 12 bytes -> no padding needed
   * }</pre>
   * 
   * <h2>Usage</h2>
   * 
   * <h3>Basic Usage</h3>
   * <pre>{@code
   * // Create allocator with pre-loaded cookie sequence data
   * CookieSequenceData cookieData = BetterAnticheat.getInstance().getCookieSequenceData();
   * CookieIdAllocator allocator = new FileBasedCookieAllocator(cookieData);
   * }</pre>
   * 
   * <h3>Allocation</h3>
   * <pre>{@code
   * // Allocate cookie IDs sequentially
   * byte[] cookie1 = allocator.allocateNext(); // First cookie from file
   * byte[] cookie2 = allocator.allocateNext(); // Second cookie from file
   * // ... continues cycling through all cookies in file
   * 
   * // When it reaches the end, it automatically cycles back to the beginning
   * }</pre>
   * 
   * <h3>Additional Methods</h3>
   * <pre>{@code
   * // Get information about the allocator
   * int totalCookies = allocator.getAvailableCookieCount();
   * int currentIndex = allocator.getCurrentIndex();
   * int cookieLength = allocator.getCookieIdLength(); // Length after padding
   * 
   * // Reset to beginning
   * allocator.reset();
   * 
   * // Validate cookie IDs
   * boolean isValid = allocator.isValidCookieId(cookieBytes);
   * }</pre>
   * 
   * <h2>File Location Examples</h2>
   * 
   * <h3>Resources (checked first)</h3>
   * <pre>{@code
   * src/main/resources/cookie_sequences.txt
   * src/main/resources/my_custom_cookies.txt
   * }</pre>
   * 
   * <h3>File System (checked second)</h3>
   * <pre>{@code
   * {server_directory}/plugins/BetterAnticheat/cookiesequence/cookie_sequences.txt
   * {server_directory}/plugins/BetterAnticheat/cookiesequence/my_custom_cookies.txt
   * }</pre>
   * 
   * <h2>Error Handling</h2>
   * <ul>
   *   <li><b>File Not Found</b>: Throws {@code RuntimeException} if file is not found in either location</li>
   *   <li><b>Empty File</b>: Throws {@code IllegalStateException} if no valid cookie IDs are loaded</li>
   *   <li><b>Parse Errors</b>: Throws {@code IOException} with detailed line number and error information</li>
   *   <li><b>Invalid Formats</b>: Provides clear error messages for malformed hex, decimal, or base64 data</li>
   * </ul>
   * 
   * <h2>Logging</h2>
   * The allocator uses SLF4J logging:
   * <ul>
   *   <li><b>INFO</b>: File loading success, cookie count, and padding information</li>
   *   <li><b>DEBUG</b>: Individual cookie parsing and padding details</li>
   *   <li><b>WARN</b>: Resource loading failures (before trying file system)</li>
   * </ul>
   * 
   * <h2>Thread Safety</h2>
   * The allocator is thread-safe:
   * <ul>
   *   <li>Uses {@code AtomicInteger} for the current index</li>
   *   <li>Returns cloned byte arrays to prevent external modification</li>
   *   <li>Immutable internal state after construction</li>
   * </ul>
   * 
   * <h2>Performance Considerations</h2>
   * <ul>
   *   <li>All cookie IDs are loaded into memory at startup</li>
   *   <li>Sequential access is O(1) with atomic operations</li>
   *   <li>Memory usage scales with file size and cookie length</li>
   *   <li>Consider file size for large cookie sequences</li>
   * </ul>
   * 
   * @author BetterAnticheat
   * @since 1.0.0
   * @see CookieIdAllocator
   * @see CookieSequenceData
   */
  @Slf4j
  public class FileBasedCookieAllocator implements CookieIdAllocator {
      
      private final CookieSequenceData cookieSequenceData;
      private final AtomicInteger currentIndex;
      @Getter
      private final int cookieIdLength;
      
      /**
       * Creates a new FileBasedCookieAllocator using the provided cookie sequence data.
       * 
       * @param cookieSequenceData the pre-loaded cookie sequence data
       * @throws IllegalArgumentException if cookieSequenceData is null
       */
      public FileBasedCookieAllocator(CookieSequenceData cookieSequenceData) {
          if (cookieSequenceData == null) {
              throw new IllegalArgumentException("CookieSequenceData cannot be null");
          }
          
          this.cookieSequenceData = cookieSequenceData;
          this.currentIndex = new AtomicInteger(0);
          this.cookieIdLength = cookieSequenceData.getCookieIdLength();
          
          log.debug("Created FileBasedCookieAllocator with {} cookie IDs of {} bytes each", 
                  cookieSequenceData.getAvailableCookieCount(), cookieIdLength);
      }
      
      @Override
      public byte[] allocateNext() {
          if (cookieSequenceData.getAvailableCookieCount() == 0) {
              throw new IllegalStateException("No cookie IDs available");
          }
          
          int index = currentIndex.getAndUpdate(i -> (i + 1) % cookieSequenceData.getAvailableCookieCount());
          return cookieSequenceData.getCookieId(index);
      }
      
      @Override
      public boolean isValidCookieId(byte[] cookieId) {
          return cookieSequenceData.isValidCookieId(cookieId);
      }
      
      /**
       * Gets the total number of cookie IDs in the sequence.
       * 
       * @return the number of available cookie IDs
       */
      public int getAvailableCookieCount() {
          return cookieSequenceData.getAvailableCookieCount();
      }
      
      /**
       * Gets the current index in the cookie ID sequence.
       * 
       * @return the current index
       */
      public int getCurrentIndex() {
          return currentIndex.get();
      }
      
      /**
       * Resets the allocator to start from the beginning of the sequence.
       */
      public void reset() {
          currentIndex.set(0);
      }
  }
  