/******************************************************************************
 *
 * Project:  GDAL
 * Purpose:  gdal "raster info" subcommand
 * Author:   Even Rouault <even dot rouault at spatialys.com>
 *
 ******************************************************************************
 * Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
 *
 * SPDX-License-Identifier: MIT
 ****************************************************************************/

#include "gdalalg_raster_info.h"

#include "cpl_conv.h"
#include "gdal_priv.h"
#include "gdal_utils.h"

//! @cond Doxygen_Suppress

#ifndef _
#define _(x) (x)
#endif

/************************************************************************/
/*          GDALRasterInfoAlgorithm::GDALRasterInfoAlgorithm()          */
/************************************************************************/

GDALRasterInfoAlgorithm::GDALRasterInfoAlgorithm(bool standaloneStep,
                                                 bool openForMixedRasterVector)
    : GDALRasterPipelineStepAlgorithm(
          NAME, DESCRIPTION, HELP_URL,
          ConstructorOptions()
              .SetStandaloneStep(standaloneStep)
              .SetInputDatasetMaxCount(1)
              .SetAddDefaultArguments(false)
              .SetInputDatasetHelpMsg(_("Input raster dataset"))
              .SetInputDatasetAlias("dataset"))
{
    if (standaloneStep)
    {
        AddRasterInputArgs(openForMixedRasterVector,
                           /* hiddenForCLI = */ false);
    }
    else
    {
        AddRasterHiddenInputDatasetArg();
    }

    AddOutputFormatArg(&m_format).SetChoices("json", "text");
    AddArg("min-max", 0, _("Compute minimum and maximum value"), &m_minMax)
        .AddAlias("mm");
    AddArg("stats", 0, _("Retrieve or compute statistics, using all pixels"),
           &m_stats)
        .SetMutualExclusionGroup("stats");
    AddArg("approx-stats", 0,
           _("Retrieve or compute statistics, using a subset of pixels"),
           &m_approxStats)
        .SetMutualExclusionGroup("stats");
    AddArg("hist", 0, _("Retrieve or compute histogram"), &m_hist);

    AddArg("no-gcp", 0, _("Suppress ground control points list printing"),
           &m_noGCP)
        .SetCategory(GAAC_ADVANCED);
    AddArg("no-md", 0, _("Suppress metadata printing"), &m_noMD)
        .SetCategory(GAAC_ADVANCED);
    AddArg("no-ct", 0, _("Suppress color table printing"), &m_noCT)
        .SetCategory(GAAC_ADVANCED);
    AddArg("no-fl", 0, _("Suppress file list printing"), &m_noFL)
        .SetCategory(GAAC_ADVANCED);
    AddArg("checksum", 0, _("Compute pixel checksum"), &m_checksum)
        .SetCategory(GAAC_ADVANCED);
    AddArg("list-mdd", 0,
           _("List all metadata domains available for the dataset"), &m_listMDD)
        .AddAlias("list-metadata-domains")
        .SetCategory(GAAC_ADVANCED);
    AddArg("metadata-domain", 0,
           _("Report metadata for the specified domain. 'all' can be used to "
             "report metadata in all domains"),
           &m_mdd)
        .AddAlias("mdd")
        .SetCategory(GAAC_ADVANCED);

    AddArg("no-nodata", 0, _("Suppress retrieving nodata value"), &m_noNodata)
        .SetCategory(GAAC_ESOTERIC);
    AddArg("no-mask", 0, _("Suppress mask band information"), &m_noMask)
        .SetCategory(GAAC_ESOTERIC);
    AddArg("subdataset", 0,
           _("Use subdataset of specified index (starting at 1), instead of "
             "the source dataset itself"),
           &m_subDS)
        .SetCategory(GAAC_ESOTERIC)
        .SetMinValueIncluded(1);
    AddArg("crs-format", 0, _("Which format to use to report CRS"),
           &m_crsFormat)
        .SetChoices("AUTO", "WKT2", "PROJJSON")
        .SetDefault(m_crsFormat)
        .SetCategory(GAAC_ESOTERIC);

    AddOutputStringArg(&m_output);
    AddStdoutArg(&m_stdout);

    AddValidationAction(
        [this]()
        {
            if (m_crsFormat != "AUTO" && m_format == "json")
            {
                ReportError(CE_Failure, CPLE_AppDefined,
                            "'crs-format' cannot be set when 'format' is set "
                            "to 'json'");
                return false;
            }
            return true;
        });
}

