/***************************************************************************
 *   Copyright (C) 2005-2006 Nicolas Hadacek <hadacek@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.                                   *
 ***************************************************************************/
#include "device_group.h"

#if !defined(NO_KDE)
#  include <tqpainter.h>
#  include <tdeglobal.h>

TQColor Device::statusColor(Status status)
{
  switch (status.type()) {
    case Status::Future:         return TQt::blue;
    case Status::InProduction:   return TQt::green;
    case Status::Mature:
    case Status::NotRecommended: return TQColor("orange");
    case Status::EOL:            return TQt::red;
    case Status::Unknown:
    case Status::Nb_Types: break;
  }
  return TQt::black;
}

TQString coloredString(const TQString &text, TQColor color)
{
  return TQString("<font color=\"") + color.name() + "\">" + text + "</font>";
}

TQString supportedString(bool supported)
{
  return coloredString(supported ? i18n("Supported") : i18n("Unsupported"),
                       supported ? TQt::green :  TQt::red);
}

class Tick {
public:
  Tick() {}
  Tick(double value, double oValue) {
    s = TDEGlobal::locale()->formatNumber(value, 1);
    min = oValue;
  }
  TQString s;
  double min;
};

class TickMap : public TQMap<double, Tick>
{
public:
  TickMap() {}
  void add(double value, double oValue) {
    insert(value, Tick(value, oValue), false);
    (*this)[value].min = TQMIN((*this)[value].min, oValue);
  }
};

TQPixmap drawGraph(const TQValueVector<Device::RangeBox> &boxes)
{
  const uint w = 300, h = 200;
  TQPixmap pixmap(w, h);
  pixmap.fill(TQt::white);
  TQPainter p(&pixmap);
  TQFontMetrics f(p.font());
  TickMap xTicks, yTicks;
  xTicks.add(0.0, 0.0);
  yTicks.add(0.0, 0.0);
  for (uint i=0; i<boxes.count(); i++) {
//    tqDebug("box #%i: %f=[%f %f] %f=[%f %f]", i, boxes[i].start.x, boxes[i].start.yMin,
//           boxes[i].start.yMax, boxes[i].end.x, boxes[i].end.yMin, boxes[i].end.yMax);
    xTicks.add(boxes[i].start.x, boxes[i].start.yMin);
    xTicks.add(boxes[i].start.x, boxes[i].start.yMax);
    xTicks.add(boxes[i].end.x, boxes[i].end.yMin);
    xTicks.add(boxes[i].end.x, boxes[i].end.yMax);
    yTicks.add(boxes[i].start.yMin, boxes[i].start.x);
    yTicks.add(boxes[i].start.yMax, boxes[i].start.x);
    yTicks.add(boxes[i].end.yMin, boxes[i].end.x);
    yTicks.add(boxes[i].end.yMax, boxes[i].end.x);
  }
  double xMax = 0.0, yMax = 0.0;
  int xStart = 0;
  int yStart = h-1 - f.lineSpacing();
  TickMap::const_iterator it = xTicks.begin();
  for (; it!=xTicks.end(); ++it) {
    xStart = TQMAX(xStart, f.width(it.data().s));
    xMax = TQMAX(xMax, it.key());
  }
  for (it = yTicks.begin(); it!=yTicks.end(); ++it)
    yMax = TQMAX(yMax, it.key());
  int xEnd = w-1 - f.width(xTicks[xMax].s)/2;
  TQRect rect = f.boundingRect(yTicks[yMax].s);
  int yEnd = rect.height()/2;

  // draw boxes
  p.setPen(TQt::lightGray);
  p.setBrush(TQt::lightGray);
  for (uint i=0; i<boxes.count(); i++) {
    double ax = double(xEnd - xStart)/xMax;
    double ay = double(yEnd - yStart)/yMax;
    TQPointArray pa(4);
    pa.setPoint(0, tqRound(ax*boxes[i].start.x), tqRound(ay*boxes[i].start.yMin));
    pa.setPoint(1, tqRound(ax*boxes[i].end.x), tqRound(ay*boxes[i].end.yMin));
    pa.setPoint(2, tqRound(ax*boxes[i].end.x), tqRound(ay*boxes[i].end.yMax));
    pa.setPoint(3, tqRound(ax*boxes[i].start.x), tqRound(ay*boxes[i].start.yMax));
    pa.translate(xStart, yStart);
    p.drawPolygon(pa);
  }

  // draw axis
  p.setPen(TQt::black);
  p.drawLine(xStart, yStart, w-1, yStart);
  p.drawLine(xStart, yStart, xStart, 0);

  // draw ticks and lines
  p.setPen(TQt::DotLine);
  for (it = yTicks.begin(); it!=yTicks.end(); ++it) {
    int y1 = yStart + tqRound(it.key()*(yEnd-yStart)/yMax);
    TQRect rect = f.boundingRect(it.data().s);
    p.drawText(xStart/2-rect.width()/2 , y1+rect.height()/2, it.data().s);
    int xmin = xStart + tqRound(it.data().min*(xEnd-xStart)/xMax);
    p.drawLine(xStart, y1, xmin, y1);
  }
  for (it = xTicks.begin(); it!=xTicks.end(); ++it) {
    int x1 = xStart + tqRound(it.key()*(xEnd-xStart)/xMax);
    TQRect rect = f.boundingRect(it.data().s);
    p.drawText(x1-rect.width()/2, h-1, it.data().s);
    int ymin = yStart + tqRound(it.data().min*(yEnd-yStart)/yMax);
    p.drawLine(x1, yStart, x1, ymin);
  }

  return pixmap;
}

