#include "FlowLineTextBrowser.h"

#include <QPainter>
#include <QTextBlock>
#include <QRegularExpression>
#include <QTextDocument>
#include <QTextCursor>
#include <QAbstractTextDocumentLayout>
#include <QScrollBar>

FlowLineTextBrowser::FlowLineTextBrowser(QWidget *parent) : QTextBrowser(parent)
{
    m_lineArea = new LineArea(this);
    m_jl = Q_NULLPTR;

    updateLineAreaWidth();
}

int FlowLineTextBrowser::getFirstVisibleBlock(QTextBlock *firstBlock) const
{
    QTextCursor curs = QTextCursor(this->document());
    curs.movePosition(QTextCursor::Start);
    for(int i=0; i < this->document()->blockCount(); ++i)
    {
        QTextBlock block = curs.block();

        QRect r1 = this->viewport()->geometry();
        QRect r2 = this->document()->documentLayout()->blockBoundingRect(block).translated(
                       this->viewport()->geometry().x(), this->viewport()->geometry().y() - (
                           this->verticalScrollBar()->sliderPosition()
                           ) ).toRect();

        if (r1.contains(r2, true) || r1.intersects(r2))
        {
   //         qDebug() << r2;
            if (firstBlock)
                *firstBlock = block;
            return i;
        }

        curs.movePosition(QTextCursor::NextBlock);
    }

    if (firstBlock)
        *firstBlock = QTextBlock();

    return 0;
}

void FlowLineTextBrowser::showEvent(QShowEvent *)
{
    this->verticalScrollBar()->setSliderPosition(this->verticalScrollBar()->sliderPosition());
}

void FlowLineTextBrowser::lineAreaPaintEvent(QPaintEvent *event)
{

    QPainter painter(m_lineArea);
    painter.fillRect(event->rect(), Qt::lightGray);

    painter.setPen(Qt::red);
    painter.setBrush(Qt::red);

    QTextBlock block;
    getFirstVisibleBlock(&block);

 //   qDebug() << block.text();
    bool foundFirst = false;
    quint16 linenum;

    QTextCursor curs = QTextCursor(this->document());
    curs.movePosition(QTextCursor::Start);
    bool inBlankLine = false;
    // for each visible block
    while (isBlockVisible(block) && block.next().isValid())
    {
        QString text = block.text();
        if (text.contains(QRegularExpression("[0-9A-Fa-f]{4}:")))
        {
            bool ok = true;
            quint16 addr = (quint16) text.left(4).toInt(&ok,16);
            if (ok)
            {
                //                                qDebug() << "Addr:" << uint16ToHex(addr);
                linenum = addr;
                foundFirst = true;
                inBlankLine = false;
            }
            else
            {
                //               qDebug() << "No address!";
            }
        }
        else
        {
            inBlankLine = true;
        }
        // qDebug() << "Channels:" << m_jl->channelsAtAddress(linenum);

        if (foundFirst)
        {
            QRect blockRect = getBlockGeometry(block);
            float topInY  = blockRect.top() + ((1./6.) * blockRect.height());
            float topOutY = blockRect.top() + ((2./6.) * blockRect.height());
            float botOutY = blockRect.top() + ((3./6.) * blockRect.height());
            float botInY  = blockRect.top() + ((4./6.) * blockRect.height());

            // painter.drawLine(0,blockRect.top(),m_lineArea->width(),blockRect.bottom());

            QList<JumpLine> jllist = m_jl->jumpLinesAtAddress(linenum);

            foreach (JumpLine jl, jllist)
            {
                if (jl.type == IsBranch || jl.type == IsBRA)
                {
                    painter.setPen(Qt::yellow);
                    painter.setBrush(Qt::yellow);

                }
                else if (jl.type == IsJMP)
                {
                    painter.setPen(Qt::white);
                    painter.setBrush(Qt::white);

                }
                else
                {
                    painter.setPen(Qt::red);
                    painter.setBrush(Qt::red);
                }
                int offset = getChannelOffset(jl.channel);
                if (!inBlankLine)
                {
                    if (jl.from == linenum)
                    {
                        if (jl.from == jl.to)
                        {
                            painter.drawLine(offset,              botInY,
                                             m_lineArea->width(), botInY);
                            painter.drawLine(offset, botInY, offset, topOutY);
                            painter.drawLine(offset,              topOutY,
                                             m_lineArea->width(), topOutY);
                  //          painter.drawEllipse(QPointF(m_lineArea->width(),topOutY),2,2);
                        }
                        else
                        {
                            if (jl.isUp())
                            {
                                painter.drawLine(offset, topOutY,
                                                 offset, blockRect.top());
                                painter.drawLine(offset,              topOutY,
                                                 m_lineArea->width(), topOutY);
                                painter.drawEllipse(QPointF(m_lineArea->width(),topOutY),2,2);
                            }
                            else
                            {
                                painter.drawLine(offset, botOutY,
                                                 offset, blockRect.bottom());
                                painter.drawLine(offset,              botOutY,
                                                 m_lineArea->width(), botOutY);
                        //        painter.drawEllipse(QPointF(m_lineArea->width(),botOutY),2,2);
                            }
                        }
                    }
                    else if (jl.to == linenum)
                    {
                        if (jl.isUp())
                        {
                            painter.drawLine(offset, botInY,
                                             offset, blockRect.bottom());
                            painter.drawLine(offset,              botInY,
                                             m_lineArea->width(), botInY);

                            QPolygonF varrow;
                            varrow.append(QPointF(offset,   botInY));
                            varrow.append(QPointF(offset-3, botInY+5));
                            varrow.append(QPointF(offset+3, botInY+5));
                            painter.drawConvexPolygon(varrow);


                            QPolygonF harrow;
                            harrow.append(QPointF(m_lineArea->width(),botInY));
                            harrow.append(QPointF(m_lineArea->width()-4,botInY-3));
                            harrow.append(QPointF(m_lineArea->width()-4,botInY+3));
                            painter.drawConvexPolygon(harrow);
                        }
                        else
                        {
                            painter.drawLine(offset, topInY,
                                             offset, blockRect.top());
                            painter.drawLine(offset,               topInY,
                                             m_lineArea->width(),  topInY);
                            QPolygonF varrow;
                            varrow.append(QPointF(offset,  topInY));
                            varrow.append(QPointF(offset-3, topInY-5));
                            varrow.append(QPointF(offset+3, topInY-5));
                            painter.drawConvexPolygon(varrow);

                            QPolygonF harrow;
                            harrow.append(QPointF(m_lineArea->width(),topInY));
                            harrow.append(QPointF(m_lineArea->width()-4,topInY-3));
                            harrow.append(QPointF(m_lineArea->width()-4,topInY+3));
                            painter.drawConvexPolygon(harrow);
                        }
                    }

                    if (linenum > jl.min() && linenum < jl.max())
                    {
                        painter.drawLine(offset, blockRect.top(),offset, blockRect.bottom());
                    }
                }
                else // inBlankLine
                {
                    if (jl.from == linenum)
                    {
                        if (jl.from != jl.to)
                        {
                            if (jl.isDown())
                            {
                                painter.drawLine(offset, blockRect.top(),offset, blockRect.bottom());

                            }
                        }
                    }
                    else if (jl.to == linenum)
                    {
                        if (jl.isUp())
                        {
                            painter.drawLine(offset, blockRect.top(),offset, blockRect.bottom());
                        }
                    }

                    if (linenum > jl.min() && linenum < jl.max())
                    {
                        painter.drawLine(offset, blockRect.top(),offset, blockRect.bottom());
                    }
                }
            }
        }
        block = block.next();
    }
}

