#include <QDebug>
#include <Qt>
#include <QApplication>
#include "sniffermodel.h"
#include "snifferwindow.h"
#include "SnifferDelegate.h"

SnifferModel::SnifferModel(QObject *parent)
    : QAbstractItemModel(parent),
      mFilter(false),
      mNeverExpire(false),
      mFadeInactive(false),
      mMuteNotched(false),
      mTimeSequence(0),
      mExpireInterval(5000)
{
    QColor TextColor = QApplication::palette().color(QPalette::Text);
    if (TextColor.red() + TextColor.green() + TextColor.blue() < 200)
    {
        mDarkMode = false;
    }
    else mDarkMode = true;
}

SnifferModel::~SnifferModel()
{
    qDeleteAll(mMap);
    mMap.clear();
    mFilters.clear();
}

void SnifferModel::setExpireInterval(int newVal)
{
    mExpireInterval = newVal;
}

int SnifferModel::columnCount(const QModelIndex &parent) const
{
    return parent.isValid() ? 0 : tc::LAST+1;
}


int SnifferModel::rowCount(const QModelIndex &parent) const
{
    const QMap<quint32, SnifferItem*>& map = mFilter ? mFilters : mMap;
    return parent.isValid() ? 0 : map.size();
}


QVariant SnifferModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    SnifferItem *item = static_cast<SnifferItem*>(index.internalPointer());
    if(!item) QVariant();

    int col = index.column();

    switch(role)
    {
        case Qt::DisplayRole:
        {
            switch(col)
            {
                case tc::DELTA:
                    return QString::number(item->getDelta(), 'f');
                case tc::FREQUENCY:
                    if (item->getDelta() == 0) return QString("0 hz");
                    return QString("%1 hz").arg(qRound(1.00 / item->getDelta()));
                case tc::ID:
                    return "0x" + QString("%1").arg(item->getId(), 5, 16, QLatin1Char('0')).toUpper();
                default:
                    break;
            }
            if(tc::DATA_0<=col && col <=tc::DATA_7)
            {
                int data = item->getData(col-tc::DATA_0);
                if(data >= 0)
                {
                    return QString("%1").arg((uint8_t)data, 2, 16, QLatin1Char('0')).toUpper();
                }
            }
            break;
        }
        case Qt::ForegroundRole:
        {
            if (!mFadeInactive ||  col < 2) return QApplication::palette().brush(QPalette::Text);
            int v = item->getSeqInterval(col - 2) * 10;
            //qDebug() << "mTS: " << mTimeSequence << " gDT(" << (col - 2) << ") " << item->getDataTimestamp(col - 2);
            if (v > 225) v = 225;
            if (v < 0) v = 0;

            if (!mDarkMode) //text defaults to being dark
            {
                return QBrush(QColor(v,v,v,255));
            }
            else //text defaults to being light
            {
                return QBrush(QColor(255-v,255-v,255-v,255));
            }
        }

        case Qt::BackgroundRole:
        {
            if(tc::ID==col)
            {
                if(item->elapsed() > 4000)
                {
                    if (!mDarkMode) return QBrush(Qt::red);
                    return QBrush(QColor(128,0,0));
                }
            }
            else if(tc::DATA_0<=col && col<=tc::DATA_7)
            {
                dc change = item->dataChange(col-tc::DATA_0);
                switch(change)
                {
                    case dc::INC:
                        if (!mDarkMode) return QBrush(Qt::green);
                        return QBrush(QColor(0,128,0));
                    case dc::DEINC:
                        if (!mDarkMode) return QBrush(Qt::red);
                        return QBrush(QColor(128,0,0));
                    default:
                        return QApplication::palette().brush(QPalette::Base);
                }
            }
            break;
        }
    }

    return QVariant();
}


Qt::ItemFlags SnifferModel::flags(const QModelIndex &index) const
{
    if (!index.isValid())
        return Qt::NoItemFlags;

    return QAbstractItemModel::flags(index);
}


QVariant SnifferModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
    {
        switch(section)
        {
            case tc::DELTA:
                return QString("Delta");
            case tc::FREQUENCY:
                return QString("Frequency");
            case tc::ID:
                return QString("ID");
            default:
                break;
        }
        if(tc::DATA_0<=section && section <=tc::DATA_7)
            return QString::number(section-tc::DATA_0);
    }

    return QVariant();
}


