/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the QtSensors module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** 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 http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** As a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "sensorfwsensorbase.h"


SensorManagerInterface* SensorfwSensorBase::m_remoteSensorManager = 0;

//According to wikipedia link http://en.wikipedia.org/wiki/Standard_gravity
const float SensorfwSensorBase::GRAVITY_EARTH_THOUSANDTH = 0.00980665;
const int SensorfwSensorBase::KErrNotFound=-1;
const int SensorfwSensorBase::KErrInUse=-14;
QStringList SensorfwSensorBase::m_bufferingSensors = QStringList()
        <<"sensorfw.accelerometer"<<"sensorfw.magnetometer"
       <<"sensorfw.gyroscope"<<"sensorfw.rotationsensor";

SensorfwSensorBase::SensorfwSensorBase(QSensor *sensor)
    : QSensorBackend(sensor),
      m_sensorInterface(0),
      m_bufferSize(-1),
      reinitIsNeeded(false),
      m_prevOutputRange(0),
      m_efficientBufferSize(1),
      m_maxBufferSize(1),
      m_available(false),
      running(false)

{
    watcher = new QDBusServiceWatcher("com.nokia.SensorService",QDBusConnection::systemBus(),
            QDBusServiceWatcher::WatchForRegistration |
            QDBusServiceWatcher::WatchForUnregistration, this);

    connect(watcher, SIGNAL(serviceRegistered(QString)),
            this, SLOT(connectToSensord()));
    connect(watcher, SIGNAL(serviceUnregistered(QString)),
            this, SLOT(sensordUnregistered()));

    connect(sensor, SIGNAL(alwaysOnChanged()),this,SLOT(standyOverrideChanged()));

    m_available = QDBusConnection::systemBus().interface()->isServiceRegistered("com.nokia.SensorService");
    if (m_available)
        connectToSensord();
}

SensorfwSensorBase::~SensorfwSensorBase()
{
    if (m_sensorInterface) {
        stop();
        delete m_sensorInterface, m_sensorInterface = 0;
    }
}

void SensorfwSensorBase::start()
{
    if (m_sensorInterface) {
        // dataRate
        QByteArray type = sensor()->type();
        if (type != QTapSensor::type && type != QProximitySensor::type) {
            int dataRate = sensor()->dataRate();
            int interval = dataRate > 0 ? 1000 / dataRate : 0;
            // for testing maximum speed
            //interval = 1;
            //dataRate = 1000;
            m_sensorInterface->setInterval(interval);
        }

        // outputRange
        int currentRange = sensor()->outputRange();
        int l = sensor()->outputRanges().size();
        if (l > 1) {
            if (currentRange != m_prevOutputRange) {
//#ifdef Q_WS_MAEMO_6
                bool isOk = m_sensorInterface->setDataRangeIndex(currentRange); //NOTE THAT THE CHANGE MIGHT NOT SUCCEED, FIRST COME FIRST SERVED
                if (!isOk) sensorError(KErrInUse);
                else m_prevOutputRange = currentRange;
//#else
//                // TODO: remove when sensord integrated, in sensorfw env there is a delay
//                qoutputrange range = sensor()->outputRanges().at(currentRange);
//                qreal correction = 1/correctionFactor();
//                DataRange range1(range.minimum*correction, range.maximum*correction, range.accuracy*correction);
//                m_sensorInterface->requestDataRange(range1);
//                m_prevOutputRange = currentRange;
//#endif
            }
        }

        // always on
        bool alwaysOn = sensor()->isAlwaysOn();
        m_sensorInterface->setStandbyOverride(alwaysOn);

        // connects after buffering checks
        doConnectAfterCheck();

        int returnCode = m_sensorInterface->start().error().type();
        if (returnCode == 0) {
            running = true;
            return;
        }
        qWarning() << "m_sensorInterface did not start, error code:" << returnCode;
    }
    sensorStopped();
}

void SensorfwSensorBase::stop()
{
    if (m_sensorInterface)
        m_sensorInterface->stop();
    running = false;
}

void SensorfwSensorBase::setRanges(qreal correctionFactor)
{
    if (!m_sensorInterface) return;

    QList<DataRange> ranges = m_sensorInterface->getAvailableDataRanges();

    for (int i = 0, l = ranges.size(); i < l; i++) {
        DataRange range = ranges.at(i);
        qreal rangeMin = range.min * correctionFactor;
        qreal rangeMax = range.max * correctionFactor;
        qreal resolution = range.resolution * correctionFactor;
        addOutputRange(rangeMin, rangeMax, resolution);
    }
}


bool SensorfwSensorBase::doConnectAfterCheck()
{
    if (!m_sensorInterface) return false;

    // buffer size
    int size = bufferSize();

    if (size == m_bufferSize) return true;

    if (m_bufferingSensors.contains(sensor()->identifier()))
        m_sensorInterface->setBufferSize(size);
    else
        size = 1;

    // if multiple->single or single->multiple or if uninitialized
    if ((m_bufferSize > 1 && size == 1) || (m_bufferSize == 1 && size > 1) || m_bufferSize == -1) {
        m_bufferSize = size;
        disconnect(this);
        if (!doConnect()) {
            qWarning() << "Unable to connect "<< sensorName();
            return false;
        }
        return true;
    }
    m_bufferSize = size;
    return true;
}

