/** Redis SETRANGE command handler. */
public class GridRedisGetRangeCommandHandler extends GridRedisRestCommandHandler {
  /** Supported commands. */
  private static final Collection<GridRedisCommand> SUPPORTED_COMMANDS = U.sealList(GETRANGE);

  /** Start offset position in Redis message parameters. */
  private static final int START_OFFSET_POS = 1;

  /** End offset position in Redis message parameters. */
  private static final int END_OFFSET_POS = 2;

  /**
   * Handler constructor.
   *
   * @param log Logger to use.
   * @param hnd Rest handler.
   */
  public GridRedisGetRangeCommandHandler(
      final IgniteLogger log, final GridRestProtocolHandler hnd) {
    super(log, hnd);
  }

  /** {@inheritDoc} */
  @Override
  public Collection<GridRedisCommand> supportedCommands() {
    return SUPPORTED_COMMANDS;
  }

  /** {@inheritDoc} */
  @Override
  public GridRestRequest asRestRequest(GridRedisMessage msg) throws IgniteCheckedException {
    assert msg != null;

    if (msg.messageSize() < 4) throw new GridRedisGenericException("Wrong number of arguments");

    GridRestCacheRequest getReq = new GridRestCacheRequest();

    getReq.clientId(msg.clientId());
    getReq.key(msg.key());
    getReq.command(CACHE_GET);

    return getReq;
  }

  /** {@inheritDoc} */
  @Override
  public ByteBuffer makeResponse(final GridRestResponse restRes, List<String> params) {
    if (restRes.getResponse() == null) return GridRedisProtocolParser.toBulkString("");

    if (restRes.getResponse() instanceof String) {
      String res = String.valueOf(restRes.getResponse());
      int startOff;
      int endOff;

      try {
        startOff = boundedStartOffset(Integer.parseInt(params.get(START_OFFSET_POS)), res.length());
        endOff = boundedEndOffset(Integer.parseInt(params.get(END_OFFSET_POS)), res.length());
      } catch (NumberFormatException e) {
        U.error(log, "Erroneous offset", e);

        return GridRedisProtocolParser.toGenericError("Offset is not an integer");
      }

      return GridRedisProtocolParser.toBulkString(res.substring(startOff, endOff));
    } else
      return GridRedisProtocolParser.toTypeError(
          "Operation against a key holding the wrong kind of value");
  }

  /**
   * @param idx Index.
   * @param size Bounds.
   * @return Offset within the bounds.
   */
  private int boundedStartOffset(int idx, int size) {
    return idx >= 0 ? Math.min(idx, size) : size + idx;
  }

  /**
   * @param idx Index.
   * @param size Bounds.
   * @return Offset within the bounds.
   */
  private int boundedEndOffset(int idx, int size) {
    return idx >= 0 ? Math.min(idx + 1, size) : size + idx + 1;
  }
}