@Override
  protected void wrappedHandle(HttpExchange t, String[] path, LinkedHashMap<String, String> query)
      throws Exception {
    l.info(
        "GET "
            + t.getRequestURI()
            + " "
            + t.getRemoteAddress()); // TODO - create a special logger for this!

    String[] longestMatch = getLongestMatch(path);
    LazyInstantiator<SafeHttpHandler> handler = handlers.get(longestMatch);
    if (handler == null) {
      String failMessage = "No handler found for: " + Arrays.toString(path);
      l.info(failMessage);
      sendText(t, failMessage);
      return;
    }

    TimedLogRecordStart start = new TimedLogRecordStart("calling " + handler);
    l.log(start);

    int startIndex = longestMatch.length;
    String[] truncatedPath = Arrays.copyOfRange(path, startIndex, path.length);
    handler.cachedInstance().wrappedHandle(t, truncatedPath, query);

    l.log(start.finishedNow());
  }
  public ScrambleRequest(String title, String scrambleRequestUrl, String seed)
      throws InvalidScrambleRequestException, UnsupportedEncodingException {
    String[] puzzle_count_copies_scheme = scrambleRequestUrl.split("\\*");
    title = URLDecoder.decode(title, "utf-8");
    for (int i = 0; i < puzzle_count_copies_scheme.length; i++) {
      puzzle_count_copies_scheme[i] = URLDecoder.decode(puzzle_count_copies_scheme[i], "utf-8");
    }
    String countStr = "";
    String copiesStr = "";
    String scheme = "";
    String puzzle;
    switch (puzzle_count_copies_scheme.length) {
      case 4:
        scheme = puzzle_count_copies_scheme[3];
      case 3:
        copiesStr = puzzle_count_copies_scheme[2];
      case 2:
        countStr = puzzle_count_copies_scheme[1];
      case 1:
        puzzle = puzzle_count_copies_scheme[0];
        break;
      default:
        throw new InvalidScrambleRequestException("Invalid puzzle request " + scrambleRequestUrl);
    }

    LazyInstantiator<Puzzle> lazyScrambler = puzzles.get(puzzle);
    if (lazyScrambler == null) {
      throw new InvalidScrambleRequestException("Invalid scrambler: " + puzzle);
    }

    try {
      this.scrambler = lazyScrambler.cachedInstance();
    } catch (Exception e) {
      throw new InvalidScrambleRequestException(e);
    }

    ScrambleCacher scrambleCacher = scrambleCachers.get(puzzle);
    if (scrambleCacher == null) {
      scrambleCacher = new ScrambleCacher(scrambler);
      scrambleCachers.put(puzzle, scrambleCacher);
    }

    this.title = title;
    fmc = countStr.equals("fmc");
    int count;
    if (fmc) {
      count = 1;
    } else {
      count = Math.min(toInt(countStr, 1), MAX_COUNT);
    }
    this.copies = Math.min(toInt(copiesStr, 1), MAX_COPIES);
    if (seed != null) {
      this.scrambles = scrambler.generateSeededScrambles(seed, count);
    } else {
      this.scrambles = scrambleCacher.newScrambles(count);
    }

    this.colorScheme = scrambler.parseColorScheme(scheme);
  }