/*
* Copyright (c) 2011 Nokia Corporation.
*/


#include "mirroritem.h"
#include <QDebug>
#include <QStyleOptionGraphicsItem>
#include <QEvent>
#include <QTouchEvent>
#include <QGraphicsScene>
#include <QGraphicsWidget>
#include <QDesktopServices>
#include <QDir>

/* ***************************************************************************
* MyVideoSurface
*/
MyVideoSurface::MyVideoSurface(QDeclarativeItem* widget, CameraUpdateIF* target, QObject* parent)
    : QAbstractVideoSurface(parent)
{
    m_framesExists = false;
    m_targetWidget = widget;
    m_target = target;
    m_imageFormat = QImage::Format_Invalid;
    m_mirrorHouseEffect = new CMirrorHouse();
}

MyVideoSurface::~MyVideoSurface()
{
    delete m_mirrorHouseEffect;
}

bool MyVideoSurface::start(const QVideoSurfaceFormat &format)
{
    m_framesExists = false;
    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::framesExists()
{
 return m_framesExists;
}

QSize MyVideoSurface::targetImageSize()
{
    return m_targetImage.size();
}

QImage::Format MyVideoSurface::targetImageFormat()
{
    return m_imageFormat;
}


bool MyVideoSurface::present(const QVideoFrame &frame)
{
    // Handle frame
    m_frame = frame;
    if (surfaceFormat().pixelFormat() != m_frame.pixelFormat() ||
            surfaceFormat().frameSize() != m_frame.size()) {
        stop();
        return false;
    } else {
        if (m_frame.map(QAbstractVideoBuffer::ReadOnly)) {
            m_mirrorHouseEffect->setSource( (unsigned int*)m_frame.bits(),
                                           m_frame.width(),m_frame.height(),
                                           m_frame.bytesPerLine()/4);

            // Target
            if (m_targetImage.width()==0) {
                // Make QImage from frame
                // Using smaller target picture that source
                m_targetImage = QImage( m_targetWidget->boundingRect().width(), m_targetWidget->boundingRect().height(), m_imageFormat);

                // Set target
                m_mirrorHouseEffect->setTarget( (unsigned int*)m_targetImage.bits(),
                                               m_targetImage.width(),m_targetImage.height(),
                                               m_targetImage.bytesPerLine()/4);
            }


            // Set effect
            switch (m_effectId) {
                case 1: {
                    m_mirrorHouseEffect->setMirrorTransform( eBUBBLES, 0.5f, 1.0f );
                    break;
                }
                case 2: {
                    m_mirrorHouseEffect->setMirrorTransform( eINVBUBBLES, 0.5f, 1.0f );
                    break;
                }
                case 3: {
                    m_mirrorHouseEffect->setMirrorTransform( eVERTICAL_WAVE, 0.5f, 6.0f );
                    break;
                }
                case 4: {
                    m_mirrorHouseEffect->setMirrorTransform( eHORIZONTAL_WAVE, 0.5f, 6.0f );
                    break;
                }
                case 5: {
                    m_mirrorHouseEffect->setMirrorTransform( eSPIRAL, 0.5f, 1.0f );
                    break;
                }
                case 6: {
                    m_mirrorHouseEffect->setMirrorTransform( eSPIRAL, 0.7f, 1.0f );
                    break;
                }
                case 7: {
                    m_mirrorHouseEffect->setMirrorTransform( eRIPPLE, 0.6f, 4.0f );
                    break;
                }
                case 8: {
                    m_mirrorHouseEffect->setMirrorTransform( eSPIKE, 1.0f, 1.0f );
                    break;
                }
                case 9: {
                    m_mirrorHouseEffect->setMirrorTransform( eTILE1, 1.0f, 14.0f );
                    break;
                }
                case 10: {
                    m_mirrorHouseEffect->setMirrorTransform( eDITHER1, 0.04f, 1.0f );
                    break;
                }
                default: {
                    break;
                }
            };
            // Effect quality selection
            if (m_targetImage.size().width()>300) {
                m_mirrorHouseEffect->setHighQuality(false);
            } else {
                m_mirrorHouseEffect->setHighQuality(true);
            }

            // Make effect
            m_mirrorHouseEffect->process();


            m_frame.unmap();

            m_framesExists = true;


            // Update widget
            m_target->updateVideo();
        }
        return true;
    }
}

bool MyVideoSurface::isFormatSupported(
        const QVideoSurfaceFormat &format, QVideoSurfaceFormat *similar) const
{
    Q_UNUSED(similar);

    const QImage::Format imageFormat = QVideoFrame::imageFormatFromPixelFormat(format.pixelFormat());
    const QSize size = format.frameSize();

    return imageFormat != QImage::Format_Invalid
            && !size.isEmpty()
            && format.handleType() == QAbstractVideoBuffer::NoHandle;
}

void MyVideoSurface::paint(QPainter *painter)
 {
    // Paint mirrored image
    if (m_targetImage.byteCount()>0) {
        painter->drawImage(0,0,m_targetImage);
    }
 }

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

void MyVideoSurface::enableEffect(int id, double strength,double count)
{
    m_effectId = id;
    m_strength = strength;
    m_count = count;
}



/* ***************************************************************************
* MirrorItem
*/
MirrorItem::MirrorItem(QDeclarativeItem *parent) :
    QDeclarativeItem(parent)
{

    // Important, otherwise the paint method is never called
    setFlag(QGraphicsItem::ItemHasNoContents, false);

    setFlag(ItemIsMovable);

    // Enable this for getting graphics scene mouse events
    //setAcceptedMouseButtons(Qt::LeftButton);

    // Enable multitouch / listen touch events
    setAcceptTouchEvents(true);
    m_touchEnabled = false;

    m_camera = 0;
    m_myVideoSurface = 0;
    m_effectId = 0;

    // Camera device list
    m_devices = QCamera::availableDevices();
    m_deviceId = 1;  // Set default camera
}

MirrorItem::~MirrorItem()
{
    if (m_myVideoSurface) {
        m_myVideoSurface->stop();
    }
    if (m_camera) {
        m_camera->stop();
    }
    delete m_camera;

}

void MirrorItem::enableEffect(QVariant id, QVariant strength,QVariant count)
{
    m_effectId =  id.toInt();
    m_strength = strength.toDouble();
    m_count = count.toDouble();
}


void MirrorItem::enableCamera(QVariant enable)
{
    bool b = enable.toBool();
    if (b) {
        // Enable camera device
        QTimer::singleShot(100,this,SLOT(startCamera()));
    } else {
        // Keep painting last image readed from camera
        keepPaintingStoredPicture();
        // Stop camera device
        stopCamera();
    }
}

void MirrorItem::pauseCamera()
{
    keepPaintingStoredPicture();
    if (m_myVideoSurface)
        m_myVideoSurface->stop();
}

void MirrorItem::stopCamera()
{
    // Stop camera device
    m_showViewFinder = false;
    if (m_myVideoSurface) {
        m_myVideoSurface->stop();
        m_myVideoSurface = 0;
    }
    if (m_camera) {
        m_camera->stop();
    }
    delete m_camera;
    m_camera = 0;
    m_deviceId = 1; // Set default camera
}

void MirrorItem::enableCameraAt(QVariant enable,QVariant cameraIndex)
{
    if (enable.toBool()) {
        stopCamera();
    }
    m_deviceId = cameraIndex.toInt();
    enableCamera(enable);
}

void MirrorItem::keepPaintingStoredPicture()
{
    if (m_myVideoSurface && m_myVideoSurface->framesExists()) {
        m_lastPicture = m_myVideoSurface->m_targetImage;
    }
}

void MirrorItem::updateVideo()
{
    if (m_showViewFinder) {
        update();
    }
}

void MirrorItem::startCamera()
{
    if (m_camera || m_myVideoSurface) {
        stopCamera();
        enableCamera(QVariant(true));
        return;
    }

    if (m_deviceId > m_devices.count()-1) {
        m_deviceId = 0;
    }
    m_camera = new QCamera(m_devices[m_deviceId]);
    connect(m_camera, SIGNAL(error(QCamera::Error)), this, SLOT(error(QCamera::Error)));

    // Own video output drawing
    QMediaService* ms = m_camera->service();
    QVideoRendererControl* vrc = ms->requestControl<QVideoRendererControl*>();
    m_myVideoSurface = new MyVideoSurface(this,this,this);
    m_myVideoSurface->enableEffect(m_effectId,m_strength,m_count);
    vrc->setSurface(m_myVideoSurface);

    // http://bugreports.qt.nokia.com/browse/QTMOBILITY-1315

    // Start camera
    m_camera->start();
    m_showViewFinder = true;
}

void MirrorItem::error(QCamera::Error e)
{
    qDebug() << "QCamera::Error: " << e << " " << m_camera->errorString();
}


void MirrorItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    painter->setRenderHint(QPainter::Antialiasing, false);
      if (m_myVideoSurface && m_myVideoSurface->framesExists()) {
        // Show view finder
        m_myVideoSurface->paint(painter);
    } else if(m_lastPicture.byteCount()>0) {
        if (boundingRect().width() != m_lastPicture.width()) {
            qDebug() << "Need to scale image";
            // Need to scale QImage picture
            painter->drawImage(boundingRect(),m_lastPicture);
        } else {
        // Keep painting last stored QImage picture
        painter->drawImage(0,0,m_lastPicture);
        }
    } else {
        // Draw empty mirror
        painter->fillRect(boundingRect(), Qt::white);
    }
}


