/*****************************************************************************
 * $CAMITK_LICENCE_BEGIN$
 *
 * CamiTK - Computer Assisted Medical Intervention ToolKit
 * (c) 2001-2025 Univ. Grenoble Alpes, CNRS, Grenoble INP - UGA, TIMC, 38000 Grenoble, France
 *
 * Visit http://camitk.imag.fr for more information
 *
 * This file is part of CamiTK.
 *
 * CamiTK is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * CamiTK is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with CamiTK.  If not, see <http://www.gnu.org/licenses/>.
 *
 * $CAMITK_LICENCE_END$
 ****************************************************************************/
// CamiTK includes
#include "DicomParser.h"
#include "DicomSeries.h"
#include <Log.h>

// GDCM includes
#include <gdcmScanner.h>
#include <gdcmIPPSorter.h>
#include <gdcmImageReader.h>


// --------------- parseDirectory -------------------
QList<DicomSeries*> DicomParser::parseDirectory(const QString& directory) {

    QList<DicomSeries*> allDicomSeries;

    // Recursively get all files in the input directory
    gdcm::Directory dir;
    dir.Load(directory.toStdString().c_str(), true);
    const std::vector< std::string >& fileNames = dir.GetFilenames();

    // Filter files to retrieve only DICOM files featuring an image (PixelData dataset)
    // Inspired for GDCM scanner basic example
    // see : http://gdcm.sourceforge.net/html/SimpleScanner_8cxx-example.html#_a7
    std::vector< std::string > imageFileNames;

    gdcm::Scanner dicomImageScanner;
    const gdcm::Tag tagPixelData(0x7fe0, 0x0010);
    dicomImageScanner.AddTag(tagPixelData);
    dicomImageScanner.Scan(fileNames);

    // Parsing files for DICOM image files
    for (std::string file : dicomImageScanner.GetFilenames()) {
        // 1st check the file is a valid DICOM file
        if (dicomImageScanner.IsKey(file.c_str())) {
            // 2nd check the dicom file is is an image file
            gdcm::Scanner::TagToValue const& ttv = dicomImageScanner.GetMapping(file.c_str());
            gdcm::Scanner::TagToValue::const_iterator it = ttv.find(tagPixelData);
            if (it != ttv.end()) {
                // This is a DICOM image file
                imageFileNames.push_back(file);
            }
            // else This is a DICOM file WITHOUT PixelData information
        }
        // else this is not a valid DICOM file
    }

    // filter files per image and STUDY
    gdcm::Tag studyUIDTag = gdcm::Tag(0x0020, 0x000d);
    // Note : we can put an observer for load bar
    gdcm::Scanner studyScanner;
    studyScanner.AddTag(studyUIDTag);

    /// DEBUG
    studyScanner.Scan(imageFileNames);

    // retrieve all the studies results
    const std::set< std::string > studyValues = studyScanner.GetValues();

    // for each value, build a new DicomStudyComponent given the associated study filenames
    for (std::string studyName : studyValues) {
        // get study associated filenames
        std::vector< std::string > studyFileNames = studyScanner.GetAllFilenamesFromTagToValue(studyUIDTag, studyName.c_str());

        // scan files for series
        gdcm::Scanner seriesScanner;
        gdcm::Tag seriesUIDTag = gdcm::Tag(0x0020, 0x000e);
        seriesScanner.AddTag(seriesUIDTag);
        seriesScanner.Scan(studyFileNames);
        const std::set< std::string > seriesValues = seriesScanner.GetValues();
        for (std::string serieName : seriesValues) {
            // get file associated with this series
            std::vector< std::string > seriesFileNames = seriesScanner.GetAllFilenamesFromTagToValue(seriesUIDTag, serieName.c_str());

            // build a DicomSeries object corresponding to what we have found
            if (seriesFileNames.size() > 0) {
                DicomSeries* additionalDicomSeries = new DicomSeries();
                additionalDicomSeries->setAcquisitionDate(DicomParser::getAcquisitionDate(seriesFileNames));
                additionalDicomSeries->setAcquisitionTime(DicomParser::getAcquisitionTime(seriesFileNames));
                additionalDicomSeries->setStudyName(DicomParser::getStudyName(seriesFileNames));
                additionalDicomSeries->setSeriesName(DicomParser::getSeriesName(seriesFileNames));
                additionalDicomSeries->setSeriesDescription(DicomParser::getSeriesDescription(seriesFileNames));
                additionalDicomSeries->setPatientName(DicomParser::getPatientName(seriesFileNames));
                additionalDicomSeries->setStdFileNames(seriesFileNames);
                additionalDicomSeries->setFileNames(stdListOfStringToQt(seriesFileNames));

                allDicomSeries.append(additionalDicomSeries);
            }
        }
    }

    return allDicomSeries;

}

