/* ============================================================
 *
 * This file is a part of digiKam project
 * https://www.digikam.org
 *
 * Date        : 2012-02-04
 * Description : a tool to create panorama by fusion of several images.
 *               This type is based on pto file format described here:
 *               http://hugin.sourceforge.net/docs/nona/nona.txt, and
 *               on pto files produced by Hugin's tools.
 *
 * Copyright (C) 2012-2015 by Benjamin Girault <benjamin dot girault at gmail dot com>
 *
 * This program is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General
 * Public License as published by the Free Software Foundation;
 * either version 2, or (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * ============================================================ */

#ifndef DIGIKAM_PTO_TYPE_H
#define DIGIKAM_PTO_TYPE_H

// Qt includes

#include <QPoint>
#include <QVector>
#include <QSize>
#include <QString>
#include <QRect>
#include <QStringList>
#include <QPair>
#include <QTextStream>

namespace Digikam
{

struct PTOType
{
    struct Project
    {
        struct FileFormat
        {
            typedef enum
            {
                PNG,
                TIFF,
                TIFF_m,
                TIFF_multilayer,
                JPEG
            } FileType;

            typedef enum
            {
                PANO_NONE,
                LZW,
                DEFLATE
            } CompressionMethod;

            FileType          fileType;
            unsigned char     quality;            ///< for JPEG
            CompressionMethod compressionMethod;  ///< for TIFF
            bool              cropped;            ///< for TIFF
            bool              savePositions;      ///< for TIFF

            FileFormat()
              : fileType(JPEG),
                quality(90),
                compressionMethod(LZW),
                cropped(false),
                savePositions(false)
            {
            }
        };

        typedef enum
        {
            RECTILINEAR      = 0,
            CYLINDRICAL      = 1,
            EQUIRECTANGULAR  = 2,
            FULLFRAMEFISHEYE = 3
        } ProjectionType;

        typedef enum
        {
            UINT8,
            UINT16,
            FLOAT
        } BitDepth;

        QStringList    previousComments;
        QSize          size;
        QRect          crop;
        ProjectionType projection;
        double         fieldOfView;
        FileFormat     fileFormat;
        double         exposure;
        bool           hdr;
        BitDepth       bitDepth;
        int            photometricReferenceId;
        QStringList    unmatchedParameters;

        Project()
          : size(0, 0),
            crop(0, 0, 0, 0),
            projection(RECTILINEAR),
            fieldOfView(0),
            exposure(0),
            hdr(false),
            bitDepth(UINT8),
            photometricReferenceId(0)
        {
        }
    };

    // --------------------------------------------------------------------------------------

    struct Stitcher
    {
        typedef enum
        {
            POLY3           = 0,
            SPLINE16        = 1,
            SPLINE36        = 2,
            SINC256         = 3,
            SPLINE64        = 4,
            BILINEAR        = 5,
            NEARESTNEIGHBOR = 6,
            SINC1024        = 7
        } Interpolator;

        typedef enum
        {
            SLOW,
            MEDIUM = 1,
            FAST   = 2
        } SpeedUp;

        QStringList  previousComments;
        double       gamma;
        Interpolator interpolator;
        SpeedUp      speedUp;
        double       huberSigma;
        double       photometricHuberSigma;
        QStringList  unmatchedParameters;

        Stitcher()
          : gamma(1),
            interpolator(POLY3),
            speedUp(FAST),
            huberSigma(0),
            photometricHuberSigma(0)
        {
        }
    };

    // --------------------------------------------------------------------------------------

    struct Mask
    {
        typedef enum
        {
            NEGATIVE      = 0,
            POSTIVE       = 1,
            NEGATIVESTACK = 2,
            POSITIVESTACK = 3,
            NEGATIVELENS  = 4
        } MaskType;

        QStringList   previousComments;
        MaskType      type;
        QList<QPoint> hull;
    };

    // --------------------------------------------------------------------------------------

    struct Optimization
    {
        typedef enum
        {
            LENSA,
            LENSB,
            LENSC,
            LENSD,
            LENSE,
            LENSHFOV,
            LENSYAW,
            LENSPITCH,
            LENSROLL,
            EXPOSURE,
            WBR,
            WBB,
            VA,
            VB,
            VC,
            VD,
            VX,
            VY,
            RA,
            RB,
            RC,
            RD,
            RE,
            UNKNOWN
        } Parameter;

        QStringList previousComments;
        Parameter   parameter;
    };

    // --------------------------------------------------------------------------------------

    struct Image
    {
        template<typename T>
        struct LensParameter
        {
            LensParameter()
              : value(T()),
                referenceId(-1)
            {
            }

            explicit LensParameter(const T& v)
              : value(v),
                referenceId(-1)
            {
            }

            T   value;
            int referenceId;

            friend QTextStream& operator<<(QTextStream& qts, const LensParameter<T>& p)
            {
               if (p.referenceId == -1)
               {
                   qts << p.value;
               }
               else
               {
                   qts << "=" << p.referenceId;
               }

               return qts;
            }
        };

