/*
 * dspitems.c: A plugin for the Video Disk Recorder
 *
 * See the README file for copyright information and how to reach the author.
 *
 * Date: 26.05.07
 */

//***************************************************************************
// Includes
//***************************************************************************

#include <sstream>

#include <sysinfo.h>
#include <theme.h>
#include <display.h>
#include <span.h>
#include <setup.h>

//***************************************************************************
// Some Helpers
//***************************************************************************

const char* getFormat(const char* value)
{
   char* fmt = strchr(value, '/');
   
   if (fmt) 
      fmt++;

   return fmt;
}

//***************************************************************************
// Init Statics
//***************************************************************************

Renderer* cDisplayItem::render = 0;
cGraphTFTDisplay* cDisplayItem::vdrStatus = 0;
int cDisplayItem::forceDraw = yes;
uint64_t cDisplayItem::nextForce = 0;
cDisplayItem* cDisplayItem::selectedItem = 0;

//***************************************************************************
// cDisplayItem
//***************************************************************************

uint64_t cDisplayItem::scheduleForce(uint64_t aTime)
{
   nextForce = aTime;

   tell(1, "shedule force in (%ld) ms", nextForce - cTimeMs::Now());

   return nextForce;
}

//***************************************************************************
// Object
//***************************************************************************

cDisplayItem::cDisplayItem()
      : cThemeItem()
{
   nextDraw = 0;
   section = 0;
   marquee_active = no;
}

cDisplayItem::~cDisplayItem()
{
}

//***************************************************************************
// Variable of Group
//***************************************************************************

const char* variableGroup(const char* value, const char* group, 
                          const char*& var)
{
   if (!group)
      return 0;

   if (strncasecmp(value, group, strlen(group)) == 0)
   {
      var = value + strlen(group);
      return group;
   }

   return 0;
}

//***************************************************************************
// Evaluate
//***************************************************************************

const char* cDisplayItem::evaluateExpression(const char* value)
{
   static char buf[1000];  // TODO, dirty and not thread save
   const char* var;
   const char* group;

   if ((group = variableGroup(value, "recording", var))
       || (group = variableGroup(value, "replay", var)))
   {
      const cRecording* recording = 0;

      if (strcmp(group, "recording") == 0)
         recording = Recordings.GetByName(vdrStatus->_recording.c_str());
      else if (strcmp(group, "replay") == 0)
         recording = Recordings.GetByName(vdrStatus->_replay.fileName.c_str());

      // recording variables

      if (!recording || !recording->Info())
      {
         tell(5, "Missing '%s' info", group);
         return "";
      }
      
      if (strcasecmp(var, "EventID") == 0)
         return Str::toStr(recording->Info()->EventID());
      else if (strcasecmp(var, "Path") == 0)
         return Str::notNull(recording->FileName());
      else if (strcasecmp(var, "Title") == 0)
         return Str::notNull(recording->Info()->Title());
      else if (strcasecmp(var, "SubTitle") == 0)
         return Str::notNull(recording->Info()->ShortText());
      else if (strncasecmp(var, "Time", 4) == 0)
         return formatDateTime(recording->start, getFormat(var), buf, sizeof(buf));
#     if APIVERSNUM >= 10500
      else if (strcasecmp(var, "Channel") == 0)
         return Str::notNull(recording->Info()->ChannelName());
#     endif
   }

   else if ((group = variableGroup(value, "event", var)) 
            || (group = variableGroup(value, "following", var))
            || (group = variableGroup(value, "present", var)))
   {
      const cEvent* event = 0;

      if (strcmp(group, "event") == 0)
         event = vdrStatus->_event;
      else if (strcmp(group, "following") == 0)
         event = vdrStatus->_followingEvent;
      else if (strcmp(group, "present") == 0)
         event = vdrStatus->_presentEvent;

      if (!event)
      {
         tell(5, "Missing '%s' info", group);
         return "";
      }

      if (strcasecmp(var, "ID") == 0)
         return Str::toStr(event->EventID());
      else if (strcasecmp(var, "Title") == 0)
         return Str::notNull(event->Title());
      else if (strcasecmp(var, "Subtitle") == 0)
         return Str::notNull(event->ShortText());
      else if (strncasecmp(var, "StartTime", 9) == 0)
         return formatDateTime(event->StartTime(), getFormat(var), buf, sizeof(buf));
      else if (strncasecmp(var, "EndTime", 7) == 0)
         return formatDateTime(event->EndTime(), getFormat(var), buf, sizeof(buf));
   }
   else
   {
      if (strncasecmp(value, "time", 4) == 0)
      {
         const char* fmt = getFormat(value);

         if (strstr(fmt, "%s") || strstr(fmt, "%S") || strstr(fmt, "%T"))
            nextDraw = cTimeMs::Now() + 1000;
         else
            nextDraw = SECONDS((time(0)/60 +1) * 60); // refresh at next full minute
         
         return formatDateTime(time(0), fmt, buf, sizeof(buf));
      }
   }

   // unknown :(

   tell(0, "Unexpected variable '%s'", value);

   return 0;
}

//***************************************************************************
// Evaluate Variable
//***************************************************************************

int cDisplayItem::evaluate(string& buf, const char* var)
{
   const char* s;
   const char* e;
   const char* v;
   const char* p = var;
   char exp[1000+TB];
   int len;

   buf = "";

   while ((s = strchr(p, '{')))
   {
      if (s > p)
         buf.append(p, s-p);
      
      if (!(e = strchr(p, '}')))
      {
         tell(0, "Parsing of [%s] failed, missing bracket '}'", var);
         return fail;
      }

      // found expression

      e--;
      len = min((int)(e-s), 1000);
      strncpy(exp, s+1, len);
      exp[len] = 0;

      if (!(v = evaluateExpression(exp)))
         return fail;
         
      buf.append(v);

      p = e+2;
   }

   buf.append(p);

   return success;
}

//***************************************************************************
// Evaluate Condition
//***************************************************************************

