// File: kernel.cpp
#include "conv2d_contract.h"
#include "convolution_arm.h"
#include "mat.h"
#include "option.h"
#include <cstring>

int ncnn::convolution2d_kernel(
    const Mat& bottom_blob, Mat& top_blob,
    const Mat& weight_data, const Mat& bias_data,
    int kernel_h, int kernel_w,
    int stride_h, int stride_w,
    int dilation_h, int dilation_w,
    int pad_top, int pad_left,
    int activation_type,
    const Option& opt)
{
    const int num_output = top_blob.c;  // pre-set by binding.cpp

    Convolution_arm conv;
    conv.num_output       = num_output;
    conv.kernel_h         = kernel_h;    conv.kernel_w   = kernel_w;
    conv.stride_h         = stride_h;   conv.stride_w   = stride_w;
    conv.dilation_h       = dilation_h; conv.dilation_w = dilation_w;
    conv.pad_top          = pad_top;    conv.pad_bottom = pad_top;
    conv.pad_left         = pad_left;   conv.pad_right  = pad_left;
    conv.pad_value        = 0.f;
    conv.bias_term        = (!bias_data.empty() && bias_data.total() > 0) ? 1 : 0;
    conv.weight_data_size = static_cast<int>(weight_data.total());
    conv.int8_scale_term  = 0;
    conv.activation_type  = activation_type;
    conv.activation_params = Mat();
    conv.dynamic_weight   = 0;
    conv.weight_data      = const_cast<Mat&>(weight_data);
    if (conv.bias_term) conv.bias_data = const_cast<Mat&>(bias_data);

    if (conv.create_pipeline(opt) != 0) return -1;

    Mat local_top;
    if (conv.forward(bottom_blob, local_top, opt) != 0) return -1;

    // Copy from local_top to pre-allocated top_blob, channel by channel.
    for (int c = 0; c < num_output; ++c)
        std::memcpy((float*)top_blob.channel(c), (const float*)local_top.channel(c),
                    top_blob.h * top_blob.w * sizeof(float));
    return 0;
}