/****************************************************************************
*
*    Copyright (c) 2020 Vivante Corporation
*
*    Permission is hereby granted, free of charge, to any person obtaining a
*    copy of this software and associated documentation files (the "Software"),
*    to deal in the Software without restriction, including without limitation
*    the rights to use, copy, modify, merge, publish, distribute, sublicense,
*    and/or sell copies of the Software, and to permit persons to whom the
*    Software is furnished to do so, subject to the following conditions:
*
*    The above copyright notice and this permission notice shall be included in
*    all copies or substantial portions of the Software.
*
*    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
*    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
*    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
*    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
*    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
*    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
*    DEALINGS IN THE SOFTWARE.
*
*****************************************************************************/
/** @file */
#ifndef _VSI_NN_OPS_H
#define _VSI_NN_OPS_H

/*------------------------------------
               Includes
  -----------------------------------*/
#include "vsi_nn_platform.h"
#include "vsi_nn_tensor.h"
#include "vsi_nn_types.h"

#if defined(__cplusplus)
extern "C"{
#endif

/*------------------------------------
                Types
  -----------------------------------*/

/**
 * Ovxlib operation type index.
 * Custom ID is start from VSI_NN_OP_CUSTOM_START.
 * Ovxlib internal id is start from VSI_NN_OP_INTERNAL_START.
 * @see include/interface/ops.def
 * @see include/custom/custom_ops.def
 * @see include/internal/internal_ops.def
 */
typedef uint32_t vsi_nn_op_t; enum
{
#define DEF_OP( NAME, ... ) VSI_NN_OP_##NAME,
    #include "interface/ops.def"
#undef DEF_OP
    VSI_NN_OP_NUM,
    VSI_NN_OP_NA = VSI_NN_OP_NUM,
    VSI_NN_OP_CLIENT = VSI_NN_OP_NA + 1,
    // add operation alias for compilation
    VSI_NN_OP_ELTWISEMAX = VSI_NN_OP_MAXIMUM,

    VSI_NN_OP_CUSTOM_START = 0x10000,
#define DEF_OP( NAME, ... ) VSI_NN_OP_##NAME,
    #include "custom/custom_ops.def"
#undef DEF_OP
    VSI_NN_OP_CUSTOM_END,
    VSI_NN_OP_CUSTOM_NUM = VSI_NN_OP_CUSTOM_END - VSI_NN_OP_CUSTOM_START - 1,

    VSI_NN_OP_INTERNAL_START = 0x10000000,
#define DEF_OP( NAME, ... ) VSI_NN_OP_##NAME,
    #include "internal/internal_ops.def"
#undef DEF_OP
    VSI_NN_OP_INTERNAL_END,
    VSI_NN_OP_INTERNAL_NUM = VSI_NN_OP_INTERNAL_END - VSI_NN_OP_INTERNAL_START - 1
};

/** Operation initialization function handler */
typedef vsi_status ( * vsi_nn_op_init_t )
    ( vsi_nn_node_t * );

/** Operation computation function handler */
typedef vsi_status ( * vsi_nn_op_compute_t )
    (
    vsi_nn_node_t *,
    vsi_nn_tensor_t **,
    vsi_nn_tensor_t **
    );

/** Operation deinitialization function handler */
typedef vsi_status ( * vsi_nn_op_deinit_t )
    ( vsi_nn_node_t * );

/** Operation validation function handler */
typedef vsi_bool ( * vsi_nn_op_check_t )
    (
    vsi_nn_node_t *,
    vsi_nn_tensor_t **,
    vsi_nn_tensor_t **
    );

/** Operation setup function handler */
typedef vsi_bool ( * vsi_nn_op_setup_t )
    (
    vsi_nn_node_t *,
    vsi_nn_tensor_t **,
    vsi_nn_tensor_t **
    );

/** Operation optimization function handler */
typedef vsi_status ( * vsi_nn_op_optimize_t )
    (
    vsi_nn_node_t *,
    vsi_nn_tensor_t **,
    vsi_nn_tensor_t **,
    vsi_nn_opt_direction_e
    );

/** Operation runtime interface. */
typedef struct _vsi_nn_op_proc
{
    vsi_nn_op_init_t     init;
    vsi_nn_op_compute_t  compute;
    vsi_nn_op_deinit_t   deinit;
    vsi_nn_op_check_t    check;
    vsi_nn_op_setup_t    setup;
    vsi_nn_op_optimize_t optimize;
    uint32_t            input_num;
    uint32_t            output_num;
} vsi_nn_op_proc_t;

/*------------------------------------
              Functions
  -----------------------------------*/

OVXLIB_API vsi_status vsi_nn_op_common_compute
    (
    vsi_nn_node_t * self,
    vsi_nn_tensor_t ** inputs,
    vsi_nn_tensor_t ** outputs
    );

OVXLIB_API vsi_status vsi_nn_op_common_deinit
    (
    vsi_nn_node_t * self
    );

OVXLIB_API vsi_bool vsi_nn_op_common_setup
    (
    vsi_nn_node_t * node,
    vsi_nn_tensor_t ** inputs,
    vsi_nn_tensor_t ** outputs
    );

/**
 * Setup for eltwise binary op
 * Setup for eltwise binary op, which need broadcast shape.
 *
 * @param[in] node Node handle.
 * @param[in] inputs input tensors.
 * @param[in] outputs output tensors.
 *
 * @return VSI_SUCCESS on success, or error code otherwise.
 */
OVXLIB_API vsi_bool vsi_nn_op_eltwise_setup
    (
    vsi_nn_node_t * node,
    vsi_nn_tensor_t ** inputs,
    vsi_nn_tensor_t ** outputs
    );

OVXLIB_API vsi_status vsi_nn_op_common_optimize
    (
    vsi_nn_node_t * node,
    vsi_nn_tensor_t ** inputs,
    vsi_nn_tensor_t ** outputs
    );

vsi_bool vsi_nn_OpIsValid
    (
    vsi_nn_op_t op
    );

const vsi_nn_op_proc_t * vsi_nn_OpGetProc
    (
    vsi_nn_op_t op
    );

/**
 * Init operation
 * Call operation init process.
 *
 * @param[in] op Operation id.
 * @param[in] node Node handle.
 *
 * @return VSI_SUCCESS on success, or error code otherwise.
 */
vsi_status vsi_nn_OpInit
    (
    vsi_nn_op_t op,
    vsi_nn_node_t * node
    );

/**
 * Build operation with vx backend
 * Call operation compute process, it will build the node with vx backend.
 *
 * @param[in] op Operation id.
 * @param[in] node Node handle.
 * @param[in] inputs Input tensors' handle..
 * @param[in] outputs Output tensors' handle.
 *
 * @return VSI_SUCCESS on success, or error code otherwise.
 */
vsi_status vsi_nn_OpCompute
    (
    vsi_nn_op_t op,
    vsi_nn_node_t * node,
    vsi_nn_tensor_t ** inputs,
    vsi_nn_tensor_t ** outputs
    );

/**
 * Deinit operation
 * Call operation deinit process, free some resource.
 *
 * @param[in] op Operation id.
 * @param[in] node Node handle.
 *
 * @return VSI_SUCCESS on success, or error code otherwise.
 */
vsi_status vsi_nn_OpDeinit
    (
    vsi_nn_op_t op,
    vsi_nn_node_t * node
    );

/**
 * Optimize operation
 * Call operation optimize process.
 * @see vsi_nn_opt_direction_e
 *
 * @param[in] op Operation id.
 * @param[in] node Node handle.
 * @param[in] inputs Input tensors' handle.
 * @param[in] outputs Output tensors' handle.
 * @param[in] driection Current loop direction, use this param to implement
 *                      different optimizations.
 *
 * @return VSI_SUCCESS on success, or error code otherwise.
 */
vsi_status vsi_nn_OpOptimize
    (
    vsi_nn_op_t op,
    vsi_nn_node_t * node,
    vsi_nn_tensor_t ** inputs,
    vsi_nn_tensor_t ** outputs,
    vsi_nn_opt_direction_e direction
    );

/**
 * Validate operation
 * Call operation check process.
 *
 * @param[in] op Operation id.
 * @param[in] node Node handle.
 * @param[in] inputs Input tensors' handle.
 * @param[in] outputs Output tensors' handle.
 *
 * @return VSI_SUCCESS on success, or error code otherwise.
 */
vsi_bool vsi_nn_OpCheck
    (
    vsi_nn_op_t op,
    vsi_nn_node_t * node,
    vsi_nn_tensor_t ** inputs,
    vsi_nn_tensor_t ** outputs
    );

void vsi_nn_OpGetIoNum
    (
    vsi_nn_op_t op,
    vsi_nn_node_t * node,
    vsi_size_t     * input_num,
    vsi_size_t     * output_num
    );

OVXLIB_API vsi_bool vsi_nn_OpGenerateTensor
    (
    vsi_nn_node_t * node,
    vsi_nn_tensor_t ** inputs,
    vsi_nn_tensor_t ** outputs
    );

/**
 * Setup operation
 * Call operation setup process, it runs before computation,
 * Ovxlib usually computes output shapes in this process.
 *
 * @param[in] op Operation id.
 * @param[in] node Node handle.
 * @param[in] inputs Input tensors' handle.
 * @param[in] outputs Output tensors' handle.
 *
 * @return VSI_SUCCESS on success, or error code otherwise.
 */
vsi_bool vsi_nn_OpSetup
    (
    vsi_nn_op_t op,
    vsi_nn_node_t * node,
    vsi_nn_tensor_t ** inputs,
    vsi_nn_tensor_t ** outputs
    );

vsi_bool vsi_nn_OpRegisterOvxInit
    (
    vsi_nn_op_t op,
    vsi_nn_op_compute_t compute
    );

/**
 * Get operation name
 * Get operation name string by operation id.
 *
 * @param[in] op Operation id.
 *
 * @return Operation name on success, or NULL otherwise.
 */
OVXLIB_API const char * vsi_nn_OpGetName
    (
    vsi_nn_op_t op
    );

#if defined(__cplusplus)
}
#endif

/**
 * Declare an operation with process functions.
 */
#define DEF_OP_REG(op,init,compute,deinit,check,setup,optimize,in,out) \
    vsi_nn_op_proc_t vsi_nn_op_##op =\
{\
    /* init       */ init,\
    /* compute    */ compute,\
    /* deinit     */ deinit,\
    /* check      */ check,\
    /* setup      */ setup,\
    /* optimize   */ optimize,\
    /* input_num  */ in,\
    /* output_num */ out \
};

#endif