int cDisplayItem::evaluateCondition(const char* cond, int value)
{
   int left, right;
   char op[100]; *op = 0;

   if (_condition.size() <= 0)
      return yes;

   tell(3, "check condition '%s'", _condition.c_str());

   Scan scan(_condition.c_str());

   // scan left

   scan.eat();

   if (scan.isString())
   {
      if (strcmp(scan.lastString(), cond) != 0)
         return yes;

      left = value;
   }
   else if (scan.isNum())
   {
      left = scan.lastInt();
   }
   else
   {
      tell(0, "Error: Invalid left '%s' expression in '%s'", 
           scan.lastString(), _condition.c_str());
      return yes;
   }

   // scan operator

   scan.eat();

   if (!scan.isOperator())
   {
      tell(0, "Error: Invalid operator '%s' expression in '%s'", 
           scan.lastString(), _condition.c_str());
      return yes;
   }

   strcpy(op, scan.lastString());

   // scan right

   scan.eat();

   if (scan.isString())
   {
      if (strcmp(scan.lastString(), cond) != 0)
         return yes;

      right = value;
   }
   else if (scan.isNum())
   {
      right = scan.lastInt();
   }
   else
   {
      tell(0, "Error: Invalid right '%s' expression in '%s'", 
           scan.lastString(), _condition.c_str());
      return yes;
   }

   tell(1, "evaluate condition '%d' '%s' '%d'", left, op, right);

   if (strcmp(op, ">") == 0)
      return left > right;
   
   if (strcmp(op, "<") == 0)
      return left < right;
   
   if (strcmp(op, ">=") == 0)
      return left >= right;
   
   if (strcmp(op, "<=") == 0)
      return left <= right;
   
   if (strcmp(op, "=") == 0 || strcmp(op, "==") == 0)
      return left = right;
   
   if (strcmp(op, "!=") == 0 || strcmp(op, "<>") == 0)
      return left != right;
   
   return no;
}

//***************************************************************************
// Interface
//***************************************************************************

int cDisplayItem::reset()
{
   if (_scroll)
   {
      marquee_active = yes;
      marquee_left = no;
      marquee_idx = na;
      marquee_count = 0;
      marquee_strip = 0;

      nextDraw = cTimeMs::Now();
   }

   return done;   
}

int cDisplayItem::clear()
{
   if (render)
      render->clear();  

   return done;
}

int cDisplayItem::draw()
{
   if (!render)
   {
      tell(0, "Fatal: Missing renderer object !");
      return fail;
   }

   nextDraw = 0;

   return success;
}

int cDisplayItem::refresh()
{
   tell(4, "forceDraw (%d)", forceDraw);
   tell(4, "timeMs (%d);", cTimeMs::Now());
   tell(4, "nextForce in (%d)ms", nextForce - cTimeMs::Now());

   LogDuration ld("cDisplayItem::refresh()");

   // scheduled force (volume, animating, osd-message, ...)

   if (nextForce && cTimeMs::Now() >= nextForce)
   {
      forceDraw = yes;
      nextForce = 0;
   }

   // nextDraw-100 to collect some drawings !

   if ((nextDraw && (cTimeMs::Now() >= nextDraw-100))
       || isForegroundItem() || forceDraw)
   {
      tell(4, "draw '%s'", nameOf());

      return draw() == success ? 1 : 0;
   }

   return 0;
}

//***************************************************************************
// 
//***************************************************************************

std::string cDisplayItem::channelLogoPath(const char* channel)
{
   int s = 0;
   int d = 0;
   int len = strlen(channel);
   char* file = new char[len+TB];

   // remove ' and / 

   while (channel[s])
   {
      if (!strchr("/'", channel[s]))
         file[d++] = channel[s];
      
      s++;
   }

   file[d] = 0;

   // special handling fore some channels

   if (!rep(file, "D [0-9] \\- ..:.."))
   {
      free(file);
      file = strdup("DIREKT");
   }

   string path = "columnimages/" + string(file) + "." 
      + (Format().length() ? Format() : "png");

   tell(2, "looking for image '%s'", path.c_str());

   return path;
}

//***************************************************************************
// Painters
//***************************************************************************

int cDisplayItem::drawText(const char* aText, int y,
                           int height, int clear, int skipLines)
{
   int width;
   unsigned int viewLength;

   string text = Str::notNull(aText);

   y       = y ? y : _y;
   width   = _width ? _width : theTheme->getWidth() - _x;
   height  = height ? height : _height;
   height  = height ? height : theTheme->getHeight() - _y;
   _scroll = _lines > 1 ? no : _scroll;
   viewLength = width / render->charWidthOf(_font.c_str(), _size);

   if (clear)
      drawBackRect(y, _bg_height ? _bg_height : height);
   
   if (!text.length())
      return done;

   tell(3, "drawing text '%s' at %d/%d (%d/%d), '%s'(%d) lines (%d)!",
     text.c_str(), _x, y, width, height, _font.c_str(), _size, _lines);

   tell(4, "scroll is (%d) and marquee_active is (%d) for test '%s'", _scroll,
     marquee_active, text.c_str());

   if (!_scroll || text.length() <= viewLength)
   {
      // normal and 'dots' mode

      render->text(text.c_str(),
                   _font.c_str(), _size, _align,
                   _x, y,
                   _red, _green, _blue,
                   width, height, _lines,
                   _dots, skipLines);
   }

   else
   {
      // marquee and ticker mode

      tell(2, "drawing text in scroll mode '%s'", text.c_str());

      if (_scroll == 1 && marquee_idx + viewLength >= text.length())
         marquee_left = yes;

      else if (_scroll == 2 && marquee_idx >= text.length())
         marquee_idx = na;

      if (marquee_left)
         marquee_idx--;
      else
         marquee_idx++;

      if (marquee_idx == 0)
      {
         marquee_left = no;
         marquee_count++;
      }

      if (_scroll_count && marquee_count > _scroll_count)
      {
         marquee_active = no;
         marquee_idx = 0;
      }

      render->text(text.substr(marquee_idx, text.length()).c_str(),
                   _font.c_str(), _size, _align,
                   _x, y, _red, _green, _blue, width, height, 
                   _lines, marquee_active ? no : _dots);

      if (marquee_active)
      {
         if (marquee_idx == 0)
            nextDraw = cTimeMs::Now() + 1000;
         else
            nextDraw = cTimeMs::Now() + 500;
      }
   }

   return done;
}

int cDisplayItem::drawRectangle()
{
   render->rectangle(_x, _y, _width, _height,
                     _red, _green, _blue, _transparent);

   return done;
}

int cDisplayItem::drawBackRect(int y, int height)
{
   int x = _bg_x != na ? _bg_x : _x;
   y = y ? y : _bg_y != na ? _bg_y : _y;
   int width = _bg_width > 0 ? _bg_width  : _width;
   height = height ? height : _bg_height > 0 ? _bg_height : _height;

   render->rectangle(x, y, width, height,
                     _bg_red, _bg_green, _bg_blue,
                     _bg_transparent);

   return done;
}