TQPixmap Device::vddGraph(const TQString &xLabel, const TQString &yLabel,
                         const TQValueVector<Device::RangeBox> &boxes)
{
  uint sp = 10;
  TQPixmap graph = drawGraph(boxes);
  TQPainter p;
  TQFontMetrics f(p.font());
  TQPixmap pixmap(graph.width() + sp + f.width(xLabel), graph.height() + sp + f.lineSpacing());
  pixmap.fill(TQt::white);
  copyBlt(&pixmap, 0, f.lineSpacing() + sp, &graph, 0, 0, graph.width(), graph.height());
  p.begin(&pixmap);
  p.setPen(TQt::black);
  p.drawText(0, f.lineSpacing(), yLabel);
  p.drawText(pixmap.width()-1-f.width(xLabel), pixmap.height()-1, xLabel);
  return pixmap;
}

const Device::Package *Device::barPackage(const char *name, const Device::Data &data)
{
  for (uint i=0; i<data.packages().count(); i++)
    for (uint k=0; k<data.packages()[i].types.count(); k++)
      if ( Package::TYPE_DATA[data.packages()[i].types[k]].name==name ) return &data.packages()[i];
  return 0;
}

TQPixmap Device::pinsGraph(const Device::Package &package)
{
  TQPixmap pixmap;
  TQPainter p;
  TQFontMetrics fm(p.font());
  uint nb = package.pins.count();
  const int hspacing = 3, wspacing = 3, wmark = 10, wpin = 4;
  int theight = fm.ascent() + (fm.ascent()%2==0 ? 1 : 0);
  int height = hspacing + (nb/2)*(hspacing + theight);
  int wnumber = fm.width("1");
  wnumber = TQMAX(wnumber, fm.width(TQString::number(nb/2)));
  wnumber = TQMAX(wnumber, fm.width(TQString::number(nb/2+1)));
  wnumber = TQMAX(wnumber, fm.width(TQString::number(nb)));
  int bwidth = 4*wspacing + 2*wnumber + wmark;
  int lwidth = 0, rwidth = 0;
  for (uint k=0; k<nb/2; k++) {
    lwidth = TQMAX(lwidth, fm.width(package.pins[k]));
    rwidth = TQMAX(rwidth, fm.width(package.pins[nb-k-1]));
  }
  int bx = lwidth + wspacing + wpin;
  int width = bx + bwidth + wpin + wspacing + rwidth;
  pixmap.resize(width, height);
  pixmap.fill(TQt::white);
  p.begin(&pixmap);
  p.setPen(TQPen(TQt::black, 2));
  p.drawRect(bx, 1, bwidth, height-1);
  p.drawArc(bx+wspacing+wnumber+wspacing, -wmark/2+2, wmark, wmark, 0, -180*16);
  for (uint k=0; k<nb/2; k++) {
    int h = hspacing + theight/2 + k*(hspacing + theight);
    p.drawLine(bx-wpin-1, h, bx, h);
    p.drawLine(bx+bwidth, h, bx+bwidth+wpin, h);
    h += theight/2;
    TQString label = package.pins[k];
    p.drawText(bx-wpin-wspacing-fm.width(label), h, label);
    p.drawText(bx+bwidth+wpin+wspacing, h, package.pins[nb-k-1]);
    uint pin = (k+1);
    if ( pin==1 || pin==(nb/2) ) {
      p.drawText(bx+wspacing, h, TQString::number(pin));
      label = TQString::number(nb-k);
      p.drawText(bx+bwidth-wspacing-fm.width(label), h, label);
    }
  }
  p.end();
  return pixmap;
}