/************************************************************************/
/*                  GDALRasterInfoAlgorithm::RunStep()                  */
/************************************************************************/

bool GDALRasterInfoAlgorithm::RunStep(GDALPipelineStepRunContext &)
{
    CPLAssert(m_inputDataset.size() == 1);
    auto poSrcDS = m_inputDataset[0].GetDatasetRef();
    CPLAssert(poSrcDS);

    if (m_format.empty())
        m_format = IsCalledFromCommandLine() ? "text" : "json";

    CPLStringList aosOptions;
    aosOptions.AddString("--invoked-from=gdal-raster-info");
    aosOptions.AddString("--crs-format=" + m_crsFormat);
    if (m_format == "json")
        aosOptions.AddString("-json");
    if (m_minMax)
        aosOptions.AddString("-mm");
    if (m_stats)
        aosOptions.AddString("-stats");
    if (m_approxStats)
        aosOptions.AddString("-approx_stats");
    if (m_hist)
        aosOptions.AddString("-hist");
    if (m_noGCP)
        aosOptions.AddString("-nogcp");
    if (m_noMD)
        aosOptions.AddString("-nomd");
    if (m_noCT)
        aosOptions.AddString("-noct");
    if (m_noFL)
        aosOptions.AddString("-nofl");
    if (m_noMask)
        aosOptions.AddString("-nomask");
    if (m_noNodata)
        aosOptions.AddString("-nonodata");
    if (m_checksum)
        aosOptions.AddString("-checksum");
    if (m_listMDD)
        aosOptions.AddString("-listmdd");
    if (!m_mdd.empty())
    {
        aosOptions.AddString("-mdd");
        aosOptions.AddString(m_mdd.c_str());
    }

    GDALDatasetH hDS = GDALDataset::ToHandle(poSrcDS);
    std::unique_ptr<GDALDataset> poSubDataset;

    if (m_subDS > 0)
    {
        CSLConstList papszSubdatasets = GDALGetMetadata(hDS, "SUBDATASETS");
        const int nSubdatasets = CSLCount(papszSubdatasets) / 2;
        if (m_subDS > nSubdatasets)
        {
            CPLError(CE_Failure, CPLE_AppDefined,
                     "Invalid value for 'subdataset' argument. Should be "
                     "between 1 and %d",
                     nSubdatasets);
            return false;
        }

        char szKeyName[64];
        snprintf(szKeyName, sizeof(szKeyName), "SUBDATASET_%d_NAME", m_subDS);
        const std::string osSubDSName =
            CSLFetchNameValueDef(papszSubdatasets, szKeyName, "");

        poSubDataset.reset(GDALDataset::Open(
            osSubDSName.c_str(), GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
            nullptr, nullptr, nullptr));
        if (!poSubDataset)
            return false;
        hDS = GDALDataset::ToHandle(poSubDataset.get());
    }

    if (m_stdout)
    {
        aosOptions.AddString("-stdout");
    }

    GDALInfoOptions *psOptions = GDALInfoOptionsNew(aosOptions.List(), nullptr);
    char *ret = GDALInfo(hDS, psOptions);
    GDALInfoOptionsFree(psOptions);
    const bool bOK = ret != nullptr;
    if (ret)
    {
        m_output = ret;
    }
    CPLFree(ret);

    return bOK;
}

GDALRasterInfoAlgorithmStandalone::~GDALRasterInfoAlgorithmStandalone() =
    default;

//! @endcond