int cDisplayItem::drawImage(const char* path, int fit, int aspectRatio)
{
   if (!path)             path = _path.c_str();
   if (fit == na)         fit = _fit;
   if (aspectRatio == na) aspectRatio = _aspect_ratio;

   tell(3, "drawing image '%s' at %d/%d (%d/%d); fit = %d; aspectRatio = %d)",
     path, _x, _y, _width, _height, _fit, aspectRatio);

   drawBackRect();

   render->image(path,
                 _x, _y,
                 _width, _height,
                 fit, aspectRatio);

   return done;
}

const char* cDisplayItem::formatDateTime(time_t time, const char* fmt, char* date, int len)
{
   tm* tmp = localtime(&time);

   string format = fmt ? fmt : (_format.length() ? _format : "%a %d.%m %H:%M");

   *date = 0;

   if (!tmp)
   {
      tell(0, "Error: Can't get localtime!");
      return 0;
   }

   if (strftime(date, len, format.c_str(), tmp) == 0)
   {
      tell(0, "Error: Can't convert time, maybe "
           "invalid format string '%s'!", format.c_str());

      return 0;
   }

   nextDraw = cTimeMs::Now() + SECONDS(1); // TODO dont call always ... ?

   return date;
}

//***************************************************************************
// Draw Image on Background Coordinates
//***************************************************************************

int cDisplayItem::drawImageOnBack(const char* path, int fit)
{
   if (!path)
      path = _path2.c_str();

   tell(3, "drawing image '%s' at %d/%d (%d/%d)",
     path, _bg_x, _bg_y, _bg_width, _bg_height);

   render->image(path,
                 _bg_x, _bg_y,
                 _bg_width, _bg_height,
                 fit);

   if (_delay)
      nextDraw = cTimeMs::Now() + SECONDS(_delay);

   return done;
}

int cDisplayItem::drawProgressBar(double current, double total,
                                  string path, int y, int height,
                                  int withFrame)
{
   int xDone;
   char tmp[50];

   int bgX = _bg_x != na ? _bg_x : _x;
   int bgWidth = _bg_width ? _bg_width : _width;

   int bgY = y != na ? y : _bg_y != na ? _bg_y : _y;
   int bgHeight = height != na ? height : _bg_height ? _bg_height : _height;

   int red = _red;
   int green = _green;
   int blue = _blue;

   bgHeight = bgHeight ? bgHeight : _height;
   height = height == na ? _height : height;
   y = y == na ? _y : y;

   if (!total) total = 1;
   xDone = (int)((current/total) * (float)_width);

   tell(5, "bar, %f/%f  xDone=%d", current, total, xDone);

   // background

   if (_bg_x && withFrame)
      render->rectangle(bgX, bgY,
                        bgWidth, bgHeight,
                        _bg_red, _bg_green, _bg_blue,
                        _bg_transparent);

   if (path != "")
      render->image(path.c_str(),
                    _x, y,
                    _width, height,
                    true);
   else // if (_bg_x <= 0)
      render->rectangle(_x, y, _width, height,
                        _bg_red, _bg_green, _bg_blue,
                        _bg_transparent);

   // foreground

   if (path != "")
   {
      // with image

      render->rectangle(_x+xDone, y,
                        _width-xDone, height,
                        _bg_red, _bg_green, _bg_blue,
                        _bg_transparent);
   }
   else
   {
      // without image

      if (_switch)
      {
         // colorchanging bar

         red = green = 255;
         blue = 0;

         double percent = current / total;

         if (percent < 0.5f)
            red = (int)(255.0f * percent * 2.0f);
         else
            green = (int)(255.0f * (1-percent) * 2.0f);
      }

      render->rectangle(_x, y, xDone, height,
                        red, green, blue,
                        _transparent);
   }

   // draw optional text

   if (_text == "percent")
   {
      int textHeight = _size * 5/3;

      sprintf(tmp, "%3.0f%%", current / (total/100));

      render->text(tmp,
                   _font.c_str(), _size, _align,
                   _x, 
                   y + (height-textHeight)/2,
                   255-red, 255-green, 255-blue,
                   bgWidth, height, 1, no); 
   }
   else if (_text == "value")
   {
      int textHeight = _size * 5/3;

      sprintf(tmp, "%d%s / %d%s", 
              (int)current, _unit.c_str(),
              (int)total, _unit.c_str());

      render->text(tmp,
                   _font.c_str(), _size, _align,
                   _x, 
                   y + (height-textHeight)/2,
                   255-red, 255-green, 255-blue,
                   bgWidth, height, 1, no); 
   }

   return success;
}

int cDisplayItem::drawPartingLine(string text, int y, int height)
{
   tell(3, "drawing parting line at %d/%d (%d/%d)",
     _x, y, _width, height);

   if (_path != "" && text != "")
      render->image(_path.c_str(),
                    _x, y,
                    _width, height);

   if (_path2 != "" && text == "")
      render->image(_path2.c_str(),
                    _x, y,
                    _width, height);

   if (text != "")
      render->text(text.c_str(),
                   _font.c_str(), _size, _align,
                   _x, y,
                   _red, _green, _blue,
                   _width, height, 1);

   return done;
}

//***************************************************************************
// Draw Classes
//***************************************************************************

int cDisplayText::draw()
{
   if (cDisplayItem::draw() != success)
      return fail;

   string p;

   if (evaluate(p, _text.c_str()) == success)
      return drawText(p.c_str());

   return fail;
}

int cDisplayRectangle::draw()
{
   if (cDisplayItem::draw() != success)
      return fail;

   return drawRectangle();
}

int cDisplayImage::draw()
{
   if (cDisplayItem::draw() != success)
      return fail;

   string p;

   for (int i = 0; i < pathCount; i++)
   {
      if (evaluate(p, GetPath(i).c_str()) != success)
         continue;

      tell(3, "%d path [%s] converted to '%s'", i, GetPath(i).c_str(), p.c_str());

      if (p[0] != '/')
      {
         p = string(GraphTFTSetup.PluginConfPath) 
            + "/graphTFT/themes/" 
            + string(theTheme->getDir()) 
            + "/" + p;
      }

      if (fileExists(p.c_str()))
         return drawImage(p.c_str());

      tell(1, "Image '%s' not found :(", p.c_str());
   }

   drawBackRect();

   return done;
}