TQString Device::htmlInfo(const Device::Data &data, const TQString &deviceHref, const TQString &documentHtml)
{
  TQString doc;

  // title
  doc += "<h1>";
  bool first = true;
  FOR_EACH(Special, special) {
    for (uint k=0; k<data.frequencyRanges().count(); k++) {
      if ( data.frequencyRanges()[k].special!=special ) continue;
      if (first) first = false;
      else doc += " / ";
      doc += data.fname(special);
      break;
    }
  }
  doc += "</h1>";

  doc += "<table>";
  TQString status = coloredString(data.status().label(), statusColor(data.status()));
  doc += htmlTableRow(i18n("Status"), status);
  if ( data.alternatives().count() ) {
    TQString s;
    for (uint i=0; i<data.alternatives().count(); i++) {
      if ( i!=0 ) s += ", ";
      if ( deviceHref.isEmpty() ) s += data.alternatives()[i].upper();
      else {
        TQString href = deviceHref.arg(data.alternatives()[i].upper());
        s += TQString("<a href=\"%1\">%2</a>").arg(href).arg(data.alternatives()[i].upper());
      }
    }
    doc += htmlTableRow(i18n("Alternatives"), s);
  }
  doc += documentHtml;
  doc += "</table>";

  doc += "<hr />";
  doc += "<table>";
  doc += data.group().informationHtml(data);
  TQString s;
  for (uint i=0; i<data.packages().count(); i++)
    for (uint k=0; k<data.packages()[i].types.count(); k++)
      s += i18n(Package::TYPE_DATA[data.packages()[i].types[k]].label) + TQString("[%1] ").arg(data.packages()[i].pins.count());
  doc += htmlTableRow(i18n("Packaging"), s);
  doc += "</table>";

  return doc;
}

TQString Device::htmlPinDiagrams(const Device::Data &data, const TQString &imagePrefix, TQMimeSourceFactory *msf)
{
  TQString doc;
  // pins
  const Package *package = 0;
  for (uint i=0; Package::TYPE_DATA[i].name; i++) {
    if ( Package::TYPE_DATA[i].shape!=Package::Bar ) continue;
    package = barPackage(Package::TYPE_DATA[i].name, data);
    if (package) break;
  }
  if (package) {
    TQPixmap pix = pinsGraph(*package);
    doc += "<table cellpadding=\"3\"><tr bgcolor=\"gray\"><th align=\"center\">";
    for (uint k=0; k<package->types.count(); k++) {
      if ( k!=0 ) doc += " ";
      doc += i18n(Package::TYPE_DATA[package->types[k]].label);
      doc += "(" + TQString::number(package->pins.count()) + ")";
    }
    doc += "</th></tr><tr><td align=\"center\">";
    TQString label = data.name() + "_pins_graph.png";
    doc += "<img src=\"" + imagePrefix + label + "\" />";
    if (msf) msf->setPixmap(label, pix);
    doc += "</td></tr></table>";
  }
  return doc;
}

