/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * Redistributions in binary form must reproduce the above copyright
**     notice, this list of conditions and the following disclaimer in
**     the documentation and/or other materials provided with the
**     distribution.
**   * Neither the name of The Qt Company Ltd nor the names of its
**     contributors may be used to endorse or promote products derived
**     from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qtcamera.h"
#include <QApplication>
#include <QMediaService>
#include <QMediaRecorder>
#include <QCameraViewfinder>
#include <QCameraInfo>
#include <QMediaMetaData>

#include <QMessageBox>
#include <QPalette>
#include <QTabWidget>
#include <QtWidgets>
#include <QHBoxLayout>
#include <QVBoxLayout>


#define QCAMERA_CAPTURE_MODE "Image Mode"
#define QCAMERA_VIDEO_MODE "Video Mode"
#define DIR_USERDATA "/userdata"
#define DIR_HOME QStandardPaths::writableLocation(QStandardPaths::HomeLocation)
Q_DECLARE_METATYPE(QCameraInfo)

qtCamera::qtCamera()
{
    initlayout();
    QFileInfo fi(DIR_USERDATA);
    if(fi.isDir()){
        locationDir = DIR_USERDATA;
    }else {
        QFileInfo fi(DIR_HOME);
        if(fi.isDir()){
            locationDir = DIR_HOME;
        }
    }
    imageCnt = videoCnt = 0;
    setCamera(QCameraInfo::defaultCamera());
}

void qtCamera::initlayout()
{
    QBoxLayout *vLayout = new QVBoxLayout();
    const QRect availableGeometry = QApplication::desktop()->availableGeometry(this);
    resize(availableGeometry.width(), availableGeometry.height());

    const QList<QCameraInfo> availableCameras = QCameraInfo::availableCameras();
    for (const QCameraInfo &cameraInfo : availableCameras) {
//        qDebug() << cameraInfo.description();
        QPushButton *camera = getButton();
        camera->setText(cameraInfo.description());
        camera->setCheckable(true);
        if (cameraInfo == QCameraInfo::defaultCamera()){
            camera->setDefault(true);
        }else {
            camera->setDefault(false);
        }
        connect(camera, SIGNAL(clicked(bool)), this, SLOT(on_cameraSwitch()));
        vLayout->addWidget(camera);
    }

    modeButton = getButton();
    modeButton->setText(cameraMode);
    connect(modeButton, SIGNAL(clicked(bool)), this, SLOT(updateCaptureMode()));

    captureButton = getButton();
    captureButton->setText(tr("Capture"));
    connect(captureButton, SIGNAL(clicked(bool)), this, SLOT(on_captureClicked()));

    exitButton = getButton();
    exitButton->setText(tr("Exit"));
    connect(exitButton, SIGNAL(clicked(bool)), this, SLOT(on_exitClicked()));

    vLayout->addWidget(modeButton);
    vLayout->addWidget(captureButton);
    vLayout->addWidget(exitButton);
    vLayout->setAlignment(Qt::AlignTop);

    viewfinder.setWindowFlag(Qt::FramelessWindowHint);
    viewfinder.setFixedSize(availableGeometry.width() - 150, availableGeometry.height());

    QBoxLayout *hlayout = new QHBoxLayout;
    hlayout->setMargin(0);
    hlayout->addWidget(&viewfinder);
    hlayout->addLayout(vLayout);

    QWidget *widget = new QWidget;
    widget->setLayout(hlayout);
    setCentralWidget(widget);
//    setWindowState(Qt::WindowMaximized);
    setWindowFlags(Qt::FramelessWindowHint);
}

void qtCamera::setCamera(const QCameraInfo &cameraInfo)
{
    m_camera.reset(new QCamera(cameraInfo));

    connect(m_camera.data(), &QCamera::stateChanged, this, &qtCamera::updateCameraState);
    connect(m_camera.data(), QOverload<QCamera::Error>::of(&QCamera::error), this, &qtCamera::displayCameraError);

    m_mediaRecorder.reset(new QMediaRecorder(m_camera.data()));
    connect(m_mediaRecorder.data(), &QMediaRecorder::stateChanged, this, &qtCamera::updateRecorderState);

    m_imageCapture.reset(new QCameraImageCapture(m_camera.data()));

    connect(m_mediaRecorder.data(), &QMediaRecorder::durationChanged, this, &qtCamera::updateRecordTime);
    connect(m_mediaRecorder.data(), QOverload<QMediaRecorder::Error>::of(&QMediaRecorder::error),
            this, &qtCamera::displayRecorderError);

    m_mediaRecorder->setMetaData(QMediaMetaData::Title, QVariant(QLatin1String("Test Title")));

    configureCaptureSettings();

    m_camera->setViewfinder(&viewfinder);

    updateCameraState(m_camera->state());
    updateRecorderState(m_mediaRecorder->state());

    connect(m_imageCapture.data(), &QCameraImageCapture::imageSaved, this, &qtCamera::imageSaved);
    connect(m_imageCapture.data(), QOverload<int, QCameraImageCapture::Error, const QString &>::of(&QCameraImageCapture::error),
            this, &qtCamera::displayCaptureError);

    updateCaptureMode();
}

