/*
 * 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.iotdb.db.schemaengine.schemaregion.mtree.impl.pbtree.schemafile;

import org.apache.iotdb.commons.exception.MetadataException;
import org.apache.iotdb.db.exception.metadata.schemafile.RecordDuplicatedException;

import java.nio.ByteBuffer;
import java.util.Queue;

/**
 * This interface interacts with bytebuffer as a segment which is the basic unit to manage index
 * entries or MNode records.<br>
 * Generic T denotes the type of its input, while R denotes the type of the return.
 */
public interface ISegment<T, R> {

  /**
   * check whether enough space, notice that pairLength including 3 parts: [var length] key string
   * itself, [int, 4 bytes] length of key string, [short, 2 bytes] key address
   *
   * @return -1 for segment space run out, otherwise for spare space
   */
  int insertRecord(String key, T rec) throws RecordDuplicatedException;

  /**
   * todo switch definition of throw and negative return for better perf
   *
   * @param key name of the record, not the alias
   * @param buffer content of the updated record
   * @return -1 for space not enough
   * @throws for key not found
   */
  int updateRecord(String key, T buffer) throws MetadataException;

  int removeRecord(String key);

  /**
   * Get a record by its name
   *
   * @return index entry if {@link InternalPage}, otherwise IMNode
   */
  R getRecordByKey(String key) throws MetadataException;

  /**
   * Get a record by its alias
   *
   * @return null if {@link InternalPage}, otherwise IMNode
   */
  R getRecordByAlias(String alias) throws MetadataException;

  boolean hasRecordKey(String key);

  boolean hasRecordAlias(String alias);

  Queue<R> getAllRecords() throws MetadataException;

  /**
   * Records are always sync with buffer, but members in header are not. This method sync these
   * values to the buffer.
   */
  void syncBuffer();

  short size();

  short getSpareSize();

  void delete();

  long getNextSegAddress();

  void setNextSegAddress(long nextSegAddress);

  /**
   * This method will write info into a buffer equal or larger to existed one. There is no need to
   * call sync before this method, since it will flush header and key-offset list intrinsically.
   *
   * @param newBuffer target buffer
   */
  void extendsTo(ByteBuffer newBuffer) throws MetadataException;

  /**
   * Split the segment into dstBuffer considering the passing in key.
   *
   * <p>A better encapsulation may indicate to split the ISegment into another ISegment with same
   * generic type, rather than splitting into a raw ByteBuffer. That approach naturally migrate
   * records in a one-by-one manner, while not capable for bulk migrating records which could
   * improve performance in monotonic cases.
   *
   * @param entry content of the insert key.
   * @param dstBuffer destination of the split.
   * @param inclineSplit whether to split with incline.
   * @return always the search key of the split segment.
   */
  String splitByKey(String key, T entry, ByteBuffer dstBuffer, boolean inclineSplit)
      throws MetadataException;

  /**
   * Reset buffer content and return corresponding slice.
   *
   * @param ptr only works for {@link InternalPage}.
   * @return slice of the buffer.
   */
  ByteBuffer resetBuffer(int ptr);

  String toString();

  String inspect();
}
