/*****************************************************************************
 * $CAMITK_LICENCE_BEGIN$
 *
 * CamiTK - Computer Assisted Medical Intervention ToolKit
 * (c) 2001-2018 Univ. Grenoble Alpes, CNRS, TIMC-IMAG UMR 5525 (GMCAO)
 *
 * 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 <Log.h>

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


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

    QList<DicomSerie*> dicomSeries;

    // 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);

    CAMITK_TRACE_ALT(tr("Parsing files for DICOM image files"))
    foreach (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()) {
                CAMITK_TRACE_ALT(tr("%1 is a DICOM image file").arg(QString::fromStdString(file)))
                imageFileNames.push_back(file);
            }
            else {
                CAMITK_TRACE_ALT(tr("%1 is a DICOM file WITHOUT PixelData information").arg(QString::fromStdString(file)))
            }
        }
        else {
            CAMITK_TRACE_ALT(QObject::tr("%1 is not a valid DICOM file").arg(QString::fromStdString(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
    foreach (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 serieUIDTag = gdcm::Tag(0x0020, 0x000e);
        seriesScanner.AddTag(serieUIDTag);
        seriesScanner.Scan(studyFileNames);
        const std::set< std::string > serieValues = seriesScanner.GetValues();
        foreach (std::string serieName, serieValues) {
            // get file associated with this series
            std::vector< std::string > serieFileNames = seriesScanner.GetAllFilenamesFromTagToValue(serieUIDTag, serieName.c_str());

            // build a DicomSerie object corresponding to what we have found
            if (serieFileNames.size() > 0) {
                DicomSerie* dicomSerie = new DicomSerie();
                dicomSerie->setAcquisitionDate(DicomParser::getAcquisitionDate(serieFileNames));
                dicomSerie->setAcquisitionTime(DicomParser::getAcquisitionTime(serieFileNames));
                dicomSerie->setStudyName(DicomParser::getStudyName(serieFileNames));
                dicomSerie->setSerieName(DicomParser::getSerieName(serieFileNames));
                dicomSerie->setSerieDescription(DicomParser::getSerieDescription(serieFileNames));
                dicomSerie->setPatientName(DicomParser::getPatientName(serieFileNames));
                dicomSerie->setStdFileNames(serieFileNames);
                dicomSerie->setFileNames(stdListOfStringToQt(serieFileNames));

                dicomSeries.append(dicomSerie);
            }
        }
    }

    return dicomSeries;

}

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

    std::string file = serieFileNames.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>& serieFileNames) {
    gdcm::Scanner scanner;
    gdcm::Tag acquisitionTimeUIDTag = gdcm::Tag(0x0008, 0x0032);
    scanner.AddTag(acquisitionTimeUIDTag);
    scanner.Scan(serieFileNames);

    std::string file = serieFileNames.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>& serieFileNames) {
    gdcm::Scanner scanner;
    gdcm::Tag patientNameUIDTag = gdcm::Tag(0x0010, 0x0010);
    scanner.AddTag(patientNameUIDTag);
    scanner.Scan(serieFileNames);

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


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

    std::string file = serieFileNames.at(0);
    const char* serieName = scanner.GetValue(file.c_str(), serieUIDTag);
    return QString(serieName);
}

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

    std::string file = serieFileNames.at(0);
    const char* serieDescription = scanner.GetValue(file.c_str(), serieDescriptionTag);
    return QString(serieDescription);
}

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

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

// --------------- stdListOfStringToQt -------------------
QList<QString> DicomParser::stdListOfStringToQt(const std::vector<std::string>& inputList) {
    QList<QString> outputFileNames;
    foreach (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;
    foreach (QString qtFile, inputList) {
        outputFileNames.push_back(qtFile.toStdString());
    }

    return outputFileNames;
}

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

    std::string file = serieFileNames.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(serieFileNames.at(0))))
        return 1.0;
    }
    return atof(value);
}