int SensorfwSensorBase::bufferSize() const
{
    int bufferSize = sensor()->bufferSize();
    if (bufferSize == 1)
        return 1;

    // otherwise check validit
    if (bufferSize < 1) {
        qWarning() << "bufferSize cannot be " << bufferSize << ", must be a positive number >= 1";
        return 1;
    }
    if (bufferSize > m_maxBufferSize) {
        qWarning() << "bufferSize cannot be " << bufferSize << ", MAX value is " << m_maxBufferSize;
        return m_maxBufferSize;
    }
    return bufferSize;
}

qreal SensorfwSensorBase::correctionFactor() const
{
    return 1;
}

void SensorfwSensorBase::connectToSensord()
{
    m_remoteSensorManager = &SensorManagerInterface::instance();
    if (!m_remoteSensorManager->isValid()) {
        qWarning() << "SensorManagerInterface is invalid";
        m_remoteSensorManager = 0;
        return;
    }
    if (running) {
        stop();
        reinitIsNeeded = true;
        start();
        reinitIsNeeded = false;
    }
}

void SensorfwSensorBase::sensordUnregistered()
{
    m_bufferSize = -1;
    reinitIsNeeded = true;
}

bool SensorfwSensorBase::initSensorInterface(QString const &name)
{
    if (!m_sensorInterface) {
        sensorError(KErrNotFound);
        return false;
    }

    //metadata
    const QList<DataRange> intervals = m_sensorInterface->getAvailableIntervals();

    for (int i = 0, l = intervals.size(); i < l; i++) {
        qreal intervalMax = intervals.at(i).max;
        qreal intervalMin = intervals.at(i).min;

        if (intervalMin == 0 && intervalMax == 0) {
            // 0 interval has different meanings in e.g. magge/acce
            // magge -> best-effort
            // acce -> lowest possible
            // in Qt API setting 0 means default
            continue;
        }

        qreal rateMin = intervalMax < 1 ? 1 : 1 / intervalMax * 1000;
        rateMin = rateMin < 1 ? 1 : rateMin;

        intervalMin = intervalMin < 1 ? 10: intervalMin;     // do not divide with 0
        qreal rateMax = 1 / intervalMin * 1000;
        addDataRate(rateMin, rateMax);
    }

    //bufferSizes
    if (m_bufferingSensors.contains(sensor()->identifier())) {

        IntegerRangeList sizes = m_sensorInterface->getAvailableBufferSizes();
        for (int i = 0; i < sizes.size(); i++) {
            int second = sizes.at(i).second;
            m_maxBufferSize = second > m_bufferSize ? second : m_maxBufferSize;
        }
        m_maxBufferSize = m_maxBufferSize < 0 ? 1 : m_maxBufferSize;
        //SensorFW guarantees to provide the most efficient size first
        //TODO: remove from comments
        //m_efficientBufferSize  = m_sensorInterface->hwBuffering()? (l>0?sizes.at(0).first:1) : 1;
    } else {
        m_maxBufferSize = 1;
    }

    sensor()->setMaxBufferSize(m_maxBufferSize);
    sensor()->setEfficientBufferSize(m_efficientBufferSize);

    // TODO deztructor: Leaking abstraction detected. Just copied code
    // from initSensor<>() here, need to
    QByteArray type = sensor()->type();
    if ((type == QAmbientLightSensor::type) // SensorFW returns lux values, plugin enumerated values
        || (type == QIRProximitySensor::type) // SensorFW returns raw reflectance values, plugin % of max reflectance
        || (name == "accelerometersensor") // SensorFW returns milliGs, plugin m/s^2
        || (name == "magnetometersensor") // SensorFW returns nanoTeslas, plugin Teslas
        || (name == "gyroscopesensor")) // SensorFW returns DSPs, plugin milliDSPs
        return true;

    setDescription(m_sensorInterface->description());

    if (name == "tapsensor") return true;
    setRanges();
    return true;
}

void SensorfwSensorBase::standyOverrideChanged()
{
    if (m_sensorInterface)
        m_sensorInterface->setStandbyOverride(sensor()->isAlwaysOn());
}

bool SensorfwSensorBase::isFeatureSupported(QSensor::Feature feature) const
{
    switch (feature) {
    case QSensor::AlwaysOn:
        return true;
    case QSensor::AxesOrientation:
    case QSensor::Buffering:
    case QSensor::AccelerationMode:
    case QSensor::SkipDuplicates:
    case QSensor::PressureSensorTemperature:
    case QSensor::GeoValues:
    case QSensor::Reserved:
    case QSensor::FieldOfView:
        return false;
        break;
    };

    return false;
}

