/**
 * Copyright (c) 2025 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 "graph/load/model_manager/aipp_utils.h"

#include <sstream>
#include "framework/common/debug/log.h"
#include "framework/common/op/ge_op_utils.h"
#include "graph/utils/type_utils.h"
#include "graph/utils/tensor_utils.h"
#include "common/checker.h"
#include "base/err_msg.h"

namespace ge {
namespace {
constexpr size_t kInvalidIdx = 0xFFFFFFFFUL;
const std::string kDataModeStatic = "static_aipp";
const std::string kDataModeDynamic = "dynamic_aipp";
const std::string kDataModeDynamicConf = "dynamic_aipp_conf";
constexpr int32_t kDecimalRadix = 10;
constexpr uint32_t kDataIndex = 0U;
}

void AippUtils::SetMatrixInfo(const domi::AippOpParams &aipp_params, AippConfigInfo &aipp_info) {
  if (aipp_params.matrix_r0c0_size() > 0) {
    aipp_info.matrix_r0c0 = aipp_params.matrix_r0c0(0);
  }
  if (aipp_params.matrix_r0c1_size() > 0) {
    aipp_info.matrix_r0c1 = aipp_params.matrix_r0c1(0);
  }
  if (aipp_params.matrix_r0c2_size() > 0) {
    aipp_info.matrix_r0c2 = aipp_params.matrix_r0c2(0);
  }
  if (aipp_params.matrix_r1c0_size() > 0) {
    aipp_info.matrix_r1c0 = aipp_params.matrix_r1c0(0);
  }
  if (aipp_params.matrix_r1c1_size() > 0) {
    aipp_info.matrix_r1c1 = aipp_params.matrix_r1c1(0);
  }
  if (aipp_params.matrix_r1c2_size() > 0) {
    aipp_info.matrix_r1c2 = aipp_params.matrix_r1c2(0);
  }
  if (aipp_params.matrix_r2c0_size() > 0) {
    aipp_info.matrix_r2c0 = aipp_params.matrix_r2c0(0);
  }
  if (aipp_params.matrix_r2c1_size() > 0) {
    aipp_info.matrix_r2c1 = aipp_params.matrix_r2c1(0);
  }
  if (aipp_params.matrix_r2c2_size() > 0) {
    aipp_info.matrix_r2c2 = aipp_params.matrix_r2c2(0);
  }
}

void AippUtils::SetBiasInfo(const domi::AippOpParams &aipp_params, AippConfigInfo &aipp_info) {
  if (aipp_params.output_bias_0_size() > 0) {
    aipp_info.output_bias_0 = aipp_params.output_bias_0(0);
  }
  if (aipp_params.output_bias_1_size() > 0) {
    aipp_info.output_bias_1 = aipp_params.output_bias_1(0);
  }
  if (aipp_params.output_bias_2_size() > 0) {
    aipp_info.output_bias_2 = aipp_params.output_bias_2(0);
  }
  if (aipp_params.input_bias_0_size() > 0) {
    aipp_info.input_bias_0 = aipp_params.input_bias_0(0);
  }
  if (aipp_params.input_bias_1_size() > 0) {
    aipp_info.input_bias_1 = aipp_params.input_bias_1(0);
  }
  if (aipp_params.input_bias_2_size() > 0) {
    aipp_info.input_bias_2 = aipp_params.input_bias_2(0);
  }
}

void AippUtils::SetChnInfo(const domi::AippOpParams &aipp_params, AippConfigInfo &aipp_info) {
  aipp_info.mean_chn_0 = aipp_params.mean_chn_0();
  aipp_info.mean_chn_1 = aipp_params.mean_chn_1();
  aipp_info.mean_chn_2 = aipp_params.mean_chn_2();
  aipp_info.mean_chn_3 = aipp_params.mean_chn_3();
  aipp_info.min_chn_0 = aipp_params.min_chn_0();
  aipp_info.min_chn_1 = aipp_params.min_chn_1();
  aipp_info.min_chn_2 = aipp_params.min_chn_2();
  aipp_info.min_chn_3 = aipp_params.min_chn_3();
  if (aipp_params.var_reci_chn_0_size() > 0) {
    aipp_info.var_reci_chn_0 = aipp_params.var_reci_chn_0(0);
  }
  if (aipp_params.var_reci_chn_1_size() > 0) {
    aipp_info.var_reci_chn_1 = aipp_params.var_reci_chn_1(0);
  }
  if (aipp_params.var_reci_chn_2_size() > 0) {
    aipp_info.var_reci_chn_2 = aipp_params.var_reci_chn_2(0);
  }
  if (aipp_params.var_reci_chn_3_size() > 0) {
    aipp_info.var_reci_chn_3 = aipp_params.var_reci_chn_3(0);
  }
}

Status AippUtils::ConvertAippParams2AippInfo(const domi::AippOpParams &aipp_params, AippConfigInfo &aipp_info) {
  aipp_info.aipp_mode = static_cast<int8_t>(aipp_params.aipp_mode());
  aipp_info.input_format = static_cast<int8_t>(aipp_params.input_format());
  aipp_info.related_input_rank = aipp_params.related_input_rank();
  aipp_info.src_image_size_w = aipp_params.src_image_size_w();
  aipp_info.src_image_size_h = aipp_params.src_image_size_h();
  aipp_info.crop = static_cast<int8_t>(aipp_params.crop());
  aipp_info.load_start_pos_w = aipp_params.load_start_pos_w();
  aipp_info.load_start_pos_h = aipp_params.load_start_pos_h();
  aipp_info.crop_size_w = aipp_params.crop_size_w();
  aipp_info.crop_size_h = aipp_params.crop_size_h();
  aipp_info.resize = static_cast<int8_t>(aipp_params.resize());
  aipp_info.resize_output_w = aipp_params.resize_output_w();
  aipp_info.resize_output_h = aipp_params.resize_output_h();
  aipp_info.padding = static_cast<int8_t>(aipp_params.padding());
  aipp_info.left_padding_size = aipp_params.left_padding_size();
  aipp_info.right_padding_size = aipp_params.right_padding_size();
  aipp_info.top_padding_size = aipp_params.top_padding_size();
  aipp_info.bottom_padding_size = aipp_params.bottom_padding_size();
  aipp_info.csc_switch = static_cast<int8_t>(aipp_params.csc_switch());
  aipp_info.rbuv_swap_switch = static_cast<int8_t>(aipp_params.rbuv_swap_switch());
  aipp_info.ax_swap_switch = static_cast<int8_t>(aipp_params.ax_swap_switch());
  aipp_info.single_line_mode = static_cast<int8_t>(aipp_params.single_line_mode());
  aipp_info.support_rotation = static_cast<int8_t>(aipp_params.support_rotation());
  aipp_info.max_src_image_size = aipp_params.max_src_image_size();

  SetMatrixInfo(aipp_params, aipp_info);
  SetBiasInfo(aipp_params, aipp_info);
  SetChnInfo(aipp_params, aipp_info);

  return SUCCESS;
}

Status AippUtils::SetAippInfoAndTypeFromOpDesc(const std::map<std::string, uint32_t> &data_index_map,
                                               const OpDescPtr &op_desc, const uint32_t index,
                                               std::map<uint32_t, AippConfigInfo> &aipp_infos,
                                               std::map<uint32_t, std::pair<InputAippType, size_t>> &aipp_types) {
  GE_CHECK_NOTNULL(op_desc);
  NamedAttrs aipp_attr;
  const bool get_attr_aipp = AttrUtils::GetNamedAttrs(op_desc, ATTR_NAME_AIPP, aipp_attr);
  const std::string *data_mode_ptr = AttrUtils::GetStr(op_desc, ATTR_DATA_RELATED_AIPP_MODE);
  const bool get_attr_mode = (data_mode_ptr != nullptr);
  std::string data_mode = "";
  if (get_attr_mode) {
    data_mode = *data_mode_ptr;
  }
  if ((!get_attr_aipp) && (!get_attr_mode)) {
    GELOGD("There is not AIPP related with op:%s, index:%u.", op_desc->GetName().c_str(), index);
    return SUCCESS;
  }
  if ((!get_attr_aipp) || (!get_attr_mode)) {
    std::stringstream error_message;
    error_message << "Both ATTR_NAME_AIPP and ATTR_DATA_RELATED_AIPP_MODE attributes are needed on the data node, "
                     "but only " << (get_attr_aipp ? "ATTR_NAME_AIPP" : "ATTR_DATA_RELATED_AIPP_MODE")
                  << " is obtained at the time.";
    REPORT_INNER_ERR_MSG("E19999", "Op:%s, index:%u, error message:%s", op_desc->GetName().c_str(), index,
                       error_message.str().c_str());
    GELOGE(INTERNAL_ERROR, "[Get][Attrs]Op:%s, index:%u, error message:%s", op_desc->GetName().c_str(), index,
           error_message.str().c_str());
    return INTERNAL_ERROR;
  }

  auto ret = SetAippInfoImpl(aipp_attr, op_desc, index, aipp_infos);
  if (ret != SUCCESS) {
    GELOGE(ret, "[Set][AippInfo]Failed to set aipp info, op:%s, index:%u.", op_desc->GetName().c_str(), index);
    return ret;
  }
  ret = SetAippTypeImpl(data_index_map, data_mode, op_desc, index, aipp_types);
  if (ret != SUCCESS) {
    GELOGE(ret, "[Set][AippType]Failed to set aipp type, op:%s, index:%u.", op_desc->GetName().c_str(), index);
    return ret;
  }
  return SUCCESS;
}

Status AippUtils::SetAippInfoImpl(const NamedAttrs &aipp_attr, const OpDescPtr &op_desc, const uint32_t index,
                                  std::map<uint32_t, AippConfigInfo> &aipp_infos) {
  domi::AippOpParams aipp_params;
  Status ret = OpUtils::ConvertAippParams(aipp_attr, aipp_params);
  if (ret != SUCCESS) {
    GELOGE(INTERNAL_ERROR, "[Convert][AippParams] Failed to convert aipp params, op:%s.", op_desc->GetName().c_str());
    return INTERNAL_ERROR;
  }
  GELOGI("Add aipp info for node:%s, type:%s, current index:%u, current node related input rank:%u",
         op_desc->GetName().c_str(), op_desc->GetType().c_str(), index, aipp_params.related_input_rank());

  AippConfigInfo aipp_info;
  ret = AippUtils::ConvertAippParams2AippInfo(aipp_params, aipp_info);
  if (ret != SUCCESS) {
    GELOGE(INTERNAL_ERROR, "[Convert][AippInfo]Failed to convert params to info, op:%s.", op_desc->GetName().c_str());
    return INTERNAL_ERROR;
  }
  aipp_infos[index] = aipp_info;
  return SUCCESS;
}

Status AippUtils::SetAippTypeImpl(const std::map<std::string, uint32_t> &data_index_map,
                                  const std::string &data_mode, const OpDescPtr &op_desc, const uint32_t index,
                                  std::map<uint32_t, std::pair<InputAippType, size_t>> &aipp_types) {
  InputAippType aipp_type = DATA_WITHOUT_AIPP;
  if (data_mode == kDataModeStatic) {
    aipp_type = DATA_WITH_STATIC_AIPP;
  } else if (data_mode == kDataModeDynamic) {
    aipp_type = DATA_WITH_DYNAMIC_AIPP;
  } else if (data_mode == kDataModeDynamicConf) {
    aipp_type = DYNAMIC_AIPP_NODE;
  } else {
    REPORT_INNER_ERR_MSG("E19999", "Get invalid mode:%s, op:%s, type:%s.", data_mode.c_str(),
                       op_desc->GetName().c_str(), op_desc->GetType().c_str());
    GELOGE(INTERNAL_ERROR, "[Get][Attr]Get invalid mode:%s, op:%s, type:%s.", data_mode.c_str(),
           op_desc->GetName().c_str(), op_desc->GetType().c_str());
    return INTERNAL_ERROR;
  }

  size_t aipp_data_index = kInvalidIdx;
  if (aipp_type == DATA_WITH_DYNAMIC_AIPP) {
    const std::string *related_name = AttrUtils::GetStr(op_desc, ATTR_DATA_AIPP_DATA_NAME_MAP);
    if (related_name == nullptr) {
      REPORT_INNER_ERR_MSG("E19999", "Failed to get attr:%s, op:%s, type:%s.", ATTR_DATA_AIPP_DATA_NAME_MAP.c_str(),
                           op_desc->GetName().c_str(), op_desc->GetType().c_str());
      GELOGE(INTERNAL_ERROR, "[Get][Attr]Failed to get attr:%s, op:%s, type:%s.", ATTR_DATA_AIPP_DATA_NAME_MAP.c_str(),
             op_desc->GetName().c_str(), op_desc->GetType().c_str());
      return INTERNAL_ERROR;
    }
    const auto iter = data_index_map.find(*related_name);
    if (iter != data_index_map.end()) {
      aipp_data_index = iter->second;
      GELOGI("Find AippData:%s of index:%zu for op:%s, index:%u", related_name->c_str(), aipp_data_index,
             op_desc->GetName().c_str(), index);
    } else {
      REPORT_INNER_ERR_MSG("E19999", "Cannot find AippData node for index:%u, op:%s", index, op_desc->GetName().c_str());
      GELOGE(INTERNAL_ERROR, "[Find][AippData]Cannot find AippData node for index:%u, op:%s",
             index, op_desc->GetName().c_str());
      return INTERNAL_ERROR;
    }
  }
  aipp_types[index] = {aipp_type, aipp_data_index};
  return SUCCESS;
}

Status AippUtils::GetAippInfo(const std::map<uint32_t, AippConfigInfo> &aipp_infos, const uint32_t index,
                              AippConfigInfo &aipp_info) {
  const auto it = aipp_infos.find(index);
  if (it == aipp_infos.end()) {
    GELOGD("There is not AIPP related info with index:%u", index);
    return ACL_ERROR_GE_AIPP_NOT_EXIST;
  }
  aipp_info = it->second;
  return SUCCESS;
}

Status AippUtils::GetAippType(const std::map<uint32_t, std::pair<InputAippType, size_t>> &aipp_types,
                              const uint32_t index, InputAippType &aipp_type, size_t &aipp_data_index) {
  const auto it = aipp_types.find(index);
  if (it == aipp_types.end()) {
    GELOGD("There is not AIPP related type with index:%u, return default value.", index);
    aipp_type = DATA_WITHOUT_AIPP;
    aipp_data_index = kInvalidIdx;
    return SUCCESS;
  }
  aipp_type = it->second.first;
  aipp_data_index = it->second.second;
  return SUCCESS;
}

Status AippUtils::ParseAIPPInfo(const std::string &in_out_info, InputOutputDims &dims_info) {
  GELOGI("ParseAIPPInfo: origin str: %s", in_out_info.c_str());
  const std::vector<std::string> infos = StringUtils::Split(in_out_info, ':');
  if (infos.size() != kAippInfoNum) {
    REPORT_INNER_ERR_MSG("E19999", "in_out_info:%s size:%zu != kAippInfoNum:%u check invalid",
        in_out_info.c_str(), infos.size(), kAippInfoNum);
    GELOGE(ACL_ERROR_GE_AIPP_MODE_INVALID, "[Check][Param] in_out_info:%s size:%zu != kAippInfoNum:%u",
        in_out_info.c_str(), infos.size(), kAippInfoNum);
    return ACL_ERROR_GE_AIPP_MODE_INVALID;
  }
  dims_info.name = infos[kAippInfoTensorName];
  dims_info.size = static_cast<uint32_t>(std::strtol(infos[kAippInfoTensorSize].c_str(), nullptr, kDecimalRadix));
  dims_info.dim_num = static_cast<size_t>(std::strtol(infos[kAippInfoDimNum].c_str(), nullptr, kDecimalRadix));

  const std::vector<std::string> dims = StringUtils::Split(infos[kAippInfoShape], ',');
  for (const auto &dim : dims) {
    if (dim.empty()) {
      continue;
    }
    dims_info.dims.emplace_back(std::strtol(dim.c_str(), nullptr, kDecimalRadix));
  }
  return SUCCESS;
}

void AippUtils::SetOrigInputInfo(const std::string &input, const uint32_t index,
    std::map<uint32_t, OriginInputInfo> &orig_input_info_map) {
  GELOGI("origin input str: %s.", input.c_str());
  const std::vector<std::string> infos = StringUtils::Split(input, ':');
  OriginInputInfo input_info{};
  input_info.format = TypeUtils::SerialStringToFormat(infos[kAippInfoFormat]);
  input_info.data_type = TypeUtils::SerialStringToDataType(infos[kAippInfoDataType]);
  input_info.dim_num = static_cast<uint32_t>(std::strtol(infos[kAippInfoDimNum].c_str(), nullptr, kDecimalRadix));
  orig_input_info_map[index] = input_info;
  return;
}

Status AippUtils::SetAippInputOutputInfoFromOpDesc(const OpDescPtr &op_desc, const uint32_t index,
      std::map<uint32_t, OriginInputInfo> &orig_input_info_map,
      std::map<uint32_t, std::pair<std::vector<InputOutputDims>, std::vector<InputOutputDims>>> &aipp_dims_info) {
  if ((!op_desc->HasAttr(ATTR_NAME_AIPP_INPUTS)) || (!op_desc->HasAttr(ATTR_NAME_AIPP_OUTPUTS))) {
    GELOGI("there is not AIPP related with index %u, node: %s.", index, op_desc->GetName().c_str());
    return SUCCESS;
  }

  std::vector<std::string> inputs;
  std::vector<InputOutputDims> input_dims;
  orig_input_info_map[index] = { FORMAT_RESERVED, DT_UNDEFINED, 0U };
  if (AttrUtils::GetListStr(op_desc, ATTR_NAME_AIPP_INPUTS, inputs) && (!inputs.empty())) {
    GELOGI("Data: %s has %zu related aippInfo.", op_desc->GetName().c_str(), inputs.size());
    for (const auto &it : inputs) {
      InputOutputDims input_info;
      GE_CHK_STATUS_RET_NOLOG(ParseAIPPInfo(it, input_info));
      input_dims.emplace_back(input_info);
      GELOGD("Aipp origin input dims info: %s", it.c_str());

      const auto data_input_desc = op_desc->GetInputDescPtr(kDataIndex);
      GE_CHECK_NOTNULL(data_input_desc);
      int64_t data_input_size = 0;
      (void)TensorUtils::GetSize(*(data_input_desc), data_input_size);
      GELOGD("Related Data[%d]: tensor_name: %s, dim_num: %zu, tensor_size: %zu, format: %s, data_type: %s, shape: %s.",
          index, op_desc->GetName().c_str(), data_input_desc->GetShape().GetDimNum(), data_input_size,
          TypeUtils::FormatToSerialString(data_input_desc->GetFormat()).c_str(),
          TypeUtils::DataTypeToSerialString(data_input_desc->GetDataType()).c_str(),
          ToString(data_input_desc->GetShape().GetDims()).c_str());
    }
    SetOrigInputInfo(inputs[kAippOriginInputIndex], index, orig_input_info_map);
  }

  std::vector<std::string> outputs;
  std::vector<InputOutputDims> output_dims;
  if (AttrUtils::GetListStr(op_desc, ATTR_NAME_AIPP_OUTPUTS, outputs) && (!outputs.empty())) {
    for (const auto &it : outputs) {
      InputOutputDims output_info;
      GE_CHK_STATUS_RET_NOLOG(ParseAIPPInfo(it, output_info));
      output_dims.emplace_back(output_info);
      GELOGD("Aipp output dims info: %s", it.c_str());
    }
  }

  aipp_dims_info[index] = { input_dims, output_dims };
  return SUCCESS;
}

Status AippUtils::GetOrigInputInfo(const std::map<uint32_t, OriginInputInfo> &orig_input_info_map, const uint32_t index,
    OriginInputInfo &orig_input_info) {
  const auto it = orig_input_info_map.find(index);
  if (it == orig_input_info_map.end()) {
    REPORT_INNER_ERR_MSG("E19999", "Get index:%u from orig_input_info_map fail", index);
    GELOGE(ACL_ERROR_GE_AIPP_NOT_EXIST, "[Check][Param] Get index:%u from orig_input_info_map fail", index);
    return ACL_ERROR_GE_AIPP_NOT_EXIST;
  }

  const OriginInputInfo &input_info = it->second;
  if ((input_info.format != FORMAT_RESERVED) || (input_info.data_type != DT_UNDEFINED)) {
    orig_input_info = input_info;
  }

  return SUCCESS;
}

Status AippUtils::GetAllAippInputOutputDims(
      const std::map<uint32_t,
      std::pair<std::vector<InputOutputDims>, std::vector<InputOutputDims>>> &aipp_dims_info,
      const uint32_t index, std::vector<InputOutputDims> &input_dims, std::vector<InputOutputDims> &output_dims) {
  const auto it = aipp_dims_info.find(index);
  if (it == aipp_dims_info.end()) {
    REPORT_INNER_ERR_MSG("E19999", "Get index:%u from aipp_dims_info fail", index);
    GELOGE(ACL_ERROR_GE_AIPP_NOT_EXIST, "[Check][Param] Get index:%u from aipp_dims_info fail", index);
    return ACL_ERROR_GE_AIPP_NOT_EXIST;
  }

  input_dims = it->second.first;
  output_dims = it->second.second;
  return SUCCESS;
}
}  // namespace ge