TQString Device::htmlVoltageFrequencyGraphs(const Device::Data &data, const TQString &imagePrefix, TQMimeSourceFactory *msf)
{
  TQString doc;
  FOR_EACH(Special, special) {
    for (uint k=0; k<data.frequencyRanges().count(); k++) {
      const Device::FrequencyRange &fr = data.frequencyRanges()[k];
      if ( fr.special!=special ) continue;
      doc += "<h3>" + data.fname(special) + " - " + i18n("Temperature range: ") + fr.operatingCondition.label() + "</h3>";
      TQString label = data.name() + "_" + data.fname(special) + "_"
        + fr.operatingCondition.key() + ".png";
      doc += "<img src=\"" + imagePrefix + label + "\" />";
      if (msf) msf->setPixmap(label, Device::vddGraph(i18n("F (MHz)"), i18n("Vdd (V)"), fr.vdds));
    }
  }
  return doc;
}

TQPixmap Device::memoryGraph(const TQValueList<MemoryGraphData> &r)
{
  TQValueList<MemoryGraphData> ranges = r;
  TQPixmap pixmap;
  TQPainter p;
  TQFontMetrics fm(p.font());
  // order
  qHeapSort(ranges);
  // add empty ranges
  TQValueList<MemoryGraphData>::iterator it;
  for (it=ranges.begin(); it!=ranges.end(); ) {
    TQValueList<MemoryGraphData>::iterator prev = it;
    ++it;
    if ( it==ranges.end() ) break;
    if ( (*prev).endAddress+1==(*it).startAddress ) continue;
    MemoryGraphData data;
    data.startAddress = (*prev).endAddress + 1;
    data.endAddress = (*it).startAddress-1;
    ranges.insert(it, data);
  }
  // compute widths and total height
  int theight = fm.ascent() + (fm.ascent()%2==0 ? 1 : 0);
  int hspacing = 5;
  int height = 1;
  int w1 = 0, w2 = 0;
  for (it=ranges.begin(); it!=ranges.end(); ++it) {
    w1 = TQMAX(w1, fm.width((*it).start));
    w1 = TQMAX(w1, fm.width((*it).end));
    w2 = TQMAX(w2, fm.width((*it).label));
    (*it).height = 2*hspacing + theight;
    if ( (*it).startAddress!=(*it).endAddress ) (*it).height += 2*theight;
    height += (*it).height;
  }
  int wspacing = 4;
  int width = wspacing + w1 + wspacing + wspacing + w2;
  pixmap.resize(width, height);
  pixmap.fill(TQt::white);
  p.begin(&pixmap);
  int h = 0;
  // draw ranges
  for (it=ranges.begin(); it!=ranges.end(); ++it) {
    p.setPen(TQPen(TQt::black, 1, TQt::DotLine));
    p.drawLine(0,h, width-1,h);
    p.setPen(TQPen(TQt::black, 1));
    p.setBrush((*it).label.isEmpty() ? TQt::gray : TQt::white);
    p.drawRect(0,h, wspacing+w1+wspacing,(*it).height+1);
    int hmid = h+(*it).height/2+theight/2;
    p.drawText(wspacing+w1+wspacing+wspacing,hmid, (*it).label);
    if ( (*it).startAddress==(*it).endAddress ) p.drawText(wspacing,hmid, (*it).start);
    else {
      p.drawText(wspacing,h+theight, (*it).start);
      p.drawText(wspacing,h+(*it).height-3, (*it).end);
    }
    h += (*it).height;
    p.setPen(TQPen(TQt::black, 1, TQt::DotLine));
    p.drawLine(0,h, width-1,h);
  }
  p.end();
  return pixmap;
}

#endif
