/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.phoenix.util;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.util.Bytes;

/**
 * Static class for various methods that would be nice to have added to
 * {@link org.apache.hadoop.hbase.client.Result}. These methods work off of the raw bytes preventing
 * the explosion of Result into object form.
 * @since 0.1
 */
public class ResultUtil {
  public static final Result EMPTY_RESULT = new Result() {
    @Override
    public final boolean isEmpty() {
      return true;
    }
  };

  private ResultUtil() {
  }

  public static Result toResult(ImmutableBytesWritable bytes) {
    byte[] buf = bytes.get();
    int offset = bytes.getOffset();
    int finalOffset = bytes.getLength() + offset;
    List<Cell> kvs = new ArrayList<Cell>();
    while (offset < finalOffset) {
      int keyLength = Bytes.toInt(buf, offset);
      offset += Bytes.SIZEOF_INT;
      kvs.add(new KeyValue(buf, offset, keyLength));
      offset += keyLength;
    }
    return Result.create(kvs);
  }

  /**
   * Return a pointer into a potentially much bigger byte buffer that points to the key of a Result.
   */
  public static ImmutableBytesWritable getKey(Result r) {
    return getKey(r, 0);
  }

  public static void getKey(Result r, ImmutableBytesWritable key) {
    key.set(r.getRow());
    // key.set(getRawBytes(r), getKeyOffset(r), getKeyLength(r));
  }

  public static void getKey(Cell value, ImmutableBytesWritable key) {
    key.set(value.getRowArray(), value.getRowOffset(), value.getRowLength());
  }

  /**
   * Return a pointer into a potentially much bigger byte buffer that points to the key of a Result.
   * Use offset to return a subset of the key bytes, for example to skip the organization ID
   * embedded in all of our keys.
   * @param offset offset added to start of key and subtracted from key length (to select subset of
   *               key bytes)
   */
  public static ImmutableBytesWritable getKey(Result r, int offset) {
    return new ImmutableBytesWritable(getRawBytes(r), getKeyOffset(r) + offset,
      getKeyLength(r) - offset);
  }

  public static void getKey(Result r, int offset, int length, ImmutableBytesWritable key) {
    key.set(getRawBytes(r), getKeyOffset(r) + offset, length);
  }

  /**
   * Comparator for comparing the keys from two Results in-place, without allocating new byte arrays
   */
  public static final Comparator<Result> KEY_COMPARATOR = new Comparator<Result>() {

    @Override
    public int compare(Result r1, Result r2) {
      byte[] r1Bytes = getRawBytes(r1);
      byte[] r2Bytes = getRawBytes(r2);
      return Bytes.compareTo(r1Bytes, getKeyOffset(r1), getKeyLength(r1), r2Bytes, getKeyOffset(r2),
        getKeyLength(r2));
    }

  };

  /**
   * Get the offset into the Result byte array to the key.
   */
  static int getKeyOffset(Result r) {
    KeyValue firstKV = PhoenixKeyValueUtil.maybeCopyCell(r.rawCells()[0]);
    return firstKV.getOffset();
  }

  static int getKeyLength(Result r) {
    // Key length stored right before key as a short
    return Bytes.toShort(getRawBytes(r), getKeyOffset(r) - Bytes.SIZEOF_SHORT);
  }

  static byte[] getRawBytes(Result r) {
    KeyValue firstKV = PhoenixKeyValueUtil.maybeCopyCell(r.rawCells()[0]);
    return firstKV.getBuffer();
  }

  public static int compareKeys(Result r1, Result r2) {
    return Bytes.compareTo(getRawBytes(r1), getKeyOffset(r1), getKeyLength(r1), getRawBytes(r2),
      getKeyOffset(r2), getKeyLength(r2));
  }

}