QModelIndex SnifferModel::index(int row, int column, const QModelIndex &parent) const
{
    if (parent.isValid())
        return QModelIndex();

    const QMap<quint32, SnifferItem*>& map = mFilter ? mFilters : mMap;

    if(column>tc::LAST || row>=map.size())
        return QModelIndex();

    /* ugly but I can't find best without creating a list to keep indexes */
    QMap<quint32, SnifferItem*>::const_iterator iter;
    int i;
    for(iter = map.begin(), i=0 ; i<row ; ++i, ++iter);

    return createIndex(row, column, iter.value());
}


QModelIndex SnifferModel::parent(const QModelIndex &) const
{
    return QModelIndex();
}

bool SnifferModel::getNeverExpire()
{
    return mNeverExpire;
}

bool SnifferModel::getFadeInactive()
{
    return mFadeInactive;
}

bool SnifferModel::getMuteNotched()
{
    return mMuteNotched;
}

void SnifferModel::setNeverExpire(bool val)
{
    mNeverExpire = val;
}

void SnifferModel::setFadeInactive(bool val)
{
    mFadeInactive = val;
}

void SnifferModel::setMuteNotched(bool val)
{
    mMuteNotched = val;
}

void SnifferModel::clear()
{
    beginResetModel();
    qDeleteAll(mMap);
    mMap.clear();
    mFilters.clear();
    mFilter = false;
    endResetModel();
}

void SnifferModel::updateNotchPoint()
{
    QMap<quint32, SnifferItem*>::iterator i;

    /* update markers */
    for (i = mMap.begin(); i != mMap.end(); ++i)
    {
        i.value()->updateMarker();
    }
}

//Called from window with a timer (currently 200ms)
void SnifferModel::refresh()
{
    QMap<quint32, SnifferItem*>::iterator i;
    QVector<quint32> toRemove;
    SnifferItem* item;

    mTimeSequence++;

    for (i = mMap.begin(); i != mMap.end(); ++i)
    {
        //i.value()->updateMarker();
        if(i.value()->elapsed() > (int)mExpireInterval && !mNeverExpire)
            toRemove.append(i.key());
    }

    if(toRemove.size())
    {
        beginResetModel();
        foreach(quint32 id, toRemove)
        {
            /* remove element */
            item = mMap.take(id);
            mFilters.remove(id);
            delete item;
            /* send notification */
            emit idChange(id, false);
        }
        endResetModel();
    }
    else
        /* refresh data */
        emit dataChanged(createIndex(0, 0),
                    createIndex(rowCount()-1, columnCount()-1), QVector<int>(Qt::DisplayRole));
}


void SnifferModel::filter(fltType pType, int pId)
{
    beginResetModel();
    switch(pType)
    {
        case fltType::NONE:
            /* erase everything */
            mFilter = true;
            mFilters.clear();
            break;
        case fltType::ADD:
            /* add filter to list */
            mFilter = true;
            mFilters[pId] = mMap[pId];
            break;
        case fltType::REMOVE:
            /* remove filter */
            if(!mFilter)
                mFilters = mMap;
            mFilter = true;
            mFilters.remove(pId);
            break;
        case fltType::ALL:
            /* stop filtering */
            mFilter = false;
            mFilters.clear();
            break;
    }
    endResetModel();
}


/***********************************************/
/**********         slots       ****************/
/***********************************************/

void SnifferModel::update(CANConnection*, QVector<CANFrame>& pFrames)
{
    foreach(const CANFrame& frame, pFrames)
    {
        if(!mMap.contains(frame.frameId()))
        {
            int index = std::distance(mMap.begin(), mMap.lowerBound(frame.frameId()));
            /* add the frame */
            beginInsertRows(QModelIndex(), index, index);
            mMap[frame.frameId()] = new SnifferItem(frame, mTimeSequence);
            mMap[frame.frameId()]->update(frame, mTimeSequence, mMuteNotched);
            endInsertRows();

            emit idChange(frame.frameId(), true);
        }
        else
            //updateData
            mMap[frame.frameId()]->update(frame, mTimeSequence, mMuteNotched);
    }
}

void SnifferModel::notch()
{
    QMap<quint32, SnifferItem*>& map = mFilter ? mFilters : mMap;

    foreach(SnifferItem* item, map)
        item->notch(true);
}

void SnifferModel::unNotch()
{
    QMap<quint32, SnifferItem*>& map = mFilter ? mFilters : mMap;

    foreach(SnifferItem* item, map)
        item->notch(false);
}