// --------------- getAcquisitionDate -------------------
QDate DicomParser::getAcquisitionDate(const std::vector<std::string>& seriesFileNames) {
    gdcm::Scanner scanner;
    gdcm::Tag acquisitionDateUIDTag = gdcm::Tag(0x0008, 0x0022);
    scanner.AddTag(acquisitionDateUIDTag);
    scanner.Scan(seriesFileNames);

    std::string file = seriesFileNames.at(0);
    const char* acquisitionDate = scanner.GetValue(file.c_str(), acquisitionDateUIDTag);

    return QDate::fromString(QString(acquisitionDate), "yyyyMMdd");
}

// --------------- getAcquisitionTime -------------------
QTime DicomParser::getAcquisitionTime(const std::vector<std::string>& seriesFileNames) {
    gdcm::Scanner scanner;
    gdcm::Tag acquisitionTimeUIDTag = gdcm::Tag(0x0008, 0x0032);
    scanner.AddTag(acquisitionTimeUIDTag);
    scanner.Scan(seriesFileNames);

    std::string file = seriesFileNames.at(0);
    const char* acquisitionTime = scanner.GetValue(file.c_str(), acquisitionTimeUIDTag);

    return QTime::fromString(QString(acquisitionTime), "hhmmss");
}

// --------------- getPatientName -------------------
QString DicomParser::getPatientName(const std::vector<std::string>& seriesFileNames) {
    gdcm::Scanner scanner;
    gdcm::Tag patientNameUIDTag = gdcm::Tag(0x0010, 0x0010);
    scanner.AddTag(patientNameUIDTag);
    scanner.Scan(seriesFileNames);

    std::string file = seriesFileNames.at(0);
    const char* patientName = scanner.GetValue(file.c_str(), patientNameUIDTag);
    return QString(patientName);
}


// --------------- getSeriesName -------------------
QString DicomParser::getSeriesName(const std::vector<std::string>& seriesFileNames) {
    gdcm::Scanner scanner;
    gdcm::Tag seriesUIDTag = gdcm::Tag(0x0020, 0x000e);
    scanner.AddTag(seriesUIDTag);
    scanner.Scan(seriesFileNames);

    std::string file = seriesFileNames.at(0);
    const char* seriesName = scanner.GetValue(file.c_str(), seriesUIDTag);
    return QString(seriesName);
}

// --------------- getSeriesDescription -------------------
QString DicomParser::getSeriesDescription(const std::vector<std::string>& seriesFileNames) {
    gdcm::Scanner scanner;
    gdcm::Tag seriesDescriptionTag = gdcm::Tag(0x0008, 0x103e);
    scanner.AddTag(seriesDescriptionTag);
    scanner.Scan(seriesFileNames);

    std::string file = seriesFileNames.at(0);
    const char* seriesDescription = scanner.GetValue(file.c_str(), seriesDescriptionTag);
    return QString(seriesDescription);
}

// --------------- getStudyName -------------------
QString DicomParser::getStudyName(const std::vector<std::string>& seriesFileNames) {
    gdcm::Scanner scanner;
    gdcm::Tag seriesUIDTag = gdcm::Tag(0x0020, 0x000e);
    scanner.AddTag(seriesUIDTag);
    scanner.Scan(seriesFileNames);

    std::string file = seriesFileNames.at(0);
    const char* studyName = scanner.GetValue(file.c_str(), seriesUIDTag);
    return QString(studyName);
}

// --------------- stdListOfStringToQt -------------------
QList<QString> DicomParser::stdListOfStringToQt(const std::vector<std::string>& inputList) {
    QList<QString> outputFileNames;
    for (std::string stdFile : inputList) {
        outputFileNames.append(QString::fromStdString(stdFile));
    }

    return outputFileNames;
}

// --------------- qtListOfStringToStd -------------------
std::vector< std::string > DicomParser::qtListOfStringToStd(const QList<QString>& inputList) {
    std::vector< std::string > outputFileNames;
    for (QString qtFile : inputList) {
        outputFileNames.push_back(qtFile.toStdString());
    }

    return outputFileNames;
}

// --------------- getZSpacing -------------------
double DicomParser::getZSpacing(const std::vector<std::string>& seriesFileNames) {
    gdcm::Scanner scanner;
    gdcm::Tag zSpacingTag = gdcm::Tag(0x0018, 0x0088);
    scanner.AddTag(zSpacingTag);
    scanner.Scan(seriesFileNames);

    std::string file = seriesFileNames.at(0);
    const char* value = scanner.GetValue(file.c_str(), zSpacingTag);
    if (!value) {
        CAMITK_ERROR_ALT(QObject::tr("No Z spacing found on image: \"%1\"").arg(QString::fromStdString(seriesFileNames.at(0))))
        return 1.0;
    }
    return atof(value);
}















