// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2021-2022 Xilinx, Inc
// Copyright (C) 2023-2024 Advanced Micro Devices, Inc. - All rights reserved

// ------ I N C L U D E   F I L E S -------------------------------------------
// Local - Include Files
#include "ReportAieShim.h"

#include "aie/ReportAie2Shim.h"
#include "core/common/info_aie.h"
#include "core/common/query_requests.h"

#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/split.hpp>

#define fmt4(x) boost::format("%4s%-22s: " x "\n") % " "
#define fmt8(x) boost::format("%8s%-22s: " x "\n") % " "
#define fmt12(x) boost::format("%12s%-22s: " x "\n") % " "
#define fmt16(x) boost::format("%16s%-22s: " x "\n") % " "

boost::property_tree::ptree
populate_aie_shim(const xrt_core::device * _pDevice, const std::string& desc)
{
  boost::property_tree::ptree pt_shim = xrt_core::aie::aie_shim(_pDevice);
  pt_shim.put("description", desc);
  return pt_shim;
}

void
ReportAieShim::getPropertyTreeInternal(const xrt_core::device* _pDevice,
                                       boost::property_tree::ptree &_pt) const
{
  // Defer to the 20202 format.  If we ever need to update JSON data, 
  // Then update this method to do so.
  getPropertyTree20202(_pDevice, _pt);
}

void 
ReportAieShim::getPropertyTree20202(const xrt_core::device* _pDevice,
                                    boost::property_tree::ptree &_pt) const
{
  switch (xrt_core::device_query<xrt_core::query::device_class>(_pDevice)) {
  case xrt_core::query::device_class::type::alveo:
    _pt.add_child("aie_shim_status", populate_aie_shim(_pDevice, "Aie_Shim_Status"));
    break;
  case xrt_core::query::device_class::type::ryzen:
    ReportAie2Shim{}.getPropertyTree20202(_pDevice, _pt);
    break;
  }
}

void 
ReportAieShim::writeReport(const xrt_core::device* _pDevice,
                           const boost::property_tree::ptree& _pt,
                           const std::vector<std::string>& _elementsFilter,
                           std::ostream & _output) const
{
  switch (xrt_core::device_query<xrt_core::query::device_class>(_pDevice)) {
  case xrt_core::query::device_class::type::alveo:
    break;
  case xrt_core::query::device_class::type::ryzen:
    ReportAie2Shim{}.writeReport(_pDevice, _pt, _elementsFilter, _output);
    return;
  }

  boost::property_tree::ptree empty_ptree;
  std::vector<std::string> aieTileList;

  _output << "AIE Shim\n";

  // Loop through all the parameters given under _elementsFilter i.e. -e option
  for (auto it = _elementsFilter.begin(); it != _elementsFilter.end(); ++it) {
    // Only show certain selected tiles from aieshim that are passed under tiles
    // Ex. -r aieshim -e tiles 0,3,5
    if (*it == "tiles") {
      auto tile_list = std::next(it);
      if (tile_list != _elementsFilter.end())
        boost::split(aieTileList, *tile_list, boost::is_any_of(","));
    }
  }

  try {
    int count = 0;
    const boost::property_tree::ptree ptShimTiles = _pt.get_child("aie_shim_status.tiles", empty_ptree);

    if (ptShimTiles.empty()) {
      _output << "  No AIE columns are active on the device" << std::endl << std::endl;
      return;
    }

    _output << "  Shim Status" << std::endl;

    for (const auto& tile : ptShimTiles) {
      int curr_tile = count++;
      if(aieTileList.size() && (std::find(aieTileList.begin(), aieTileList.end(),
	                       std::to_string(curr_tile)) == aieTileList.end()))
        continue;

      _output << boost::format("Tile[%2d]\n") % curr_tile;
      _output << fmt4("%d") % "Column" % tile.second.get<int>("column");
      _output << fmt4("%d") % "Row" % tile.second.get<int>("row");

      if (tile.second.find("dma") != tile.second.not_found()) {
        _output << boost::format("    %s:\n") % "DMA";

        _output << boost::format("%12s:\n") % "FIFO";
        for (const auto& node : tile.second.get_child("dma.fifo.counters")) {
          _output << fmt16("%s") % node.second.get<std::string>("index")
                    % node.second.get<std::string>("count");
        }

        _output << boost::format("        %s:\n") % "MM2S";

        _output << boost::format("            %s:\n") % "Channel";
        for (const auto& node : tile.second.get_child("dma.mm2s.channel")) {
          _output << fmt16("%s") % "Id" % node.second.get<std::string>("id");
          _output << fmt16("%s") % "Channel Status" % node.second.get<std::string>("channel_status");
          _output << fmt16("%s") % "Queue Size" % node.second.get<std::string>("queue_size");
          _output << fmt16("%s") % "Queue Status" % node.second.get<std::string>("queue_status");
          _output << fmt16("%s") % "Current BD" % node.second.get<std::string>("current_bd");
          _output << std::endl;
        }

        _output << boost::format("        %s:\n") % "S2MM";

        _output << boost::format("            %s:\n") % "Channel";
        for (const auto& node : tile.second.get_child("dma.s2mm.channel")) {
          _output << fmt16("%s") % "Id" % node.second.get<std::string>("id");
          _output << fmt16("%s") % "Channel Status" % node.second.get<std::string>("channel_status");
          _output << fmt16("%s") % "Queue Size" % node.second.get<std::string>("queue_size");
          _output << fmt16("%s") % "Queue Status" % node.second.get<std::string>("queue_status");
          _output << fmt16("%s") % "Current BD" % node.second.get<std::string>("current_bd");
          _output << std::endl;
        }
      }

      if (tile.second.find("locks") != tile.second.not_found()) {
        _output << boost::format("    %s:\n") % "Locks";
        for(auto& node : tile.second.get_child("locks",empty_ptree)) {
          _output << fmt8("%s")  % node.second.get<std::string>("name")
                                 % node.second.get<std::string>("value");
        }
        _output << std::endl;
      }

      if (tile.second.find("errors") != tile.second.not_found()) {
        _output << boost::format("    %s:\n") % "Errors";
        for(auto& node : tile.second.get_child("errors",empty_ptree)) {
          _output << boost::format("        %s:\n") % node.second.get<std::string>("module");
          for(auto& enode : node.second.get_child("error",empty_ptree)) {
            _output << fmt12("%s")  % enode.second.get<std::string>("name")
                                    % enode.second.get<std::string>("value");
          }
        }
        _output << std::endl;
      }

      if (tile.second.find("events") != tile.second.not_found()) {
        _output << boost::format("    %s:\n") % "Events";
        for(auto& node : tile.second.get_child("events",empty_ptree)) {
          _output << fmt8("%s")  % node.second.get<std::string>("name")
                                 % node.second.get<std::string>("value");
        }
        _output << std::endl;
      }

      if (tile.second.find("bd_info") != tile.second.not_found()) {
        _output << boost::format("    %s:\n") % "BDs";
        for (const auto& bd_info : tile.second.get_child("bd_info")) {
          _output << fmt8("%s") %  "bd_num: " % bd_info.second.get<std::string>("bd_num");
          for (const auto& bd_detail : bd_info.second.get_child("bd_details")) 
            _output<< fmt8("%s") % bd_detail.second.get<std::string>("name") % bd_detail.second.get<std::string>("value");
          _output << std::endl;
        }
      }
    }
  } catch (std::exception const& e) {
    _output <<  e.what() << std::endl;
  }
}
