/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the demonstration applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** 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 Digia Plc and its Subsidiary(-ies) 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 "cameraexample.h"
#include "messagehandling.h"
#include "contactsdlg.h"
#include "button.h"
#include "businesscardhandling.h"
#include <QDebug>

/*****************************************************************************
* MyVideoSurface
*/
MyVideoSurface::MyVideoSurface(QWidget* widget, VideoIF* target, QObject* parent)
    : QAbstractVideoSurface(parent)
{
    m_targetWidget = widget;
    m_target = target;
    m_imageFormat = QImage::Format_Invalid;
}

MyVideoSurface::~MyVideoSurface()
{
}

bool MyVideoSurface::start(const QVideoSurfaceFormat &format)
{
    m_videoFormat = format;
    const QImage::Format imageFormat = QVideoFrame::imageFormatFromPixelFormat(format.pixelFormat());
    const QSize size = format.frameSize();

    if (imageFormat != QImage::Format_Invalid && !size.isEmpty()) {
        m_imageFormat = imageFormat;
        QAbstractVideoSurface::start(format);
        return true;
    } else {
        return false;
    }
}

bool MyVideoSurface::present(const QVideoFrame &frame)
{
    m_frame = frame;
    if (surfaceFormat().pixelFormat() != m_frame.pixelFormat() ||
            surfaceFormat().frameSize() != m_frame.size()) {
        stop();
        return false;
    } else {
        m_target->updateVideo();
        return true;
    }
}

void MyVideoSurface::paint(QPainter *painter)
 {
     if (m_frame.map(QAbstractVideoBuffer::ReadOnly)) {
         QImage image(
                 m_frame.bits(),
                 m_frame.width(),
                 m_frame.height(),
                 m_frame.bytesPerLine(),
                 m_imageFormat);

         QRect r = m_targetWidget->rect();
         QPoint centerPic((qAbs(r.size().width() - image.size().width())) / 2, (qAbs(
             r.size().height() - image.size().height())) / 2);

         if (!image.isNull()) {
            painter->drawImage(centerPic,image);
         }

         m_frame.unmap();
     }
 }

QList<QVideoFrame::PixelFormat> MyVideoSurface::supportedPixelFormats(
            QAbstractVideoBuffer::HandleType handleType) const
{
    if (handleType == QAbstractVideoBuffer::NoHandle) {
        return QList<QVideoFrame::PixelFormat>()
                << QVideoFrame::Format_RGB32
                << QVideoFrame::Format_ARGB32
                << QVideoFrame::Format_ARGB32_Premultiplied
                << QVideoFrame::Format_RGB565
                << QVideoFrame::Format_RGB555;
    } else {
        return QList<QVideoFrame::PixelFormat>();
    }
}


/*****************************************************************************
* CameraExample
*/
CameraExample::CameraExample(QWidget *parent) :
    QMainWindow(parent)
{
    setWindowTitle("QCameraExample");

    // Opitimizations for screen update and drawing qwidget
    setAutoFillBackground(false);

    // Prevent to screensaver to activate
    m_systemScreenSaver = new QSystemScreenSaver(this);
    m_systemScreenSaver->setScreenSaverInhibit();

    m_myVideoSurface = 0;
    pictureCaptured = false;
    showViewFinder = false;
    m_focusing = false;

    // MMS handling
    m_message = new Message(this);
    QObject::connect(m_message, SIGNAL(messageStateChanged(int)), this, SLOT(messageStateChanged(int)));
    QObject::connect(m_message, SIGNAL(messageReceived(QString,QString,QPixmap)), this, SLOT(messageReceived(QString,QString,QPixmap)));

    // Business card handling (Contact's avatar picture)
    m_businessCardHandling = new BusinessCardHandling(this);

    // Black background
    QPalette palette = this->palette();
    palette.setColor(QPalette::Background, Qt::black);
    setPalette(palette);

    // Main widget & layout
    QWidget* mainWidget = new QWidget(this);
    mainWidget->setPalette(palette);

    QHBoxLayout* hboxl = new QHBoxLayout;
    hboxl->setSpacing(0);
    hboxl->setMargin(0);

    // UI stack
    m_stackedWidget = new QStackedWidget();
    m_stackedWidget->setPalette(palette);

    // First widget to stack
    m_videoWidget = new QWidget();
    m_videoWidget->setPalette(palette);
    m_stackedWidget->addWidget(m_videoWidget);

    // Second widget to stack
    QWidget* secondWidget = new QWidget(this);
    secondWidget->setPalette(palette);
    m_stackedWidget->addWidget(secondWidget);
    m_stackedWidget->setCurrentIndex(0);

    hboxl->addWidget(m_stackedWidget);

    // Buttons
    QSize iconSize(80, 80);
    QVBoxLayout* vboxl = new QVBoxLayout;
    vboxl->setSpacing(0);
    vboxl->setMargin(0);

    // Exit button
    m_exit = new Button(this);
    QObject::connect(m_exit, SIGNAL(pressed()), qApp, SLOT(quit()));
    QPixmap p = QPixmap(":/icons/exit.png");
    m_exit->setPixmap(p.scaled(iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation));
    vboxl->addWidget(m_exit);
    vboxl->setAlignment(m_exit, Qt::AlignHCenter | Qt::AlignTop);

    // Camera button
    m_cameraBtn = new Button(this);
    QObject::connect(m_cameraBtn, SIGNAL(pressed()), this, SLOT(searchAndLock()));
    p = QPixmap(":/icons/camera.png");
    m_cameraBtn->setPixmap(p.scaled(iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation));
    vboxl->addWidget(m_cameraBtn);
    vboxl->setAlignment(m_cameraBtn, Qt::AlignCenter);

    // Send MMS button
    m_mms = new Button(this);
    QObject::connect(m_mms, SIGNAL(pressed()), this, SLOT(openContactsDlg()));
    p = QPixmap(":/icons/mms.png");
    m_mms->setPixmap(p.scaled(iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation));
    vboxl->addWidget(m_mms);
    vboxl->setAlignment(m_mms, Qt::AlignHCenter | Qt::AlignBottom);
#ifndef MESSAGING_ENABLED
    m_mms->disableBtn(true);
    m_mms->setEnabled(false);
#endif

    hboxl->addLayout(vboxl);
    mainWidget->setLayout(hboxl);

    setCentralWidget(mainWidget);

    // Enable camera after 1s, so that the application is started
    // and widget is created to landscape orientation
    QTimer::singleShot(1000,this,SLOT(enableCamera()));
}