int cDisplayImageFile::draw()
{
   if (cDisplayItem::draw() != success)
      return fail;

   FILE* fp;
   char line[1000]; *line = 0;
   char* c;

   fp = fopen(_path.c_str(), "r");

   if (fp)
   {
      c = fgets(line, 100 ,fp);

      if (c)
         line[strlen(line)-1] = 0;

      fclose(fp);
   }

   if (!_fit || _aspect_ratio)
      drawBackRect(); 

   if (!Str::isEmpty(line) && fileExists(line))
      drawImage(line);
   else
      drawImage(_path2.c_str());

   if (_delay)
      nextDraw = cTimeMs::Now() + SECONDS(_delay);

   return success;
}

int cDisplayCalibrationCursor::draw()
{
   if (cDisplayItem::draw() != success)
      return fail;

   if (_path != "")
      render->image(_path.c_str(),
                    vdrStatus->calibrationPos.x,
                    vdrStatus->calibrationPos.y,
                    _width, _height,
                    _width || _height);
   else
      render->rectangle(vdrStatus->calibrationPos.x,
                        vdrStatus->calibrationPos.y,
                        _width ? _width : 10,
                        _height ? _height : 10,
                        _red, _green, _blue,
                        _transparent);

   return success;
}

int cDisplayMenuButton::draw()
{
   if (cDisplayItem::draw() != success)
      return fail;

   int index = _item - itemMenuButton;

   if (index >= 0 && index <= 3)
      return drawText(vdrStatus->_menu.buttons[index].c_str(),
                      0, 0, no /*clear*/);

   return fail;
}

int cDisplayMenuButtonBackground::draw()
{
   if (cDisplayItem::draw() != success)
      return fail;

   int index = _item - itemMenuButtonBackground;

   if (index < 0 || index > 3)
      return fail;

   if (vdrStatus->_menu.buttons[index].length())
   {
      if (_path != "")
         return drawImage(_path.c_str());
   }
   else if (_path2 != "")
   {
      return drawImage(_path2.c_str());
   }

   return done;
}

int cDisplayMessage::draw()
{
   static int showUntil = na;
   static string lastMessage = "";

   if (cDisplayItem::draw() != success)
      return fail;

   tell(3, "draw message '%s', vdrStatus->_message", 
     vdrStatus->_message.c_str());

   if (lastMessage != vdrStatus->_message)
   {
      lastMessage = vdrStatus->_message;

      if (vdrStatus->_message == "")
         showUntil = time(0);
      else
         showUntil = time(0) + _delay;

      nextDraw = scheduleForce(cTimeMs::Now() + _delay * 1000);
   }

   if (time(0) < showUntil && vdrStatus->_message != "")
   {
      if (_path != "")
         drawImageOnBack(_path.c_str());
      else
         drawBackRect();
      
      if (vdrStatus->_message.length())
         drawText(vdrStatus->_message.c_str(), 0, 0, no /*clean*/);

      return success;
   }

   return ignore;
}

int cDisplaySymRecording::draw()
{
   if (cDisplayItem::draw() != success)
      return fail;

   if (vdrStatus->_recordings.size() > 0)
      drawImage(_path.c_str());
   else if (_path2.length() > 0)
      drawImage(_path2.c_str());
   else
      drawBackRect();

   nextDraw = cTimeMs::Now() + SECONDS(30);

   return done;
}

int cDisplayRecording::draw()
{
   if (cDisplayItem::draw() != success)
      return fail;

   if (vdrStatus->_recordings.size() > 0)
      drawText(vdrStatus->_recordings[vdrStatus->_showRecording].name.c_str());
   else
      drawBackRect();

   nextDraw = cTimeMs::Now() + SECONDS(30);

   return done;
}

int cDisplayMenuTitle::draw()
{
   if (cDisplayItem::draw() != success)
      return fail;

   return drawText(vdrStatus->_menu.title.c_str());
}

int cDisplayMenuText::draw()
{
   if (cDisplayItem::draw() != success)
      return fail;

   return drawText(vdrStatus->_menu.text.c_str(), 0, 0, yes, vdrStatus->_menu.textStartLine);
}

int cDisplayMenuTextBackground::draw()
{
   if (cDisplayItem::draw() != success)
      return fail;

   if (vdrStatus->_menu.text.length() != 0)
      return drawImage(_path.c_str());

   return done;
}


int cDisplayChannelName::draw()
{
   if (cDisplayItem::draw() != success)
      return fail;

   string text = vdrStatus->_channelInfo != "" ?
                 vdrStatus->_channelInfo : vdrStatus->_channelText;

   drawText(skipspace(text.c_str()));

   return done;
}

int cDisplayChannelOnly::draw()
{
   if (cDisplayItem::draw() != success)
      return fail;

   string text = vdrStatus->_channelInfo != "" ?
                 vdrStatus->_channelInfo : vdrStatus->_channelText;

   drawText(text.substr(text.find(' ') + 1).c_str());

   return done;
}

int cDisplayChannelNumber::draw()
{
   if (cDisplayItem::draw() != success)
      return fail;

   string text = vdrStatus->_channelInfo != "" ?
                 vdrStatus->_channelInfo : vdrStatus->_channelText;

   drawText(text.substr(0, text.find(' ')).c_str());

   return done;
}

int cDisplayVolumeMuteSymbol::draw()
{
   static int lastMute = vdrStatus->_mute;
   static int showUntil = na;

   if (cDisplayItem::draw() != success)
      return fail;

   if (!_permanent && lastMute != vdrStatus->_mute)
   {
      lastMute = vdrStatus->_mute;
      showUntil = time(0) + _delay;

      nextDraw = scheduleForce(cTimeMs::Now() + _delay * 1000);
   }

   if (_permanent || time(0) < showUntil)
   {
      if (vdrStatus->_mute)
         drawImage(_path.c_str());   // is muted
      else if (_path2 != "")
         drawImage(_path2.c_str());
   }

   return done;
}

int cDisplayVolumebar::draw()
{
   static int lastVolume = vdrStatus->_volume;
   static int showUntil = na;

   if (cDisplayItem::draw() != success)
      return fail;

   if (!_permanent && lastVolume != vdrStatus->_volume)
   {
      lastVolume = vdrStatus->_volume;
      showUntil = time(0) + _delay;

      nextDraw = scheduleForce(cTimeMs::Now() + _delay * 1000);
   }

   if ((_permanent || time(0) < showUntil)) // ) && !vdrStatus->_mute)
   {
      if (_path2 != "")
         drawImageOnBack(_path2.c_str());

      drawProgressBar(vdrStatus->_volume, 255, _path,
                      _y, _height, no /*withFrame*/);
   }

   return done;
}