        typedef enum
        {
            RECTILINEAR      = 0,
            PANORAMIC        = 1,
            CIRCULARFISHEYE  = 2,
            FULLFRAMEFISHEYE = 3,
            EQUIRECTANGULAR  = 4
        } LensProjection;

        typedef enum
        {
            PANO_NONE              = 0,
            RADIAL                 = 1,
            FLATFIELD              = 2,
            PROPORTIONNALRADIAL    = 5,
            PROPORTIONNALFLATFIELD = 6
        } VignettingMode;

        QStringList                   previousComments;
        QSize                         size;
        int                           id;
        QList<Mask>                   masks;
        QList<Optimization>           optimizationParameters;
        LensProjection                lensProjection;
        LensParameter<double>         fieldOfView;
        double                        yaw;
        double                        pitch;
        double                        roll;
        LensParameter<double>         lensBarrelCoefficientA;
        LensParameter<double>         lensBarrelCoefficientB;
        LensParameter<double>         lensBarrelCoefficientC;
        LensParameter<int>            lensCenterOffsetX;
        LensParameter<int>            lensCenterOffsetY;
        LensParameter<int>            lensShearX;
        LensParameter<int>            lensShearY;
        LensParameter<double>         exposure;
        LensParameter<double>         whiteBalanceRed;
        LensParameter<double>         whiteBalanceBlue;
        LensParameter<VignettingMode> vignettingMode;
        LensParameter<double>         vignettingCorrectionI;      ///< Va
        LensParameter<double>         vignettingCorrectionJ;      ///< Vb
        LensParameter<double>         vignettingCorrectionK;      ///< Vc
        LensParameter<double>         vignettingCorrectionL;      ///< Vd
        LensParameter<int>            vignettingOffsetX;
        LensParameter<int>            vignettingOffsetY;
        QString                       vignettingFlatfieldImageName;
        LensParameter<double>         photometricEMoRA;
        LensParameter<double>         photometricEMoRB;
        LensParameter<double>         photometricEMoRC;
        LensParameter<double>         photometricEMoRD;
        LensParameter<double>         photometricEMoRE;
        double                        mosaicCameraPositionX;
        double                        mosaicCameraPositionY;
        double                        mosaicCameraPositionZ;
        double                        mosaicProjectionPlaneYaw;
        double                        mosaicProjectionPlanePitch;
        QRect                         crop;
        LensParameter<int>            stackNumber;
        QString                       fileName;
        QStringList                   unmatchedParameters;

        Image()
          : size(0, 0),
            id(0),
            lensProjection(RECTILINEAR),
            fieldOfView(0),
            yaw(0),
            pitch(0),
            roll(0),
            lensBarrelCoefficientA(0),
            lensBarrelCoefficientB(0),
            lensBarrelCoefficientC(0),
            lensCenterOffsetX(0),
            lensCenterOffsetY(0),
            lensShearX(0),
            lensShearY(0),
            exposure(0),
            whiteBalanceRed(1),
            whiteBalanceBlue(1),
            vignettingMode(PANO_NONE),
            vignettingCorrectionI(0),
            vignettingCorrectionJ(0),
            vignettingCorrectionK(0),
            vignettingCorrectionL(0),
            vignettingOffsetX(0),
            vignettingOffsetY(0),
            photometricEMoRA(0),
            photometricEMoRB(0),
            photometricEMoRC(0),
            photometricEMoRD(0),
            photometricEMoRE(0),
            mosaicCameraPositionX(0),
            mosaicCameraPositionY(0),
            mosaicCameraPositionZ(0),
            mosaicProjectionPlaneYaw(0),
            mosaicProjectionPlanePitch(0),
            crop(0, 0, 0, 0),
            stackNumber(0)
        {
        }
    };

    // --------------------------------------------------------------------------------------

    struct ControlPoint
    {
        QStringList previousComments;
        int         image1Id;
        int         image2Id;
        double      p1_x;
        double      p1_y;
        double      p2_x;
        double      p2_y;
        int         type;                   // FIXME: change that for an enum if possible
        QStringList unmatchedParameters;
    };

    // --------------------------------------------------------------------------------------

    enum
    {
        PRE_V2014,
        V2014
    } version;

    PTOType()
      : version(PRE_V2014)
    {
    }

    explicit PTOType(const QString& version)
      : version((version.split(QLatin1Char('.'))[0].toInt() >= 2014) ? V2014
                                                                     : PRE_V2014)
    {
    }

    bool createFile(const QString& filepath);

/*
    NOTE: Work in progress
    QPair<double, int>  standardDeviation(int image1Id, int image2Id);
    QPair<double, int>  standardDeviation(int imageId);
    QPair<double, int>  standardDeviation();
*/

    Project             project;
    Stitcher            stitcher;
    QVector<Image>      images;
    QList<ControlPoint> controlPoints;
    QStringList         lastComments;
};

} // namespace Digikam

#endif // DIGIKAM_PTO_TYPE_H