CameraExample::~CameraExample()
{
if (m_myVideoSurface)
    m_myVideoSurface->stop();
    m_camera->stop();
    delete m_stackedWidget;
    delete m_stillImageCapture;
    delete m_camera;
}


void CameraExample::enableCamera()
{
    m_camera = new QCamera();
    m_camera->setCaptureMode(QCamera::CaptureStillImage);
    connect(m_camera, SIGNAL(error(QCamera::Error)), this, SLOT(error(QCamera::Error)));
    connect(m_camera, SIGNAL(lockStatusChanged(QCamera::LockStatus,QCamera::LockChangeReason)), this, SLOT(lockStatusChanged(QCamera::LockStatus,QCamera::LockChangeReason)));

    // Own video output drawing that shows camera view finder pictures
    //! [0]
    QMediaService* ms = m_camera->service();
    QVideoRendererControl* vrc = ms->requestControl<QVideoRendererControl*>();
    m_myVideoSurface = new MyVideoSurface(this,this,this);
    vrc->setSurface(m_myVideoSurface);
//! [0]
    // Image capturer
    m_stillImageCapture = new QCameraImageCapture(m_camera);
    connect(m_stillImageCapture, SIGNAL(imageCaptured(int,QImage)), this, SLOT(imageCaptured(int,QImage)));

    // Start camera
    if (m_camera->state() == QCamera::ActiveState) {
        m_camera->stop();
    }
    m_videoWidget->show();
    m_camera->start();
    showViewFinder = true;
}

void CameraExample::mousePressEvent(QMouseEvent *event)
{
    QMainWindow::mousePressEvent(event);

    if (pictureCaptured) {
        // Starting view finder
        pictureCaptured = false;
        m_stackedWidget->setCurrentIndex(0);
        if (m_myVideoSurface) {
            showViewFinder = true;
        }
    }
}

void CameraExample::searchAndLock()
{
    m_focusing = false;
    m_focusMessage.clear();

    if (pictureCaptured) {
        // Starting view finder again
        pictureCaptured = false;
        m_stackedWidget->setCurrentIndex(0);
        if (m_myVideoSurface) {
            showViewFinder = true;
        }
    }
    else {
        // Search and lock picture (=focus)
        if (m_camera->supportedLocks() & QCamera::LockFocus) {
            m_focusing = true;
            m_focusMessage = "Focusing...";
            m_camera->searchAndLock(QCamera::LockFocus);
        } else {
        // No focus functionality, take picture right away
            captureImage();
        }
    }
}

void CameraExample::lockStatusChanged(QCamera::LockStatus status, QCamera::LockChangeReason reason)
{
    if (status == QCamera::Locked) {
        if (reason == QCamera::LockAcquired) {
            // Focus locked
            m_focusMessage.clear();
            m_focusing = false;
            // Capture new image
            captureImage();
            // Unlock camera
            m_camera->unlock();
        } else {
            if (m_focusing)
                m_focusMessage = "No focus, try again";
        }
    } else if (status == QCamera::Unlocked && m_focusing) {
        m_focusMessage = "No focus, try again";
    }
}