void qtCamera::configureCaptureSettings()
{
    QSize size(640, 480);

    m_imageSettings.setCodec("jpeg");
    m_imageSettings.setQuality(QMultimedia::VeryHighQuality);
    m_imageSettings.setResolution(size);
    m_imageCapture->setEncodingSettings(m_imageSettings);

    m_audioSettings.setCodec("audio/x-adpcm");
    m_audioSettings.setChannelCount(2);
    m_audioSettings.setQuality(QMultimedia::NormalQuality);
    m_mediaRecorder->setAudioSettings(m_audioSettings);

    m_videoSettings.setCodec("video/x-h264");
    m_videoSettings.setResolution(size);
    m_videoSettings.setQuality(QMultimedia::NormalQuality);
    m_mediaRecorder->setVideoSettings(m_videoSettings);

    m_mediaRecorder->setContainerFormat("video/quicktime");

    if (0) {
        QList<QSize> supportedResolutions;
        supportedResolutions = m_imageCapture->supportedResolutions();
        for (const QSize &resolution : supportedResolutions) {
             qDebug() << "image resolution: " << resolution.width() << "x" << resolution.height();
        }

        supportedResolutions = m_mediaRecorder->supportedResolutions();
        for (const QSize &resolution : supportedResolutions) {
            qDebug() << "video resolution: " << resolution.width() << "x" << resolution.height();
        }

        const QStringList supportedAudioCodecs = m_mediaRecorder->supportedAudioCodecs();
        for (const QString &codecName : supportedAudioCodecs) {
            QString description = m_mediaRecorder->audioCodecDescription(codecName);
            qDebug() << "audio codec:" << codecName + ": " + description;
        }

        const QStringList supportedVideoCodecs = m_mediaRecorder->supportedVideoCodecs();
        for (const QString &codecName : supportedVideoCodecs) {
            QString description = m_mediaRecorder->videoCodecDescription(codecName);
            qDebug() << "video codec:" << codecName + ": " + description;
        }

        const QStringList formats = m_mediaRecorder->supportedContainers();
        for (const QString &format : formats) {
<<<<<<< Updated upstream
            QString description = m_mediaRecorder->containerDescription(format);
            qDebug() << "container: " << format << ": " << description;
        }
=======
            qDebug() << format;
        }

        supportedResolutions = m_mediaRecorder->supportedResolutions();
        for (const QSize &resolution : supportedResolutions) {
            if(size.width()<resolution.width() && size.height()<resolution.height())
                size = resolution;
        }

        m_audioSettings.setCodec("audio/mpeg");
        m_audioSettings.setQuality(QMultimedia::VeryHighQuality);
        m_videoSettings.setCodec("video/x-h264");
        m_videoSettings.setQuality(QMultimedia::VeryHighQuality);
        m_videoSettings.setResolution(size);
        m_mediaRecorder->setAudioSettings(m_audioSettings);
        m_mediaRecorder->setVideoSettings(m_videoSettings);
        m_mediaRecorder->setContainerFormat("video/quicktime, variant=(string)iso");
        break;
    }
    default:
        break;
>>>>>>> Stashed changes
    }
}

QPushButton* qtCamera::getButton()
{
    QPushButton *button = new QPushButton;
    button->setFixedSize(144, 70);
    return button;
}

void qtCamera::updateRecordTime()
{
    QString str = QString("Recorded %1 sec").arg(m_mediaRecorder->duration()/1000);
    statusBar()->showMessage(str);
}

void qtCamera::record()
{
    QFileInfo fi;
    QString lo;

    lo = locationDir + "/" + "VIDEO" + QString::number(videoCnt) + ".mov";
    fi = QFileInfo(lo);

    while(fi.isFile()){
        videoCnt++;
        lo = locationDir + "/" + "VIDEO" + QString::number(videoCnt) + ".mov";
        fi = QFileInfo(lo);
    }

    m_mediaRecorder->setOutputLocation(QUrl::fromLocalFile(lo));
    m_mediaRecorder->record();
    updateRecordTime();
}

