/*
 * 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.fory;

import java.io.OutputStream;
import java.util.function.Function;
import org.apache.fory.io.ForyInputStream;
import org.apache.fory.io.ForyReadableChannel;
import org.apache.fory.memory.MemoryBuffer;
import org.apache.fory.resolver.TypeResolver;
import org.apache.fory.serializer.BufferCallback;
import org.apache.fory.serializer.Serializer;
import org.apache.fory.serializer.SerializerFactory;
import org.apache.fory.serializer.Serializers;

/** All Fory’s basic interface, including Fory’s basic methods. */
public interface BaseFory {

  /**
   * Register class and allocate an auto-grown ID for this class.
   *
   * <p><b>NOTE</b>: The registration order is important. If registration order is inconsistent, the
   * allocated ID will be different, and the deserialization will failed !!!
   *
   * @param cls class to register.
   */
  void register(Class<?> cls);

  /**
   * register class with given id.
   *
   * <p><b>NOTE</b>: The registration id is important. If registration id is inconsistent, and the
   * deserialization will failed !!!
   */
  void register(Class<?> cls, int id);

  /** register class with given type name which will be used for cross-language serialization. */
  void register(Class<?> cls, String typeName);

  /**
   * register class with given type namespace and name. This can be used mapping different classes
   * into same type when deserializing.
   */
  void register(Class<?> cls, String namespace, String typeName);

  /**
   * Register class and allocate an auto-grown ID for this class.
   *
   * <p><b>NOTE</b>: The registration order is important. If registration order is inconsistent, the
   * allocated ID will be different, and the deserialization will failed !!!
   *
   * @param className full class name to register.
   */
  void register(String className);

  /** register class with given id. */
  void register(String className, int classId);

  /**
   * register class with given type namespace and name. This can be used mapping different classes
   * into same type when deserializing.
   */
  void register(String className, String namespace, String typeName);

  void registerUnion(Class<?> cls, int id, Serializer<?> serializer);

  void registerUnion(Class<?> cls, String namespace, String typeName, Serializer<?> serializer);

  /**
   * Register a Serializer for a class, and allocate an auto-grown ID for this class if it's not
   * registered yet.
   *
   * <p><b>NOTE</b>: The registration order is important. If registration order is inconsistent, the
   * allocated ID will be different, and the deserialization will failed !!!
   *
   * @param type class needed to be serialized/deserialized.
   * @param serializerClass serializer class can be created with {@link Serializers#newSerializer}.
   * @param <T> type of class.
   */
  <T> void registerSerializer(Class<T> type, Class<? extends Serializer> serializerClass);

  /**
   * Register a Serializer for a class, and allocate an auto-grown ID for this class if it's not
   * registered yet.
   *
   * <p><b>NOTE</b>: The registration order is important. If registration order is inconsistent, the
   * allocated ID will be different, and the deserialization will failed !!!
   */
  void registerSerializer(Class<?> type, Serializer<?> serializer);

  /**
   * Register a Serializer created by serializerCreator when fory created. And allocate an
   * auto-grown ID for this class if it's not registered yet.
   *
   * <p><b>NOTE</b>: The registration order is important. If registration order is inconsistent, the
   * allocated ID will be different, and the deserialization will failed !!!
   *
   * @param type class needed to be serialized/deserialized.
   * @param serializerCreator serializer creator with param {@link TypeResolver}
   */
  void registerSerializer(Class<?> type, Function<TypeResolver, Serializer<?>> serializerCreator);

  /**
   * Register a class (if not already registered) and then register its serializer class.
   *
   * <p><b>NOTE</b>: The registration order is important. If registration order is inconsistent, the
   * allocated ID will be different, and the deserialization will failed !!!
   *
   * @param type class needed to be serialized/deserialized.
   * @param serializerClass serializer class can be created with {@link Serializers#newSerializer}.
   * @param <T> type of class.
   */
  <T> void registerSerializerAndType(Class<T> type, Class<? extends Serializer> serializerClass);

  /**
   * Register a class (if not already registered) and then register its serializer instance.
   *
   * <p><b>NOTE</b>: The registration order is important. If registration order is inconsistent, the
   * allocated ID will be different, and the deserialization will failed !!!
   */
  void registerSerializerAndType(Class<?> type, Serializer<?> serializer);

  /**
   * Register a class (if not already registered) and then register a serializer created by
   * serializerCreator when fory created.
   *
   * <p><b>NOTE</b>: The registration order is important. If registration order is inconsistent, the
   * allocated ID will be different, and the deserialization will failed !!!
   *
   * @param type class needed to be serialized/deserialized.
   * @param serializerCreator serializer creator with param {@link TypeResolver}
   */
  void registerSerializerAndType(
      Class<?> type, Function<TypeResolver, Serializer<?>> serializerCreator);

  void setSerializerFactory(SerializerFactory serializerFactory);

  TypeResolver getTypeResolver();

  /**
   * Ensure all compilation for serializers and accessors even for lazy initialized serializers.
   * This method will block until all compilation is done.
   *
   * <p>This method is mainly used for graalvm native image build time or trigger compilation ahead
   * for online service ahead to avoid cold start.
   *
   * <p>Note that this method should be invoked after all registrations and invoked only once.
   * Repeated invocations will have no effect.
   */
  void ensureSerializersCompiled();

  /** Return serialized <code>obj</code> as a byte array. */
  byte[] serialize(Object obj);

  /** Return serialized <code>obj</code> as a byte array. */
  byte[] serialize(Object obj, BufferCallback callback);

  /**
   * Serialize <code>obj</code> to a off-heap buffer specified by <code>address</code> and <code>
   * size</code>.
   */
  MemoryBuffer serialize(Object obj, long address, int size);

  /** Serialize data into buffer. */
  MemoryBuffer serialize(MemoryBuffer buffer, Object obj);

  /** Serialize <code>obj</code> to a <code>buffer</code>. */
  MemoryBuffer serialize(MemoryBuffer buffer, Object obj, BufferCallback callback);

  void serialize(OutputStream outputStream, Object obj);

  void serialize(OutputStream outputStream, Object obj, BufferCallback callback);

  /** Deserialize <code>obj</code> from a byte array. */
  Object deserialize(byte[] bytes);

  <T> T deserialize(byte[] bytes, Class<T> type);

  <T> T deserialize(MemoryBuffer buffer, Class<T> type);

  <T> T deserialize(ForyInputStream inputStream, Class<T> type);

  <T> T deserialize(ForyReadableChannel channel, Class<T> type);

  Object deserialize(byte[] bytes, Iterable<MemoryBuffer> outOfBandBuffers);

  /**
   * Deserialize <code>obj</code> from a off-heap buffer specified by <code>address</code> and
   * <code>size</code>.
   */
  Object deserialize(long address, int size);

  /** Deserialize <code>obj</code> from a <code>buffer</code>. */
  Object deserialize(MemoryBuffer buffer);

  Object deserialize(MemoryBuffer buffer, Iterable<MemoryBuffer> outOfBandBuffers);

  Object deserialize(ForyInputStream inputStream);

  Object deserialize(ForyInputStream inputStream, Iterable<MemoryBuffer> outOfBandBuffers);

  Object deserialize(ForyReadableChannel channel);

  Object deserialize(ForyReadableChannel channel, Iterable<MemoryBuffer> outOfBandBuffers);

  /** Deep copy the <code>obj</code>. */
  <T> T copy(T obj);
}