QVariant MirrorItem::isTouch()
{
    return QVariant(m_touchEnabled);
}


bool MirrorItem::sceneEvent(QEvent *event)
{
    switch (event->type()) {
    case QEvent::TouchBegin:
    case QEvent::TouchUpdate:
    case QEvent::TouchEnd: {
        if (event->type() == QEvent::TouchBegin) {
            m_pinchCounter = 3;
            m_touchEnabled = false;
            m_signalSended = false;
        } else if (event->type() == QEvent::TouchUpdate) {
            m_touchEnabled = true;
        } else if (event->type() == QEvent::TouchEnd) {
            m_touchEnabled = false;
            m_signalSended = false;
        }

        QTouchEvent *touchEvent = static_cast<QTouchEvent*>(event);
        QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints();
        if(touchPoints.count() == 2) {
            const QTouchEvent::TouchPoint &touchPoint0 = touchPoints.first();
            const QTouchEvent::TouchPoint &touchPoint1 = touchPoints.last();
            qreal posLineLength = QLineF(touchPoint0.pos(), touchPoint1.pos()).length();
            qreal startPosLineLength = QLineF(touchPoint0.startPos(), touchPoint1.startPos()).length();
            qreal scaleVal = posLineLength / startPosLineLength;

            if (scaleVal < 1.0) {
                m_pinchCounter--;
                if (m_pinchCounter < 0) {
                    if (!m_signalSended) {
                        emit minimizeMirror();
                    }
                    m_signalSended = true;
                }
            } else {
                m_pinchCounter++;
                if (m_pinchCounter > 6) {
                    if (!m_signalSended) {
                        emit maximizeMirror();
                    }
                    m_signalSended = true;
                }
            }
        }
        return true;
    }
    default:
        break;
    }

    return QDeclarativeItem::event(event);
}

void MirrorItem::saveToFile()
{
    if (m_myVideoSurface && m_myVideoSurface->framesExists()) {
        QImage image(m_myVideoSurface->targetImageSize(),m_myVideoSurface->targetImageFormat());
        QPainter painter(&image);
        m_myVideoSurface->paint(&painter);
        doSave(image);
        }
    else if (m_lastPicture.width()>0) {
        doSave(m_lastPicture);
    }
}

void MirrorItem::doSave(QImage image)
{
    QString path(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation));
    QDir dir(path);

    // Get next filename
    QStringList files = dir.entryList(QStringList() << "mirror_*.jpg");
    int lastImage = 0;
    foreach( QString fileName, files ) {
            int imgNumber = fileName.mid(7, fileName.size() - 11).toInt();
            lastImage = qMax(lastImage, imgNumber);
        }
    QString newFileName = QString("mirror_%1.jpg").arg(lastImage+1);
    path.append("/");
    path.append(newFileName);

    image.save(path);
}

