#include <QtGui>
#include <SDL_audio.h>
#include "edwaveform.h"


QEDWaveTrackDisplay::QEDWaveTrackDisplay(QWidget *parent) : QWidget(parent)
{
    data      = NULL;
    size      = 0;
    format    = AUDIO_S16SYS;
    channels  = 1;
    freq      = 1;
    scale     = 1.0f;
    time      = offs = 0;
    duration  = 0;
    sduration = 0;

    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
}


void QEDWaveTrackDisplay::setWaveform(void *mdata, int msize, int mformat, int mchannels, int mfreq)
{
    data     = mdata;
    size     = msize;
    format   = mformat;
    channels = mchannels;
    freq     = mfreq;

    int bps = getBps();
    if (bps != 0)
    {
        // Duration in milliseconds
        duration  = ((float) (size / bps) / (float) freq) * 1000.0f;
        
        // Duration in samples
        sduration = msize / bps;
    }
    else
    {
        duration  = 0;
        sduration = 0;
    }

    update();
}


int QEDWaveTrackDisplay::getBps()
{
    int bps = channels;
    switch (format)
    {
        case AUDIO_S16SYS:
        case AUDIO_U16SYS:
            bps *= sizeof(quint16);
            break;
        case AUDIO_S8:
        case AUDIO_U8:
            bps *= sizeof(quint8);
            break;
    }
    return bps;
}


float QEDWaveTrackDisplay::getDuration()
{
    return duration;
}


float QEDWaveTrackDisplay::getTimeScale(float value)
{
    return (value * scale * (float) freq) / 1000.0f;
}


float QEDWaveTrackDisplay::getTimeFromCoord(float value)
{
    return value * scale;
}


void QEDWaveTrackDisplay::setTime(const float mtime)
{
    if (time != mtime && mtime >= 0 && mtime < duration)
    {
        time = mtime;
        emit timeChanged(time);
    }
}


void QEDWaveTrackDisplay::setOffset(const float moffs)
{
    if (offs != moffs && moffs >= 0 && moffs < getDuration())
    {
        offs = moffs;
        emit offsetChanged(offs);
    }
}


void QEDWaveTrackDisplay::setScale(const float mscale)
{
    if (mscale > 0.05)
        scale = mscale;
    emit scaleChanged(scale);
}


void QEDWaveTrackDisplay::setSelection(const float mstart, const float mend)
{
    if (mstart >= 0 && mend >= 0 && fabs(mend - mstart) > 0)
    {
        selectionValid = true;
        if (mend > mstart)
        {
            selectionStart    = mstart;
            selectionDuration = mend - mstart + 1;
        }
        else
        {
            selectionStart    = mend;
            selectionDuration = mstart - mend + 1;
        }
        emit selectionChanged(selectionStart, selectionDuration);
    }
}


void QEDWaveTrackDisplay::clearSelection()
{
    selectionValid    = false;
    selectionStart    = 0;
    selectionDuration = 0;
    emit selectionChanged(selectionStart, selectionDuration);
}


bool QEDTimelineTrackDisplay::getSelection(float *mstart, float *mduration)
{
    if (selectionValid)
    {
        *mstart = selectionStart;
        *mduration = selectionDuration;
    }
    return selectionValid;
}


float QEDWaveTrackDisplay::getScaledWidth()
{
    return getTimeScale(width());
}


float QEDWaveTrackDisplay::getTime()
{
    return time;
}


float QEDWaveTrackDisplay::getOffset()
{
    return offs;
}


void QEDWaveTrackDisplay::paintEvent(QPaintEvent *)
{
    QColor waveColor(0, 150, 0);
    QColor waveCenterLine(100, 100, 100);
    QColor markerColor(255,255,255);
    QColor bgColor(0, 0, 0);//255, 255, 255);

    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.fillRect(QRect(0, 0, width(), height()), bgColor);

    if (data != NULL)
    {
        int voffs = 0;

        painter.save();
        painter.translate(0, height() / 2);

        painter.setPen(waveCenterLine);
        painter.drawLine(0, 0, width(), 0);

        switch (format)
        {
            case AUDIO_S16SYS:
                painter.scale(1.0f, (float) height() / 32768.0f);
                break;
            case AUDIO_U16SYS:
                voffs = -32768;
                painter.scale(1.0f, height() / 32768.0f);
                break;

            case AUDIO_S8:
                painter.scale(1.0f, height() / 128.0f);
                break;
            case AUDIO_U8:
                voffs = -128;
                painter.scale(1.0f, height() / 128.0f);
                break;
        }

        painter.scale(1.0f, 0.5f);
        painter.setPen(waveColor);

        float mscale = (scale * (float)freq) / 1000.0f;
        int prevY = 0, prevX = 0;
        if (format == AUDIO_S16SYS || format == AUDIO_U16SYS)
        {
            qint16 *buf = (qint16 *) data;
            for (int xc = 0; xc < width(); xc++)
            {
                int moffs = (int) (((offs + xc) * mscale));
                if (moffs >= sduration) break;
                int value = buf[moffs * channels] + voffs;
                painter.drawLine(prevX, prevY, xc, value);
                prevY = value;
                prevX = xc;
            }
        }
        else
        if (format == AUDIO_S8 || format == AUDIO_U8)
        {
            qint8 *buf = (qint8 *) data;
            for (int xc = 0; xc < width(); xc++)
            {
                int moffs = (int) (((offs + xc) * mscale));
                if (moffs >= sduration) break;
                int value = buf[moffs * channels] + voffs;
                painter.drawLine(prevX, prevY, xc, value);
                prevY = value;
                prevX = xc;
            }
        }
        
        painter.restore();
    }
    
    float xc = getTimeScale(time - offs), wd = getTimeScale(width());
    if (xc >= 0 && xc <= wd)
    {
        xc = time - offs;
        painter.scale(scale, 1.0f);
        painter.setPen(markerColor);
        painter.drawLine(xc, 0, xc, height());
    }
}


