/*
 * Copyright (c) 2012      Francisco Salvador Ballina Sánchez <zballinita@gmail.com>
 * Copyright (c) 2007      Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net>
 * Copyright (c) 2002,2003 Hamish Rodda <rodda@kde.org>
 *
 *  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 of the License, 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.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include <QtCore/QDebug>
#include <QtGui/QLabel>
#include <QtGui/QDesktopWidget>
#include <QX11Info>
#include <QtCore/QTimer>
#include <QtGui/QPixmap>

#include "qtimerconfirmdialog.h"
#include "legacyrandrscreen.h"

LegacyRandRScreen::LegacyRandRScreen(int screenIndex)
    : m_config(0L), m_screen(screenIndex), m_shownDialog(NULL)
{
    loadSettings();
    setOriginal();
}

LegacyRandRScreen::~LegacyRandRScreen()
{
    if (m_config)
        XRRFreeScreenConfigInfo(m_config);
}

void LegacyRandRScreen::loadSettings()
{
    if (m_config)
        XRRFreeScreenConfigInfo(m_config);

    m_config = XRRGetScreenInfo(QX11Info::display(), rootWindow());
    Q_ASSERT(m_config);

    Rotation rotation;
    m_currentSize = m_proposedSize = XRRConfigCurrentConfiguration(m_config, &rotation);
    m_currentRotation = m_proposedRotation = rotation;

    m_pixelSizes.clear();
    m_mmSizes.clear();
    int numSizes;
    XRRScreenSize* sizes = XRRSizes(QX11Info::display(), m_screen, &numSizes);
    for (int i = 0; i < numSizes; i++) {
        m_pixelSizes.append(QSize(sizes[i].width, sizes[i].height));
        m_mmSizes.append(QSize(sizes[i].mwidth, sizes[i].mheight));
    }

    m_rotations = XRRRotations(QX11Info::display(), m_screen, &rotation);

    m_currentRefreshRate = m_proposedRefreshRate = refreshRateHzToIndex(m_currentSize, XRRConfigCurrentRate(m_config));
}

void LegacyRandRScreen::setOriginal()
{
    m_originalSize = m_currentSize;
    m_originalRotation = m_currentRotation;
    m_originalRefreshRate = m_currentRefreshRate;
}

bool LegacyRandRScreen::applyProposed()
{
    Status status;

    if (proposedRefreshRate() < 0)
        status = XRRSetScreenConfig(QX11Info::display(), m_config, rootWindow(), (SizeID)proposedSize(), (Rotation)proposedRotation(), CurrentTime);
    else
    {
        if( refreshRateIndexToHz(proposedSize(), proposedRefreshRate()) <= 0 )
        {
            m_proposedRefreshRate = 0;
        }
        status = XRRSetScreenConfigAndRate(QX11Info::display(), m_config, rootWindow(), (SizeID)proposedSize(), (Rotation)proposedRotation(), refreshRateIndexToHz(proposedSize(), proposedRefreshRate()), CurrentTime);
    }

    if (status == RRSetConfigSuccess)
    {
        m_currentSize = m_proposedSize;
        m_currentRotation = m_proposedRotation;
        m_currentRefreshRate = m_proposedRefreshRate;
        return true;
    }

    return false;
}

bool LegacyRandRScreen::applyProposedAndConfirm()
{
    if (proposedChanged())
    {
        setOriginal();

        if (applyProposed())
        {
            if (!RandR::confirm())
            {
                proposeOriginal();
                applyProposed();
                return false;
            }
        }
        else
        {
            return false;
        }
    }

    return true;
}

Window LegacyRandRScreen::rootWindow() const
{
    return RootWindow(QX11Info::display(), m_screen);
}

QString LegacyRandRScreen::changedMessage() const
{
    if (refreshRate() == -1)
    {
        return tr("New configuration:\nResolution: %1 x %2\nOrientation: %3").arg(QString::number(currentPixelSize().width()), QString::number(currentPixelSize().height()), currentRotationDescription());
    }
    else
    {
        return tr("New configuration:\nResolution: %1 x %2\nOrientation: %3\nRefresh rate: %4").arg(QString::number(currentPixelSize().width()), QString::number(currentPixelSize().height()), currentRotationDescription(), currentRefreshRateDescription());
    }
}

bool LegacyRandRScreen::changedFromOriginal() const
{
    return m_currentSize != m_originalSize || m_currentRotation != m_originalRotation || m_currentRefreshRate != m_originalRefreshRate;
}

void LegacyRandRScreen::proposeOriginal()
{
    m_proposedSize = m_originalSize;
    m_proposedRotation = m_originalRotation;
    m_proposedRefreshRate = m_originalRefreshRate;
}

bool LegacyRandRScreen::proposedChanged() const
{
    return m_currentSize != m_proposedSize || m_currentRotation != m_proposedRotation || m_currentRefreshRate != m_proposedRefreshRate;
}

QString LegacyRandRScreen::currentRotationDescription() const
{
    QString ret = RandR::rotationName(m_currentRotation & RandR::RotateMask);

    if (m_currentRotation != (m_currentRotation & RandR::RotateMask))
    {
        if (m_currentRotation & RR_Rotate_0)
            ret = RandR::rotationName(m_currentRotation & (RR_Reflect_X + RR_Reflect_X), true, true);
        else
            ret += ", " + RandR::rotationName(m_currentRotation & (RR_Reflect_X + RR_Reflect_X), true, false);
    }

    return ret;
}

int LegacyRandRScreen::rotationIndexToDegree(int rotation) const
{
    switch (rotation & RandR::RotateMask) {
        case RR_Rotate_90:
            return 90;

        case RR_Rotate_180:
            return 180;

        case RR_Rotate_270:
            return 270;

        default:
            return 0;
    }
}

int LegacyRandRScreen::rotationDegreeToIndex(int degree) const
{
    switch (degree) {
        case 90:
            return RR_Rotate_90;

        case 180:
            return RR_Rotate_180;

        case 270:
            return RR_Rotate_270;

        default:
            return RR_Rotate_0;
    }
}

const QSize& LegacyRandRScreen::currentPixelSize() const
{
    return m_pixelSizes[m_currentSize];
}

RateList LegacyRandRScreen::refreshRates(int size) const
{
    int nrates;
    short* rrates = XRRRates(QX11Info::display(), m_screen, (SizeID)size, &nrates);

    RateList rateList;
    for (int i = 0; i < nrates; i++)
        rateList.append(rrates[i]);

    return rateList;
}

QString LegacyRandRScreen::refreshRateDirectDescription(int rate) const
{
    return QObject::tr("Refresh rate in Hertz (Hz)", "%1 Hz").arg(rate);
}

QString LegacyRandRScreen::refreshRateIndirectDescription(int size, int index) const
{
    return QObject::tr("Refresh rate in Hertz (Hz)", "%1 Hz").arg(refreshRateIndexToHz(size, index));
}

QString LegacyRandRScreen::refreshRateDescription(int size, int index) const
{
    return QObject::tr("%1 Hz").arg(refreshRates(size)[index]);
}

bool LegacyRandRScreen::proposeRefreshRate(int index)
{
    if (index >= 0 && (int)refreshRates(proposedSize()).count() > index)
    {
        m_proposedRefreshRate = index;
        return true;
    }

    return false;
}

int LegacyRandRScreen::refreshRate() const
{
    return m_currentRefreshRate;
}

QString LegacyRandRScreen::currentRefreshRateDescription() const
{
    return refreshRateIndirectDescription(m_currentSize, m_currentRefreshRate);
}

int LegacyRandRScreen::proposedRefreshRate() const
{
    return m_proposedRefreshRate;
}

int LegacyRandRScreen::refreshRateHzToIndex(int size, int hz) const
{
    int nrates;
    short* rates = XRRRates(QX11Info::display(), m_screen, (SizeID)size, &nrates);

    for (int i = 0; i < nrates; i++)
        if (hz == rates[i])
            return i;

    if (nrates != 0)
        // Wrong input Hz!
        Q_ASSERT(false);

    return -1;
}

int LegacyRandRScreen::refreshRateIndexToHz(int size, int index) const
{
    int nrates;
    short* rates = XRRRates(QX11Info::display(), m_screen, (SizeID)size, &nrates);

    if (nrates == 0 || index < 0)
        return 0;

    // Wrong input Hz!
    if(index >= nrates)
        return 0;

    return rates[index];
}

int LegacyRandRScreen::numSizes() const
{
    return m_pixelSizes.count();
}

const QSize& LegacyRandRScreen::pixelSize(int index) const
{
    return m_pixelSizes[index];
}

const QSize& LegacyRandRScreen::mmSize(int index) const
{
    return m_mmSizes[index];
}

int LegacyRandRScreen::sizeIndex(const QSize &pixelSize) const
{
    for (int i = 0; i < m_pixelSizes.count(); i++)
        if (m_pixelSizes[i] == pixelSize)
            return i;

    return -1;
}

int LegacyRandRScreen::rotations() const
{
    return m_rotations;
}

int LegacyRandRScreen::rotation() const
{
    return m_currentRotation;
}

int LegacyRandRScreen::size() const
{
    return m_currentSize;
}

int LegacyRandRScreen::proposedRotation() const
{
    return m_proposedRotation;
}

void LegacyRandRScreen::proposeRotation(int newRotation)
{
    m_proposedRotation = newRotation & RandR::OrientationMask;
}

int LegacyRandRScreen::proposedSize() const
{
    return m_proposedSize;
}

bool LegacyRandRScreen::proposeSize(int newSize)
{
    if ((int)m_pixelSizes.count() > newSize)
    {
        m_proposedSize = newSize;
        return true;
    }

    return false;
}

void LegacyRandRScreen::load(QSettings &config)
{
    config.beginGroup(QString("Screen%1").arg(m_screen));

    QSize size = config.value("size", currentPixelSize()).toSize();
    int refresh = config.value("refresh", refreshRate()).toInt();
    if (proposeSize(sizeIndex(size)))
        proposeRefreshRate(refreshRateHzToIndex(proposedSize(), refresh));

    int rotation = config.value("rotation", 0).toInt();
    bool reflectX = config.value("reflectX", false).toBool();
    bool reflectY = config.value("reflectY", false).toBool();

    proposeRotation(rotationDegreeToIndex(rotation) +
                        (reflectX ? RandR::ReflectX : 0) +
                        (reflectY ? RandR::ReflectY : 0));
    config.endGroup();
}

void LegacyRandRScreen::save(QSettings &config) const
{
    config.beginGroup(QString("Screen%1").arg(m_screen));
    config.setValue("size", currentPixelSize());
    config.setValue("refresh", refreshRateIndexToHz(size(), refreshRate()));
    config.setValue("rotation", rotationIndexToDegree(rotation()));
    config.setValue("reflectX", (bool)(rotation() & RandR::ReflectMask) == RandR::ReflectX);
    config.setValue("reflectY", (bool)(rotation() & RandR::ReflectMask) == RandR::ReflectY);
    config.endGroup();
}

QStringList LegacyRandRScreen::startupCommands() const
{
    QString command = QString("xrandr -s %1x%2 -r %3 ").arg( currentPixelSize().width(),
        currentPixelSize().height(), refreshRateIndexToHz(size(), refreshRate()));
    switch( rotation())
    {
        case RR_Rotate_90:
            command += " -o 1 ";
            break;
        case RR_Rotate_180:
            command += " -o 2 ";
            break;
        case RR_Rotate_270:
            command += " -o 3 ";
            break;
    }
    if((rotation() & RandR::ReflectMask) == RandR::ReflectX)
        command += " -x ";
    if((bool)(rotation() & RandR::ReflectMask) == RandR::ReflectY)
        command += " -y ";
    return QStringList() << command;
}

int LegacyRandRScreen::pixelCount( int index ) const
{
    QSize sz = pixelSize(index);
    return sz.width() * sz.height();
}

SizeList LegacyRandRScreen::pixelSizes() const
{
    return m_pixelSizes;
}