void CameraExample::captureImage()
{
    if (pictureCaptured) {
        // Starting view finder again
        pictureCaptured = false;
        m_stackedWidget->setCurrentIndex(0);
        showViewFinder = true;
    }
    else {
        // Capturing image
        showViewFinder = false;
        // Get picture location where to store captured images
        QString path(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation));
        QDir dir(path);

        // Get next filename
        QStringList files = dir.entryList(QStringList() << "camera_*.jpg");
        int lastImage = 0;
        foreach ( QString fileName, files ) {
                int imgNumber = fileName.mid(7, fileName.size() - 11).toInt();
                lastImage = qMax(lastImage, imgNumber);
            }
        // Capture image
        if (m_stillImageCapture->isReadyForCapture()) {
            m_imageName = QString("camera_%1.jpg").arg(lastImage+1);
            m_stillImageCapture->capture(m_imageName);
        }
    }
}

void CameraExample::imageCaptured(int id, const QImage &preview)
{
    showViewFinder = false;
    m_focusing = false;

    // Image captured, show it to the user
    m_stackedWidget->setCurrentIndex(1);

    // Get picture location
    QString path(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation));
    m_imageName.prepend(path + "/");

    m_capturedImage = preview;

    // Set suitable size to the image
    QSize s = m_videoWidget->size();
    s = s - QSize(20, 20);
    m_capturedImage = m_capturedImage.scaled(s, Qt::KeepAspectRatio, Qt::SmoothTransformation);

    pictureCaptured = true;
    update();
}

void CameraExample::error(QCamera::Error e)
{
    switch (e) {
    case QCamera::NoError:
    {
        break;
    }
    case QCamera::CameraError:
    {
        QMessageBox::warning(this, "QCameraExample", "General Camera error");
        break;
    }
    case QCamera::InvalidRequestError:
    {
        QMessageBox::warning(this, "QCameraExample", "Camera invalid request error");
        break;
    }
    case QCamera::ServiceMissingError:
    {
        QMessageBox::warning(this, "QCameraExample", "Camera service missing error");
        break;
    }
    case QCamera::NotSupportedFeatureError :
    {
        QMessageBox::warning(this, "QCameraExample", "Camera not supported error");
        break;
    }
    };
}

void CameraExample::openContactsDlg()
{
    // Open dialog for showing contacts
    if (!m_contactsDialog) {

        if (m_capturedImage.isNull()) {
            QMessageBox::information(this, "QCameraExample", "Take picture first");
            return;
        }

        // Show dialog
        m_contactsDialog = new ContactsDialog(this);
        QObject::connect(m_contactsDialog, SIGNAL(contactSelected(QString)),
        this, SLOT(contactSelected(QString)));
        m_contactsDialog->exec();
        QObject::disconnect(m_contactsDialog, SIGNAL(contactSelected(QString)),
        this, SLOT(contactSelected(QString)));

        delete m_contactsDialog;
        m_contactsDialog = 0;
    }
}

void CameraExample::contactSelected(QString phoneNumber)
{
    m_phoneNumber = phoneNumber;
    QTimer::singleShot(1000,this,SLOT(sendMMS()));
}

void CameraExample::sendMMS()
{
#ifdef MESSAGING_ENABLED
    m_message->sendMMS(m_imageName, m_phoneNumber);
#endif
}

void CameraExample::messageStateChanged(int /*error*/)
{
}

void CameraExample::updateVideo()
{
    if (showViewFinder) {
        repaint();
    }
}

void CameraExample::paintEvent(QPaintEvent *event)
{
    //QMainWindow::paintEvent(event);

    QPainter painter(this);
    QRect r = this->rect();

    QFont font = painter.font();
    font.setPixelSize(20);
    painter.setFont(font);
    painter.setPen(Qt::white);

    if (showViewFinder && m_myVideoSurface && m_myVideoSurface->isActive()) {
        // Show view finder
        m_myVideoSurface->paint(&painter);

        // Paint focus message
        if (!m_focusMessage.isEmpty())
            painter.drawText(r, Qt::AlignCenter, m_focusMessage);

    } else {
        // Draw black
        painter.fillRect(event->rect(), palette().background());
        // Show captured image
        if (pictureCaptured) {
            // Paint captured image
            QPoint centerPic((qAbs(r.size().width() - m_capturedImage.size().width())) / 2, (qAbs(
                r.size().height() - m_capturedImage.size().height())) / 2);

            painter.drawImage(centerPic, m_capturedImage);

            // Paint filename
            painter.drawText(r, Qt::AlignBottom | Qt::AlignCenter, m_imageName);
        }
    }

}

void CameraExample::messageReceived(QString phoneNumber, QString filename, QPixmap pixmap)
{
#ifdef MESSAGING_ENABLED
    // MMS message received
    // Check that is came from some of our contact
    QContact contact;
    if (m_businessCardHandling->findContact(phoneNumber, contact)) {
        // Ask from user to store it as sender avatar picture into contacts
        if (QMessageBox::question(
            this,
            "QCameraExample",
            QString(
                "MMS picture message received from %1. Do you want to store it as sender avatar picture?").arg(
                contact.displayLabel()), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {

            m_businessCardHandling->storeAvatarToContact(phoneNumber, filename, pixmap);
        }
    }
#endif
}