void QEDWaveTrackDisplay::mousePressEvent(QMouseEvent *ev)
{
    switch (ev->button())
    {
        case Qt::LeftButton:
            selectionPoint = ev->pos();
            selectionOffs = offs;
            selecting = false;
            break;

        case Qt::RightButton:
            dragPoint = ev->pos();
            dragOffs = offs;
            dragging = false;
            break;

        default:
            break;
    }
}


void QEDWaveTrackDisplay::mouseMoveEvent(QMouseEvent *ev)
{
    if ((ev->buttons() & Qt::LeftButton) && ev->pos().x() != selPoint.x())
    {
        selecting = true;
        setSelection(selectionOffs + (ev->pos().x() - selectionPoint.x()) / scale);
    }
    if ((ev->buttons() & Qt::RightButton) && ev->pos().x() != dragPoint.x())
    {
        dragging = true;
        setOffset(dragOffs - (ev->pos().x() - dragPoint.x()) / scale);
    }
}


void QEDWaveTrackDisplay::mouseReleaseEvent(QMouseEvent *ev)
{
    if (ev->button() == Qt::LeftButton)
    {
        if (selecting)
        {
            selecting = false;
            setSelection(selOffs + (ev->pos().x() - selPoint.x()) / scale);
        }
    }
    else
    if (ev->button() == Qt::RightButton && !dragging)
    {
        setTime(offs + getTimeFromCoord(ev->pos().x()));
    }
}


QEDWaveTrackView::QEDWaveTrackView(QWidget *parent) : QWidget(parent)
{
    QHBoxLayout *mainLayout = new QHBoxLayout(this);
    mainLayout->setMargin(0);
    wave = new QEDWaveTrackDisplay(this);

    QFrame *infoLayoutContainer = new QFrame(this);
    infoLayoutContainer->setFrameStyle(QFrame::StyledPanel | QFrame::Plain);
    infoLayoutContainer->setLineWidth(2);
    infoLayoutContainer->setFixedWidth(200);

    QVBoxLayout *infoLayout = new QVBoxLayout(infoLayoutContainer);
    infoLayout->setMargin(0);
    infoName = new QLabel("Audio");
    infoName->setStyleSheet("QLabel { background-color: black; color: white; padding: 2px; }");
    infoLayout->addWidget(infoName);

    infoData = new QLabel();
    infoData->setStyleSheet("QLabel { padding: 2px; }");
    infoLayout->addWidget(infoData);

    mainLayout->addWidget(infoLayoutContainer);
    mainLayout->addWidget(wave);

    connect(wave, SIGNAL(timeChanged(float)), this, SLOT(slotTimeChanged(float)));
    connect(wave, SIGNAL(offsetChanged(float)), this, SLOT(slotOffsetChanged(float)));
    connect(wave, SIGNAL(selectionChanged(float,float)), this, SLOT(slotSelectionChanged(float,float)));
}


void QEDWaveTrackView::setWaveform(void *mdata, int msize, int mformat, int mchannels, int mfreq)
{
    QString fmt;
    switch (mformat)
    {
        case AUDIO_S16SYS: fmt = "16bit (S)"; break;
        case AUDIO_U16SYS: fmt = "16bit (U)"; break;
        case AUDIO_S8:     fmt = "8bit (S)"; break;
        case AUDIO_U8:     fmt = "8bit (U)"; break;
        default:           fmt = "?"; break;
    }
    infoData->setText(QString("<b>%1</b>, <b>%2</b> ch, <b>%3</b> Hz").arg(fmt).arg(mchannels).arg(mfreq));
    wave->setWaveform(mdata, msize, mformat, mchannels, mfreq);
    update();
}


void QEDWaveTrackView::setName(QString name)
{
    infoName->setText(name);
    update();
}


void QEDWaveTrackView::setTime(const float mtime)
{
    wave->setTime(mtime);
}


void QEDWaveTrackView::setOffset(const float moffs)
{
    wave->setOffset(moffs);
}


void QEDWaveTrackView::setScale(const float mscale)
{
    wave->setScale(mscale);
}


float QEDWaveTrackView::getTime()
{
    return wave->getTime();
}


float QEDWaveTrackView::getOffset()
{
    return wave->getOffset();
}


float QEDWaveTrackView::getScaledWidth()
{
    return wave->getScaledWidth();
}


void QEDWaveTrackView::slotTimeChanged(float value)
{
    emit timeChanged(value);
}


void QEDWaveTrackView::slotOffsetChanged(float value)
{
    emit offsetChanged(value);
}


float QEDWaveTrackView::getDuration()
{
    return wave->getDuration();
}