int cDisplayTimebar::draw()
{
   if (cDisplayItem::draw() != success)
      return fail;

   if (vdrStatus->_presentEvent && vdrStatus->_followingEvent)
      drawProgressBar(time(0) - vdrStatus->_presentEvent->StartTime(),
                      vdrStatus->_followingEvent->StartTime() 
                      - vdrStatus->_presentEvent->StartTime(),
                      _path);
   else
      drawBackRect();

   nextDraw = cTimeMs::Now() + SECONDS(30);

   return done;
}

int cDisplayReplayProgressBar::draw()
{
   if (cDisplayItem::draw() != success)
      return fail;

   int current, total;

   if (vdrStatus->_replay.control
       && vdrStatus->_replay.control->GetIndex(current, total))
   {
      drawProgressBar(current, total, _path);
   }

   // update every second

   nextDraw = cTimeMs::Now() + SECONDS(3);

   return done;
}

int cDisplayReplayName::draw()
{
   if (cDisplayItem::draw() != success)
      return fail;

   return drawText(vdrStatus->_replay.name.c_str());
}

int cDisplayReplayCurrent::draw()
{
   if (cDisplayItem::draw() != success)
      return fail;

   int current, total;

   if (vdrStatus->_replay.control
         && vdrStatus->_replay.control->GetIndex(current, total))
   {
      if (total == 0 && current == 0)
         current = total = 1;

      drawText(*IndexToHMSF(current));
   }

   // refresh every second

   nextDraw = cTimeMs::Now() + SECONDS(1);

   return done;
}

int cDisplayReplayTotal::draw()
{
   if (cDisplayItem::draw() != success)
      return fail;

   int current, total;

   if (vdrStatus->_replay.control
         && vdrStatus->_replay.control->GetIndex(current, total))
   {
      if (total == 0 && current == 0)
         current = total = 1;
      else if (total == 0)
         total = current;

      drawText(*IndexToHMSF(total));
   }

   // refresh every second, due to live recording

   nextDraw = cTimeMs::Now() + SECONDS(1);

   return done;
}

int cDisplayReplayRemaining::draw()
{
   if (cDisplayItem::draw() != success)
      return fail;

   int current, total;

   if (vdrStatus->_replay.control
         && vdrStatus->_replay.control->GetIndex(current, total))
   {
      if (total == 0 && current == 0)
         current = total = 1;
      else if (total == 0)
         total = current;
      
      string tmp = (string)"-" + *IndexToHMSF(total - current);
      drawText(tmp.c_str());
   }

   // refresh every second, due to live recording

   nextDraw = cTimeMs::Now() + SECONDS(1);

   return done;
}

int cDisplaySysinfo::draw()
{
   int status = ignore;

   if (cDisplayItem::draw() != success)
      return fail;

   if (_type.find("cpu") == 0)
   {
      int load = Sysinfo::cpuLoad();

      // if (forceDraw || abs(load - lastCpuLoad) > 2)
      {
         lastCpuLoad = load;

         if (_type == "cpuload")
            status = drawProgressBar(load, 100, _path);
         else if (_type == "cpuidle")
            status = drawProgressBar(100-load, 100, _path);
      }
   }

   else if (_type.find("mem") == 0)
   {
      unsigned long total, used, free, cached;
      
      Sysinfo::memInfoMb(total, used, free, cached);

      if (forceDraw || used != lastUsedMem)
      {
         int f = _factor / (1024*1024);  // due to memInfoMb already return MB

         lastUsedMem = used;

         // scale to given factor
         
         total /= f;
         used  /= f;
         free  /= f;
         
         if (_type == "memused")
            status = drawProgressBar(used, total, _path);
         else if (_type == "memfree")
            status = drawProgressBar(free, total, _path);
      }
   }

   else if (_type == "disk" && _reference != "")
   {
      unsigned long free, used;

      free = FreeDiskSpaceMB(_reference.c_str(), (int*)&used);

      // trick, at least 1 due to divide by zero error

      used = used ? used : 1;
      free = free ? free : 1;

      if (forceDraw || used != lastUsedDisk)
      {
         int f = _factor / (1024*1024);  // due to FreeDiskSpaceMB return MB
         
         lastUsedDisk = used;

         // scale to given factor
         
         used /= f;
         free /= f;
         
         status = drawProgressBar(used, free + used, _path);
      }
   }
   else
   {
      // return without sheduling next draw !

      tell(0, "Ignoring sysinfo item of unexpected type '%s'", 
        _type.c_str());

      return fail;
   }

   nextDraw = cTimeMs::Now() + SECONDS(_delay);

   return status;
}

int cDisplaySymbol::draw()
{
   static bool lastPlay = no;
   static bool lastForward = no;
   static int  lastSpeed = 0;

   if (cDisplayItem::draw() != success)
      return fail;

   drawBackRect();

   int status = ignore;
   const char* image = 0;
   bool play, forward;
   int speed;
   cChannel* chan;
   int mailFlag = no;

   if (_item == itemMailSymbol)
   {
      if (cPluginManager::CallFirstService("MailBox-HasNewMail-1.0", &mailFlag))
      {
         nextDraw = cTimeMs::Now() + SECONDS(30);
         image = mailFlag ? _path.c_str() : _path2.c_str();
         tell(3, "mailbox: status (%d)", mailFlag);
      }
   }

   if (vdrStatus->_replay.control
       && vdrStatus->_replay.control->GetReplayMode(play, forward, speed))
   {
      // if (forceDraw || lastPlay != play || lastForward != forward || lastSpeed != speed)
      {
         // only on channge

         switch (_item)
         {
            case itemReplaySymbolPlay:
               image = (play && speed == -1) ? _path.c_str() : _path2.c_str();
                  break;
            case itemReplaySymbolPause:
               image = (!play && speed == -1) ? _path.c_str() : _path2.c_str();
                  break;
            case itemReplaySymbolFF:
               image = (play && forward && speed != -1) ? _path.c_str() : _path2.c_str();
                  break;
            case itemReplaySymbolFB:
               image = (play && !forward && speed != -1) ? _path.c_str() : _path2.c_str();
                  break;
            case itemReplaySymbolSF:
               image = (!play && forward && speed != -1) ? _path.c_str() : _path2.c_str();
                  break;
            case itemReplaySymbolSB:
               image = (!play && !forward && speed != -1) ? _path.c_str() : _path2.c_str();
                  break;
         }

         lastPlay = play; lastForward = forward; lastSpeed = speed;
      }
   }
   else if (vdrStatus->isModeNormal(vdrStatus->_mode) && (chan = Channels.GetByNumber(vdrStatus->_channel)))
   {
      switch (_item)
      {
         case itemSym2ch:
            image = chan->Apid(1) ? _path.c_str() : _path2.c_str();
            break;
         case itemSymDD:
            image = chan->Dpid(0) ? _path.c_str() : _path2.c_str();
            break;
         case itemSymVTX:
            image = chan->Tpid() ? _path.c_str() : _path2.c_str();
            break;
         case itemSymCrypt:
            image = chan->Ca() ? _path.c_str() : _path2.c_str();
            break;
      }
   }

   if (image && *image)
      status = drawImage(image);

   // trigger update for 'replay' icons every second 
   //   :( there are no trigger from vdr

   if (vdrStatus->_replay.control)
      nextDraw = cTimeMs::Now() + SECONDS(2);

   return status;
}

