/*
 * Copyright (c) 2024 Huawei Technologies Co., Ltd.
 * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
 * CANN Open Software License Agreement Version 2.0 (the "License").
 * Please refer to the License for details. You may not use this file except in compliance with the License.
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
 * See LICENSE in the root of the software repository for the full text of the License.
 */
#include "atb/utils/runner_util.h"
#include <map>
#include <asdops/params/params.h>
#include "atb/utils/log.h"

static constexpr size_t DIM_2 = 2;
static constexpr size_t SIZE_2 = 2;
static constexpr size_t SIZE_3 = 3;

namespace atb {
Mki::OpDesc RunnerUtil::GetActivationNodeOpDesc(infer::ActivationParam activationParam)
{
    std::map<infer::ActivationType, AsdOps::OpParam::Activation::ActivationType> typeTable = {
        {infer::ActivationType::ACTIVATION_RELU, AsdOps::OpParam::Activation::ACTIVATION_RELU},
        {infer::ActivationType::ACTIVATION_GELU, AsdOps::OpParam::Activation::ACTIVATION_GELU},
        {infer::ActivationType::ACTIVATION_FAST_GELU, AsdOps::OpParam::Activation::ACTIVATION_FAST_GELU},
        {infer::ActivationType::ACTIVATION_SWISH, AsdOps::OpParam::Activation::ACTIVATION_SWISH},
        {infer::ActivationType::ACTIVATION_LOG, AsdOps::OpParam::Activation::ACTIVATION_LOG},
        {infer::ActivationType::ACTIVATION_SWIGLU_FORWARD, AsdOps::OpParam::Activation::ACTIVATION_SWIGLU_FORWARD},
        {infer::ActivationType::ACTIVATION_SWIGLU_BACKWARD, AsdOps::OpParam::Activation::ACTIVATION_SWIGLU_BACKWARD},
        {infer::ActivationType::ACTIVATION_SIGMOID, AsdOps::OpParam::Activation::ACTIVATION_SIGMOID},
        {infer::ActivationType::ACTIVATION_FASTER_GELU_FORWARD,
         AsdOps::OpParam::Activation::ACTIVATION_FASTER_GELU_FORWARD},
    };

    std::map<infer::ActivationType, AsdOps::OpParam::Activation::ActivationType>::const_iterator it =
        typeTable.find(activationParam.activationType);
    AsdOps::OpParam::Activation param = {};
    param.activationType = (it == typeTable.end()) ? AsdOps::OpParam::Activation::ACTIVATION_UNDEFINED : it->second;
    param.scale = activationParam.scale;
    param.dim = activationParam.dim;
    param.approx = activationParam.geluMode;
    return {0, "ActivationOperation", param};
}

/**
 * transdata前,保证矩阵为3维,若为2维或合轴后2维,则第0维补1
 * @param transdataNode
 * @param needMergeAxis
 */
void RunnerUtil::TransdataSqueeze(KernelGraphNode &transdataNode, bool needMergeAxis)
{
    size_t viewFuncsSize = transdataNode.inTensors.size();
    if (viewFuncsSize < 1) {
        ATB_LOG(ERROR) << "node inTensorViewFuncs size error!";
        return;
    }
    transdataNode.inTensorViewFuncs.resize(viewFuncsSize);
    transdataNode.inTensorViewFuncs.at(0) = [needMergeAxis](const Mki::SVector<int64_t> &oldDims,
                                                            Mki::SVector<int64_t> &newDims) {
        if (oldDims.size() == SIZE_2) {
            newDims = {1, oldDims.at(0), oldDims.at(1)};
        } else if (oldDims.size() == SIZE_3 && needMergeAxis) {
            newDims = {1, oldDims.at(0) * oldDims.at(1), oldDims.at(2)};
        } else {
            newDims = oldDims;
        }
    };
}

/**
 * 将维度值进行对齐
 * @param dim 维度值
 * @param align 对齐值
 * @return 对齐后的维度值
 */
int64_t RunnerUtil::AlignUp(int64_t dim, int64_t align)
{
    if (align == 0) {
        return -1;
    }
    return (dim + align - 1) / align * align;
}

/**
 * 配置matmulNode的inTensorViewFuncs,根据需求进行合轴或reshape
 * @param matmulNode
 * @param needMergeAxis 是否需要合轴;当X矩阵为3维,且Y矩阵为2维,则需将X矩阵前两维合轴
 * @param needReshape 是否需要进行reshape;当权重矩阵为NZ时,若为2维或3维,进行reshape
 * @param align
 */
void RunnerUtil::ConfigViewFuncs(KernelGraphNode &matmulNode, bool needMergeAxis, bool needReshape, int64_t align)
{
    size_t viewFuncSize = matmulNode.inTensors.size();
    if (viewFuncSize < SIZE_2) {
        ATB_LOG(ERROR) << "node inTensorViewFuncs size error!";
        return;
    }
    matmulNode.inTensorViewFuncs.resize(viewFuncSize);
    matmulNode.inTensorViewFuncs.at(0) = [needMergeAxis](const Mki::SVector<int64_t> &oldDims,
                                                         Mki::SVector<int64_t> &newDims) {
        newDims = oldDims;
        if (needMergeAxis && oldDims.size() == SIZE_3) {
            newDims = {oldDims.at(0) * oldDims.at(1), oldDims.at(DIM_2)};
        }
        ATB_LOG_IF(newDims != oldDims, INFO) << " - Merge axis, Before: " << oldDims << "; After: " << newDims;
    };
    if (needReshape) {
        if (align == 0) {
            ATB_LOG(ERROR) << "align should not be 0";
            return;
        }
        matmulNode.inTensorViewFuncs.at(1) = [align](const Mki::SVector<int64_t> &oldDims,
                                                     Mki::SVector<int64_t> &newDims) {
            if (oldDims.size() == SIZE_2) {
                newDims = {1, AlignUp(oldDims.at(1), align) / align, AlignUp(oldDims.at(0), DEFAULT_ALIGN), align};
            } else if (oldDims.size() == SIZE_3) {
                newDims = {oldDims.at(0), AlignUp(oldDims.at(DIM_2), align) / align,
                           AlignUp(oldDims.at(1), DEFAULT_ALIGN), align};
            } else {
                newDims = oldDims;
            }
            ATB_LOG_IF(newDims != oldDims, INFO) << " - Weight reshape, Before: " << oldDims << "; After: " << newDims;
        };
    }
}

bool RunnerUtil::InitGraphRunnerNode(GraphRunner::Node &node, Operation *operation,
                                     const std::vector<int64_t> &operationIds, ContextBase &context)
{
    if (!operation) {
        return false;
    }
    node.op.reset(operation);
    OperationBase *opBase = dynamic_cast<OperationBase *>(operation);
    if (!opBase) {
        ATB_LOG(ERROR) << "dynamic_cast operation to OperationBase failed!";
        return false;
    }
    node.runner = opBase->CreateRunner(context);
    if (!node.runner) {
        ATB_LOG(ERROR) << "CreateRunner failed!";
        return false;
    }
    node.runner->SetRunnerInfo(node.op->GetName(), operationIds);
    return true;
}
} // namespace atb