/* ============================================================
 *
 * 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:
 *               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,
            POSITIVE      = 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