int cDisplayLogo::draw()
{
   if (cDisplayItem::draw() != success)
      return fail;

   string path = _path;

   // if type is given and normal mode (with channelInfo) selected
   // build path dynamic

   if (_type != "" && vdrStatus->isModeNormal(vdrStatus->_mode))
   {
      string text = vdrStatus->_channelInfo != ""
                    ? vdrStatus->_channelInfo : vdrStatus->_channelText;
      path = _path + "/" + text.substr(text.find(' ') + 1) + "." + _type;
   }

   if (_animated)
   {
      // todo
      //       if (_animation)
      //       {
      //          _animation = true;
      //          _displayFrame = 0;
      //          _frameIn = _delay;
      //       }
      //       else if (vdrStatus->_frameIn < 0)
      //       {
      //          _displayFrame = (_displayFrame + 1) % _count;
      //          _frameIn = _delay;
      //       }

      int pos;

      if ((pos = path.rfind('.')) != -1)
      {
         char* temp;
         //          asprintf(&temp, "%s%d%s", path.substr(0, pos).c_str(),
         //                   _displayFrame, path.substr(pos).c_str());
         path = temp;
         free(temp);
      }
   }
   //    else
   //       _animation = false;

   tell(2, "looking for image '%s'", path.c_str());

   return drawImage(path.c_str(), true);
}

int cDisplayMailCount::draw()
{
   if (cDisplayItem::draw() != success)
      return fail;

   unsigned long mailCount = 0;

   if (cPluginManager::CallFirstService("MailBox-GetTotalUnseen-1.0", &mailCount))
   {
      nextDraw = cTimeMs::Now() + SECONDS(30);

      if (mailCount)
         drawText(itoa(mailCount));

      tell(3, "mailbox: (%d) pending mails", mailCount);
   }

   return done;
}

int cDisplayBackground::draw()
{
   if (cDisplayItem::draw() != success)
      return fail;

   // todo
   // implement a force of all other items !?
   
   if (_path != "")
      return cDisplayImage::draw();

   return drawRectangle();
}

int cDisplayMenuSelected::draw()
{
   if (cDisplayItem::draw() != success)
      return fail;

   selectedItem = this;

   return done;
}

int cDisplayMenuColumnSelected::draw()
{
   if (cDisplayItem::draw() != success)
      return fail;

   selectedItem = this;

   return done;
}

//***************************************************************************
// Menu - old style
//***************************************************************************

int cDisplayMenu::draw()
{
   if (cDisplayItem::draw() != success)
      return fail;

   if (!selectedItem)
      return fail;

   int total;
   int tabPercent[cGraphTFTService::MaxTabs] = {0, 0, 0, 0, 0, 0};
   int afterSelect = 0;
   int lineHeight = _size * 5/3;
   int lineHeightSelect = selectedItem->Size() * 5/3;
   int count = ((_height-lineHeightSelect) / lineHeight) + 1;
   int step = vdrStatus->_menu.currentPos - vdrStatus->_menu.currentPosLast;     // step since last refresh

   vdrStatus->_menu.lineHeight = lineHeight;

   if (vdrStatus->_menu.topPos < 0)
      vdrStatus->_menu.topPos = max(0, vdrStatus->_menu.currentPos - count/2);   // initial
   else if (vdrStatus->_menu.currentPos == vdrStatus->_menu.topPos-1)
      vdrStatus->_menu.topPos = vdrStatus->_menu.currentPos;                     // up
   else if (vdrStatus->_menu.currentPos == vdrStatus->_menu.topPos+count)
      vdrStatus->_menu.topPos++;                                                 // down
   else if (vdrStatus->_menu.currentPos < vdrStatus->_menu.topPos
            || vdrStatus->_menu.currentPos > vdrStatus->_menu.topPos+count)
      vdrStatus->_menu.topPos += step;                                           // page up / page down

   if (vdrStatus->_menu.topPos > (int)vdrStatus->_menu.items.size()-count)
      vdrStatus->_menu.topPos = vdrStatus->_menu.items.size()-count;

   if (vdrStatus->_menu.topPos < 0)
      vdrStatus->_menu.topPos = 0;

   vdrStatus->_menu.currentPosLast = vdrStatus->_menu.currentPos;

   // calculate column width

   total = vdrStatus->_menu.charInTabs[0] + vdrStatus->_menu.charInTabs[1] + vdrStatus->_menu.charInTabs[2]
      + vdrStatus->_menu.charInTabs[3] + vdrStatus->_menu.charInTabs[4] + vdrStatus->_menu.charInTabs[5];

   if (!total)
      return done;

   for (int i = 0; vdrStatus->_menu.charInTabs[i] != 0; i++)
      tabPercent[i] = (int)((100L / (double)total) * (double)vdrStatus->_menu.charInTabs[i]);
   
   tell(4, "Graphtft: tabs set to - %d:%d:%d:%d:%d",
        tabPercent[0], tabPercent[1], tabPercent[2],
        tabPercent[3], tabPercent[4]);

   // loop over visible rows ...

   for (int i = vdrStatus->_menu.topPos; i < min((int)vdrStatus->_menu.items.size(),
         vdrStatus->_menu.topPos + count); ++i)
   {
      cDisplayItem* p = i == vdrStatus->_menu.currentPos ? selectedItem : this;
      int y = p->Y() + ((i - vdrStatus->_menu.topPos) * lineHeight);

      if (i == vdrStatus->_menu.currentPos)
      {
         afterSelect = lineHeightSelect - lineHeight;

         // draw the selected backround

         if (selectedItem->Path() != "")
            render->image(selectedItem->Path().c_str(), _x,
                          _y + (i - vdrStatus->_menu.topPos) * lineHeight,
                          _width, _height);

         // draw the columns

         int x = _x;

         for (int t = 0; t < vdrStatus->_menu.items[i].tabCount; ++t)
         {
            if (vdrStatus->_menu.items[i].tabs[t] != "")
            {
               int width = (_width * tabPercent[t]) / 100;

               render->text(vdrStatus->_menu.items[i].tabs[t].c_str(),
                            selectedItem->Font().c_str(), selectedItem->Size(), selectedItem->Align(),
                            x,
                            _y + ((i - vdrStatus->_menu.topPos) * lineHeight),
                            selectedItem->Red(), selectedItem->Green(), selectedItem->Blue(),
                            width,
                            lineHeightSelect, 1);

               x += width;
            }
         }

         if (selectedItem->Path2() != "")
            render->image(selectedItem->Path2().c_str(),
                          _x, _y + (i - vdrStatus->_menu.topPos) * lineHeight,
                          _width, _height);

         if (p->StaticPicture())
         {
            // for the selected item we optionaly draw a additional picture (ImageMap)
            // as image name use the text after the number in the last column ...

            string path = theTheme->getPathFromImageMap(vdrStatus->_menu.items[i].tabs[vdrStatus->_menu.items[i].tabCount-1].c_str());

            if (path != "")
            {
               if (p->StaticX() && p->StaticY())
                  render->image(path.c_str(), p->StaticX(), p->StaticY(),
                                p->StaticWidth(), p->StaticHeight(), yes, yes);
               else if (p->StaticX())
                  render->image(path.c_str(), 
                                p->StaticX(), _y + (i - vdrStatus->_menu.topPos) * lineHeight, 
                                p->StaticWidth(), p->StaticHeight(), yes, yes);
            }
         }
      }

      else
      {
         if (_path != "")
            render->image(_path.c_str(), _x, y + afterSelect, _width, _height);

         int x = _x;

         for (int t = 0; t < vdrStatus->_menu.items[i].tabCount; ++t)
         {
            if (vdrStatus->_menu.items[i].tabs[t] != "")
            {
               int width = (_width * tabPercent[t]) / 100;

               render->text(vdrStatus->_menu.items[i].tabs[t].c_str(),           // test,
                            _font.c_str(), _size, _align,                        // font, font-size, align
                            x, y + afterSelect,
                            _red, _green, _blue,
                            width, lineHeight, 1);

               x += width;
            }
         }
      }
   }

   return done;
}