void qtCamera::stop()
{
    m_mediaRecorder->stop();
}

void qtCamera::takeImage()
{
    m_isCapturingImage = true;
    QFileInfo fi;
    QString lo;

    lo = locationDir + "/" + "PIC" + QString::number(imageCnt) + ".jpg";
    fi = QFileInfo(lo);

    while(fi.isFile()){
        imageCnt++;
        lo = locationDir + "/" + "PIC" + QString::number(imageCnt) + ".jpg";
        fi = QFileInfo(lo);
    }

    m_imageCapture->capture(lo);
}

void qtCamera::displayCaptureError(int id, const QCameraImageCapture::Error error, const QString &errorString)
{
    Q_UNUSED(id);
    Q_UNUSED(error);
    QMessageBox::warning(this, tr("Image Capture Error"), errorString);
    m_isCapturingImage = false;
}

void qtCamera::updateCaptureMode()
{
    QCamera::CaptureModes captureMode;
    QString capture;
    if (cameraMode.compare(QCAMERA_CAPTURE_MODE)){
        captureMode = QCamera::CaptureStillImage ;
    }else {
        captureMode = QCamera::CaptureVideo;
    }

    if (m_camera->isCaptureModeSupported(captureMode)){
        m_camera->unload();
        m_camera->setCaptureMode(captureMode);
        m_camera->start();
        if(captureMode == QCamera::CaptureStillImage){
            cameraMode = QString(QCAMERA_CAPTURE_MODE);
            capture = "Capture";
        }else {
            cameraMode = QString(QCAMERA_VIDEO_MODE);
            capture = "Record";
        }
        modeButton->setText(cameraMode);
        captureButton->setText(capture);
    }
}

void qtCamera::updateCameraState(QCamera::State state)
{
    switch (state) {
    case QCamera::ActiveState:
        break;
    case QCamera::UnloadedState:
    case QCamera::LoadedState:
        break;
    }
}

void qtCamera::updateRecorderState(QMediaRecorder::State state)
{
    switch (state) {
    case QMediaRecorder::StoppedState:
        captureButton->setText(tr("Record"));
        break;
    case QMediaRecorder::PausedState:
        break;
    case QMediaRecorder::RecordingState:
        captureButton->setText(tr("Recording"));
        break;
    }
}

void qtCamera::displayRecorderError()
{
    QMessageBox::warning(this, tr("Capture Error"), m_mediaRecorder->errorString());
}

void qtCamera::displayCameraError()
{
    QMessageBox::warning(this, tr("Camera Error"), m_camera->errorString());
}

void qtCamera::imageSaved(int id, const QString &fileName)
{
    Q_UNUSED(id);
    statusBar()->showMessage(tr("Captured \"%1\"").arg(QDir::toNativeSeparators(fileName)));
    statusBar()->show();
    m_isCapturingImage = false;
    if (m_applicationExiting)
        close();
}

void qtCamera::closeEvent(QCloseEvent *event)
{
    if (m_isCapturingImage) {
        setEnabled(false);
        m_applicationExiting = true;
        event->ignore();
    } else {
        event->accept();
    }
}

void qtCamera::on_cameraSwitch()
{
    QList<QPushButton *> buttons = centralWidget()->findChildren<QPushButton *>();
    for(auto *but: buttons){
        if(but->isChecked()){
            for(auto *button: buttons){
                if(button->isDefault())
                    button->setDefault(false);
            }
            but->setDefault(true);
            but->setChecked(false);
            qDebug() << "switch to " + but->text();
            const QList<QCameraInfo> availableCameras = QCameraInfo::availableCameras();
            for (const QCameraInfo &cameraInfo : availableCameras) {
                if(! but->text().compare(cameraInfo.description())){
                    qDebug() << cameraInfo.description();
                    setCamera(cameraInfo);
                }
            }
            break;
        }
    }
}

void qtCamera::on_captureClicked()
{
    if (m_camera->captureMode() == QCamera::CaptureStillImage) {
        takeImage();
    } else {
        if (m_mediaRecorder->state() == QMediaRecorder::RecordingState)
            stop();
        else
            record();
    }
}

void qtCamera::on_exitClicked()
{
        qApp->exit(0);
}
