/* -*- c++ -*-
 *
 * availability.cpp
 *
 * Copyright (C) 2003 Petter Stokke <ummo@hellokitty.com>
 *
 * 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 Steet, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#include "availability.h"
#include "kmldonkey.h"
#include "network.h"

#include <kglobal.h>
#include <kconfig.h>
#include <klocale.h>
#include <kdebug.h>
#include <kimageeffect.h>
#include <qpainter.h>
#include <qcache.h>
#include <qimage.h>

static QCache<QImage> pieceCache;
static int hits = 0;

AvailabilityRenderer::AvailabilityRenderer(int file)
{
    pieceCache.setAutoDelete(true);
    pieceCache.setMaxCost(2048);
    rfileno = file;
    updateFileInfo();
    isClientRenderer = false;
}

AvailabilityRenderer::AvailabilityRenderer(int file, int source)
{
    rfileno = file;
    clno = source;
    updateFileInfo();
    isClientRenderer = true;
}

void AvailabilityRenderer::updateFileInfo()
{
    FileInfo* fi = fileInfo();
    chunkNo = 0;
    if (fi) chunkNo = fi->fileChunks().size();
}

FileInfo* AvailabilityRenderer::fileInfo()
{
    return KMLDonkey::App->donkey->findDownloadFileNo(rfileno);
}

int AvailabilityRenderer::chunks() const
{
    return chunkNo;
}

void AvailabilityRenderer::paintSection(QPainter& p, QRect& r, QColor& c, int depth, bool isTop)
{
    QString cacheKey = QString("%1:%2:%3:%4:%5").arg(r.width()).arg(r.height())
		       .arg(depth).arg(isTop).arg(c.name());
    QImage* img = pieceCache.find(cacheKey);
    if (!img) {
	QColor d(c.dark(depth));
	img = new QImage(KImageEffect::gradient(r.size(), isTop ? d : c, isTop ? c : d,
						KImageEffect::VerticalGradient, 8));
	pieceCache.insert(cacheKey, img);
    } else hits++;
    p.drawImage(r, *img);
}

void AvailabilityRenderer::paintChunk(QPainter& p, QRect& r, int chunk)
{
    FileInfo* fi = fileInfo();
    if (!fi) return;

    QColorGroup& cg = KMLDonkey::App->availabilityColours;
    int threshold = KMLDonkey::App->availabilityThreshold,
	shadingDepth = KMLDonkey::App->availabilityShadingDepth;
    bool shading = KMLDonkey::App->availabilityShading;
    
    QColor top, bottom;

    if (KMLDonkey::App->donkey->findNetworkNo(fi->fileNetwork())->networkName() == "Donkey") {
	// Only the Donkey network currently supports chunk availability.

	int avail = (int)fi->fileAvailability()[fi->fileNetwork()][chunk];

	if (!avail)
	    top = cg.background();
	else {
	    avail--;
	    if (avail > threshold) avail = threshold;
	    int rdiff = cg.light().red() - cg.dark().red(),
		gdiff = cg.light().green() - cg.dark().green(),
		bdiff = cg.light().blue() - cg.dark().blue();
	    top = QColor(cg.dark().red() + ((rdiff * avail) / threshold),
		       cg.dark().green() + ((gdiff * avail) / threshold),
		       cg.dark().blue() + ((bdiff * avail) / threshold));
	}
    } else {
	// Other networks seem to report ascii '0' as availability,
	// we just render it as a single source.
	top = cg.dark();
    }

    QChar dv;
    if (isClientRenderer) {
	const QMap<int,QString>& cc = fi->fileSources();
	if (cc.contains(clno))
	    dv = cc[clno][chunk];
	else
	    dv = '0';
    } else {
	dv = fi->fileChunks()[chunk];
    }
    if (dv.digitValue() > 0 && dv != '0') {
	bottom = cg.foreground();
	if (dv != '1' || isClientRenderer)
	    top = bottom;
    } else
	bottom = top;

    QRect d = r;
    d.setBottom(d.center().y());
    if (!shading) p.setPen(Qt::NoPen);
    if (shading) paintSection(p, d, top, shadingDepth, true);
    else { p.setBrush(top); p.drawRect(d); }
    d = r;
    d.setTop(d.center().y());
    if (shading) paintSection(p, d, bottom, shadingDepth, false);
    else { p.setBrush(bottom); p.drawRect(d); }
}

void AvailabilityRenderer::paintAvailability(QPainter& p, QRect& g)
{
    if (m_cache.isNull() || m_cache.size() != g.size()) {
	hits = 0;
	m_cache.resize(g.size());
	updateFileInfo();
	QPainter cp(&m_cache);
	int c = chunkNo, s = -1, l, i, r;
	for (i = 0; i < c; i++) {
	    l = (i * m_cache.width()) / c;
	    if (l > s) {
		r = (((i+1) * g.width()) / c) - l;
		if (!r) r = 1;
		QRect d(l, 0, r, m_cache.height());
		paintChunk(cp, d, i);
		s = l;
	    }
	}
    }
    p.drawPixmap(g, m_cache);
}

void AvailabilityRenderer::loseCache()
{
    m_cache.resize(0, 0);
}



AvailabilityWidget::AvailabilityWidget(int file, QWidget* parent, const char* name, bool frame)
    : QFrame(parent, name)
    , AvailabilityRenderer(file)
{
    if (frame)
        setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
    setMinimumSize(minimumSizeHint());
    setBackgroundMode(Qt::NoBackground);
}

void AvailabilityWidget::paintEvent(QPaintEvent* e)
{
    QFrame::paintEvent(e);
    QPainter p(this);
    QRect g = contentsRect();
    p.setClipRect(g);
    paintAvailability(p, g);
    FileInfo* fi = fileInfo();
    QString t;
    if (fi)
        t = i18n("percentage", "%1%").arg(KGlobal::locale()
					  ->formatNumber(((float)fi->fileDownloaded() * 100.0)
							 / (float)fi->fileSize(), 2));
    else
        t = i18n("Unknown file!");
    QFont f = font();
    f.setBold(true);
    p.setFont(f);
    p.setPen(Qt::black);
    for (int x = -1; x <= 1; x++) {
        for (int y = -1; y <= 1; y++) {
            QRect foo = rect();
            foo.moveBy(x, y);
            p.drawText(foo, AlignCenter, t);
        }
    }
    p.setPen(Qt::white);
    p.drawText(rect(), AlignCenter, t);
}

QSizePolicy AvailabilityWidget::sizePolicy() const
{
    return QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
}

QSize AvailabilityWidget::minimumSizeHint() const
{
    return QSize((chunkNo > 300) ? 300 : chunkNo, QFontMetrics(font()).height() + 4 + (frameWidth() * 2));
}

#include "availability.moc"