//***************************************************************************
// Menu Column - new style
//***************************************************************************

int cDisplayMenuColumn::draw()
{
   if (cDisplayItem::draw() != success)
      return fail;

   if (!selectedItem)
      return fail;

   cGraphTFTDisplay::MenuInfo* _menu = &vdrStatus->_menu;

   int afterSelect = 0;
   int lineHeight = _size * 5/3;
   int lineHeightSelect = selectedItem->Size() * 5/3;
   int count = ((_height-lineHeightSelect) / lineHeight) + 1;
   int step = _menu->currentPos - _menu->currentPosLast;       // step since last refresh

   //    tell(0, "_menu->items.size(%d), _menu->topPos(%d), _menu->currentPos(%d)," 
   //         "_menu->currentPosLast(%d), count(%d)", 
   //         _menu->items.size(), _menu->topPos, _menu->currentPos, _menu->currentPosLast, count);

   _menu->lineHeight = lineHeight;
   
   if (_menu->topPos < 0)
      _menu->topPos = max(0, _menu->currentPos - count/2);     // initial
   else if (_menu->currentPos == _menu->topPos-1)
      _menu->topPos = _menu->currentPos;                       // up
   else if (_menu->currentPos == _menu->topPos+count)
      _menu->topPos++;                                         // down
   else if (_menu->currentPos < _menu->topPos
            || _menu->currentPos > _menu->topPos+count)
      _menu->topPos += step;                                   // page up / page down
   
   if (_menu->topPos > (int)_menu->items.size()-count)
      _menu->topPos = _menu->items.size()-count;
   
   if (_menu->topPos < _menu->currentPos-count)
      _menu->topPos = _menu->currentPos-count;
   
   if (_menu->topPos < 0)
      _menu->topPos = 0;
   
   if (step)
      marquee_active = no;

   _menu->currentPosLast = _menu->currentPos;

   // paint the visible rows

   for (int i = _menu->topPos; i < min((int)_menu->items.size(), _menu->topPos + count); ++i)
   {
      cDisplayItem* p = i == _menu->currentPos ? selectedItem : this;
      int y = p->Y() + ((i - _menu->topPos) * lineHeight) + afterSelect;

      tell(1, "colcount of row (%d) is (%d)", i, _menu->items[i].tabCount);

      if (!p->evaluateCondition("colcount", _menu->items[i].tabCount))
         continue ;

      if (i == _menu->currentPos)
      {
         afterSelect = lineHeightSelect - lineHeight;

         if (p->ImageMap())
         {
            // for the selected item we optionaly draw a additional picture (ImageMap)
            // as image name use the text after the number in the last column ...

            string path = theTheme->getPathFromImageMap(_menu->items[i].tabs[p->Number()-1].c_str());

            if (path != "")
            {
               if (p->StaticX() && p->StaticY())
                  render->image(path.c_str(), p->StaticX(), p->StaticY(), 
                                p->StaticWidth(), p->StaticHeight(), yes, yes);
               else if (p->StaticX())
                  render->image(path.c_str(), p->StaticX(), y, 
                                p->StaticWidth(), p->StaticHeight(), yes, yes);
            }
         }

         if (p->StaticPicture())
         {
            // Image with static position for this column

            if (_menu->items[i].tabs[p->Number()-1].c_str()[0])
            {
               if (p->StaticX() && p->StaticY())
                  render->image(p->channelLogoPath(_menu->items[i].tabs[p->Number()-1].c_str()).c_str(),
                                p->StaticX(), p->StaticY(), 0, 0);
               else if (p->StaticX())
                  render->image(p->channelLogoPath(_menu->items[i].tabs[p->Number()-1].c_str()).c_str(),
                                p->StaticX(), y, 0, 0);
            }
         }

         if (p->StaticText())
         {
            // Text with static position for this column

            if (_menu->items[i].tabs[p->Number()-1].c_str()[0])
            {
               render->text(_menu->items[i].tabs[p->Number()-1].c_str(),   // text
                            p->Font().c_str(), p->Size(), p->Align(),      // font, font-size, align
                            p->StaticX(),                                  // x-pos
                            p->StaticY(),                                  // y-pos
                            p->Red(), p->Green(), p->Blue(),               // RGB
                            p->StaticWidth(),                              // width
                            lineHeight, 1);                                // height, line-count
            }
         }
      }

      if (p->Width() == na)
         continue;

      if (_menu->items[i].type == itPartingLine)
      {
         cDisplayItem* partingLine = section->getItem(itemPartingLine);

         if (partingLine)
            partingLine->drawPartingLine(_menu->items[i].tabs[0], y, lineHeight);

         continue;
      }

      // draw image

      if (p->Path() != "")
         render->image(p->Path().c_str(),
                       p->X(), y,
                       p->Width(), lineHeight);

      if (p->Type() == "progress")
      {
         int current = 0;
         int n = 0;
         int barHeight = p->BarHeight();

         if (p->BarHeightUnit() == iuPercent)      // BarHeight in % of row
            barHeight = (int)(((double)lineHeight/100) * (double)p->BarHeight());

         // calc progress

         while (_menu->items[i].tabs[p->Number()-1][n])
         {
            if (_menu->items[i].tabs[p->Number()-1][n] == '|')
               current++;
            n++;
         }

         // draw column's progress-bar

         tell(5, "progress [%d] of (%d%%) at position y = %d [%s]",
           i, current, y, _menu->items[i].tabs[p->Number()-1].c_str());

         if (current)
            p->drawProgressBar(current, 8, p->Path2(), y + (lineHeight-barHeight)/2, barHeight);
      }

      else if (p->Type() == "image")
      {
         // draw column's image

         if (_menu->items[i].tabs[p->Number()-1].length())
         {
            if (p != selectedItem)
               p->drawBackRect(y, lineHeight);

            render->image(p->channelLogoPath(_menu->items[i].tabs[p->Number()-1].c_str()).c_str(),
                          p->X(), y, p->Width(), lineHeight, true);
         }
      }

      else //  if (_menu->items[i].tabs[p->Number()-1] != "")
      {
         // draw column's text

         tell(5, "draw col (%d) '%s'", p->Number(), _menu->items[i].tabs[p->Number()-1].c_str());

         p->drawText(_menu->items[i].tabs[p->Number()-1].c_str(),
                     y, lineHeight, no); // p != selectedItem);
      }
   }

   return done;
}