QRect FlowLineTextBrowser::getBlockGeometry(QTextBlock block) const
{
    QRect rect = this->document()->documentLayout()->blockBoundingRect(block).translated(
                     this->viewport()->geometry().x(), this->viewport()->geometry().y() - (
                         this->verticalScrollBar()->sliderPosition()
                         ) ).toRect();
    return rect;
}

bool FlowLineTextBrowser::isBlockVisible(QTextBlock block) const
{
    QRect r1 = this->viewport()->geometry();
    QRect r2 = this->document()->documentLayout()->blockBoundingRect(block).translated(
                   this->viewport()->geometry().x(), this->viewport()->geometry().y() - (
                       this->verticalScrollBar()->sliderPosition()
                       ) ).toRect();

    return (r1.contains(r2, true) || r1.intersects(r2));
}

int FlowLineTextBrowser::getChannelOffset(int channel)
{
    int width = lineAreaWidth();
    return width - (channel * 9)- 10;
}


int FlowLineTextBrowser::lineAreaWidth()
{
    if (!m_jl) return 10;
    return m_jl->m_maxChannel* 9 + 20;
}

void FlowLineTextBrowser::setJumpLines(JumpLines *jl)
{
    m_jl = jl;
    m_lineArea->setJumpLines(jl);
    updateLineAreaWidth();
}

void FlowLineTextBrowser::paintEvent(QPaintEvent *event)
{
    m_lineArea->update();
    QTextBrowser::paintEvent(event);
}

void FlowLineTextBrowser::resizeEvent(QResizeEvent *event)
{
    QTextBrowser::resizeEvent(event);

    QRect cr = contentsRect();
    m_lineArea->setGeometry(QRect(cr.left(), cr.top(), lineAreaWidth(), cr.height()));
}

void FlowLineTextBrowser::updateLineAreaWidth()
{
    setViewportMargins(lineAreaWidth(),0,0,0);
}

void FlowLineTextBrowser::updateLineArea(const QRect &rect, int dy)
{
    if (dy)
        m_lineArea->scroll(0, dy);
    else
        m_lineArea->update(0, rect.y(), m_lineArea->width(), rect.height());

    if (rect.contains(viewport()->rect()))
        updateLineAreaWidth();
}

void FlowLineTextBrowser::setLineAreaVisible(bool visible) { m_lineArea->setVisible(visible); }