//***************************************************************************
// Spectrum Analyzer
//***************************************************************************

int cDisplaySpectrumAnalyzer::draw()
{
   if (cDisplayItem::draw() != success)
      return fail;

   if (cPluginManager::CallFirstService(SPAN_GET_BAR_HEIGHTS_ID, 0))
   {
      // tell(0, "draw SpectrumAnalyzer II");

      cSpanService::Span_GetBarHeights_v1_0 GetBarHeights;

      int bandsSA = 20;
      int falloffSA = 8;

      unsigned int* barHeights = new unsigned int[bandsSA];
      unsigned int* barHeightsLeftChannel = new unsigned int[bandsSA];
      unsigned int* barHeightsRightChannel = new unsigned int[bandsSA];
      unsigned int* barPeaksBothChannels = new unsigned int[bandsSA];
      unsigned int* barPeaksLeftChannel = new unsigned int[bandsSA];
      unsigned int* barPeaksRightChannel = new unsigned int[bandsSA];
      unsigned int volumeLeftChannel;
      unsigned int volumeRightChannel;
      unsigned int volumeBothChannels;

      GetBarHeights.bands = bandsSA;
      GetBarHeights.barHeights = barHeights;
      GetBarHeights.barHeightsLeftChannel = barHeightsLeftChannel;
      GetBarHeights.barHeightsRightChannel = barHeightsRightChannel;
      GetBarHeights.volumeLeftChannel = &volumeLeftChannel;
      GetBarHeights.volumeRightChannel = &volumeRightChannel;
      GetBarHeights.volumeBothChannels = &volumeBothChannels;

      GetBarHeights.name = "graphtft";
      GetBarHeights.falloff = falloffSA;
      GetBarHeights.barPeaksBothChannels = barPeaksBothChannels;
      GetBarHeights.barPeaksLeftChannel = barPeaksLeftChannel;
      GetBarHeights.barPeaksRightChannel = barPeaksRightChannel;

      if (cPluginManager::CallFirstService(SPAN_GET_BAR_HEIGHTS_ID, &GetBarHeights))
      {
         int i;
         int barWidth = _width / (2 * bandsSA);
         int width = barWidth * 2 * bandsSA;

         tell(4, "width = %d; barWidth = %d; ", width, barWidth);

         if (_path != "")
            render->image(_path.c_str(),
                          _x, _y, width, _height, true);
         else
            render->rectangle(_x, _y,
                              width, _height,
                              _red, _green, _blue,
                              _transparent);

         for (i = 0; i < bandsSA; i++)
         {
            render->rectangle(_x + 2 * barWidth * i,
                              _y,
                              barWidth,
                              _height - (barHeightsLeftChannel[i] * _height / 100),
                              0, 0, 0, _transparent);

            render->rectangle(_x + 2 * barWidth * i + barWidth,
                              _y,
                              barWidth,
                              _height - (barHeightsRightChannel[i] * _height / 100),
                              0, 0, 0, _transparent);

            // the peak

            //                height = barPeaksBothChannels[i] * item->Height() / 100;

            //                if (height > 0)
            //                {
            //                   render->rectangle(item->X() + barWidth*2*i+ barWidth + 1,
            //                                      item->Y(),
            //                                      item->X() + barWidth*2*i + barWidth+ barWidth + 1,
            //                                      height,
            //                                      item->Red(), item->Green(), item->Blue(),
            //                                      item->Transparent());
            //                }
         }
      }

      delete[] barHeights;
      delete[] barHeightsLeftChannel;
      delete[] barHeightsRightChannel;
      delete[] barPeaksBothChannels;
      delete[] barPeaksLeftChannel;
      delete[] barPeaksRightChannel;
   }

   // for the SpectrumAnalyzer delay is configured in ms!

   nextDraw = cTimeMs::Now() + _delay;  

   return done;
}
