/**
 *  GraphTFT plugin for the Video Disk Recorder 
 * 
 *  theme.c - A plugin for the Video Disk Recorder
 *
 *  (c) 2004 Lars Tegeler, Sascha Volkenandt, Jörg Wendel
 *
 * This code is distributed under the terms and conditions of the
 * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
 *
 * $Id: theme.c,v 1.12 2007/12/03 19:58:20 root Exp $
 *
 **/

#include <theme.h>
#include <common.h>

#include <setup.h>

//***************************************************************************
// Scanner
//***************************************************************************

const char* Scan::delimiters = " ;,(){}[]";
const char* Scan::operators  = " +-*:<>!=";
const char* Scan::whitespace = " ()";

//***************************************************************************
// The Theme
//***************************************************************************

cGraphTFTTheme* theTheme = 0;
cGraphTFTThemes themes;

//***************************************************************************
// cThemeService
//***************************************************************************

const char* cThemeService::items[] =
{
   "Include",
   "Theme",

   "Text",
   "Image",
   "ImageFile",
   "Channel",
   "ChannelOnly",
   "ChannelNumber",
   "Rectangle",
   "Logo",
   "Timebar",

   "Recording",
   "SymRecording",

   "Sym2ch",
   "SymDD",
   "SymVTX",
   "SymCrypt",
   "MailSymbol",
   "MailCount",

   "ReplaySymbolPlay",
   "ReplaySymbolPause",
   "ReplaySymbolFF",
   "ReplaySymbolFB",
   "ReplaySymbolSF",
   "ReplaySymbolSB",

   "Message",
   "VolumeMuteSymbol",
   "Volumebar",
   "ReplayName",
   "ReplayProgressbar",
   "ReplayTotal",
   "ReplayCurrent",
   "ReplayRemaining",
   "MenuTitle",
   "Menu",
   "MenuSelected",
   "MenuStaticPicture",
   "MenuStaticX",
   "MenuStaticY",

   "MenuButtonRed",
   "MenuButtonGreen",
   "MenuButtonYellow",
   "MenuButtonBlue",

   "MenuButtonBackgroundRed",
   "MenuButtonBackgroundGreen",
   "MenuButtonBackgroundYellow",
   "MenuButtonBackgroundBlue",

   "MenuText",
   "MenuTextBackground",
   "MenuImageMap",
   "SpectrumAnalyzer",
   "PartingLine",
   "Sysinfo",
   "Background",

   "ClickArea",
   "MenuNavigationArea",
   "CalibrationCursor",

   "Column",
   "ColumnSelected",

   "Defaults",

   0
};

const char* cThemeService::toName(eThemeItem aItem)
{
   if (!isValid(aItem))
      return "unknown";

   return items[aItem];
}

cThemeService::eThemeItem cThemeService::toItem(const char* aName)
{
   for (int i = 0; items[i]; i++)
      if (strcasecmp(items[i], aName) == 0)
         return (eThemeItem)i;

   return itemUnknown;
}

string cThemeItem::defines[];
int cThemeItem::defineCount = 0;
int cThemeItem::pendingIfs = 0;
int cThemeItem::skipContent = no;

//***************************************************************************
// Class cThemeItem
//***************************************************************************

cThemeItem::cThemeItem()
{
   _item = itemUnknown;
   //   _section = sectionUnknown;
   _x = na;
   _y = 1;
   _bg_x = na;
   _bg_y = na;
   _image_map = 0;
   _stat_pic=0;
   _stat_text=0;
   _stat_width = 0;
   _stat_height = 0;
   _stat_x=0;
   _stat_y=0;
   _width=0;
   _height=0;
   _bg_width=0;
   _bg_height=0;
   _red=255;
   _green=255;
   _blue=255;
   _transparent=255;
   _bg_red=0;
   _bg_green=0;
   _bg_blue=0;
   _bg_transparent=255;
   _lines=0;
   _size=32;
   _switch=no;
   _align=0;
   _animated=0;
   _delay=0;
   _count=0;
   _spacing = 0;
   _bar_height = 0;
   _bar_height_unit = iuAbsolute;
   _scroll = no;
   _scroll_count = 0;
   _dots = no;
   _permanent = no;
   _factor = 1;
   _aspect_ratio = no;
   _fit = no;

   _unit = "";
   _reference = "";
   _font = "Vera";
   _type = "";
   _format = "";
   _path = "";
   _path2 = "";
   _text = "";
   _number = 0;
   _index = 0;
   _onClick = "";
   _onDblClick = "";
   _sectionInclude = "";
   _condition = "";

   pathCount = 0;
}

cThemeItem::~cThemeItem()
{
}

//***************************************************************************
// Parse one line => one Item
//***************************************************************************

bool cThemeItem::Parse(const char* s)
{
   static cThemeSection* currentSection = 0;

   cDisplayItem* dspItem;
   string tmp;
   int posA;
   int posB;
   int value;
   string toParse = strdup(s);

   // detect directives

   if ((posA = toParse.find("#")) >= 0)
   {
      if (parseDirectives(toParse) != success)
         return true;
   }

   // Skip comments

   if ((posA = toParse.find("//")) >= 0)
   {   
      toParse[posA] = 0;
      
      if (Str::isBlank(toParse.c_str()))
         return true;
   }

   // check if it is a section start

   posA = toParse.find("[");
   posB = toParse.find("]", posA);
   
   if (posA == 0 && posB > 0)
   {
      // new section, switch parsing on !

      if (pendingIfs)
         tell(0, "Warning: Missing '#endif'");

      skipContent = no;
      pendingIfs = 0;

      // section ...
      
      tmp = toParse.substr(posA+1, posB-1);

      // append section

      currentSection = new cThemeSection(tmp);
      theTheme->getSections()->Add(currentSection);
      theTheme->addNormalSection(currentSection->getName());

      return true;
   }

   if (skipContent)
   {
      tell(1, "Skipping line due to directive [%.50s%s]",
        s, strlen(s) > 50 ? ".." : "");

      return true;
   }

   if (currentSection)
   {
      // include section ...

      if (ParseVar(toParse, "Include", &tmp))
      {
         if ((dspItem = newDisplayItem(itemSectionInclude)))
         {
            currentSection->Add(dspItem);
            dspItem->_sectionInclude = tmp;
         }

         return true;
      }

      // item ...

      if (!ParseVar(toParse, "Item", &tmp))
      {
         tell(0, "Ignoring invalid theme line [%.50s%s]",
           s, strlen(s) > 50 ? ".." : "");

         return true;
      }

      // Get the item's data

      _item = toItem(tmp.c_str());

      switch (_item)
      {
         case itemTheme:
         {
            ParseVar(toParse, "name", &tmp);
            theTheme->setName(tmp);
            ParseVar(toParse, "version", &tmp);
            theTheme->setVersion(tmp);
            ParseVar(toParse, "dir", &tmp);
            theTheme->setDir(tmp);
            ParseVar(toParse, "startImage", &tmp);
            theTheme->setStartImage(tmp);
            ParseVar(toParse, "endImage", &tmp);
            theTheme->setEndImage(tmp);
            if (ParseVar(toParse, "width", &value))
               theTheme->setWidth(value);
            if (ParseVar(toParse, "height", &value))
               theTheme->setHeight(value);

            return true;
         }

        case itemDefaults:
        {
           currentSection->setDefaultsItem(newDisplayItem(_item));
           currentSection->getDefaultsItem()->ParseText(toParse);

           return true;
        }

        case itemUnknown:
        {
           tell(0, "Ignoring unknown theme item = %s", tmp.c_str());

           return true;
        }
      }

      dspItem = newDisplayItem(_item);

      if (dspItem)
      {
         if (_item == itemMenuImageMap)
            theTheme->AddMapItem(dspItem);
         else
            currentSection->Add(dspItem);

         // copy default values

         if (currentSection->getDefaultsItem())
            *dspItem = *currentSection->getDefaultsItem(); // copy constructor

         // set/parse item properties

         dspItem->_item = _item;
         dspItem->setSection(currentSection);
         dspItem->ParseText(toParse);
      }
   }

   return true;
}

//***************************************************************************
// Parse Directives
//***************************************************************************

int cThemeItem::parseDirectives(string& toParse)
{
   string exeption;
   
   if (ParseDirective(toParse, "#define", &exeption) == success && exeption.length())
   {
      tell(4, "Adding define '%s'", exeption.c_str());
      defines[defineCount] = exeption;
      defineCount++;
      
      return ignore;
   }

   else if (ParseDirective(toParse, "#endif", &exeption) == success)
   {
      if (!pendingIfs)
         tell(0, "Warning: Ignoring unexpected '#endif'");
      else
         pendingIfs--;

      if (skipContent)
         tell(4, "Swiching parser on again ('#endif' found)");

      skipContent = no;

      return ignore;
   }

   else if ((ParseDirective(toParse, "#ifdef", &exeption) == success
             || ParseDirective(toParse, "#ifndef", &exeption) == success) 
            && exeption.length())
   {
      int negate = no;

      if (toParse.find("#ifndef") == 0)
         negate = yes;

      tell(4, "Found '%s %s'", negate ? "#ifndef" : "#ifdef", exeption.c_str());

      if (pendingIfs)
      {
         tell(0, "Warning: Ignoring cascading '#ifdef' [#ifdef %s]", exeption.c_str());
         return ignore;
      }

      pendingIfs++;
      skipContent = negate ? no : yes;

      for (int i = 0; i < defineCount; i++) 
      {
         if (defines[i] == exeption)
         {
            tell(4, "Define '%s' is set, switching parser '%s'", 
                 negate ? "off" : "on", exeption.c_str());

            skipContent = negate ? yes : no;

            break;
         }
      }

      if (skipContent)
         tell(4, "Parser now '%s' due to '%s' '%s'", 
              skipContent ? "off" : "on",
              negate ? "#ifndef" : "#ifdef",
              exeption.c_str());

      return ignore;
   }

   return success;
}

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

cDisplayItem* cThemeItem::newDisplayItem(int item)
{
   cDisplayItem* newItem = 0;

   switch (item)
   {
      case itemMenuImageMap: 
      case itemDefaults:
      case itemMenuNavigationArea:
      case itemSectionInclude:     newItem = new cDisplayItem();                break;

      case itemText:               newItem = new cDisplayText();                break;
      case itemRectangle:          newItem = new cDisplayRectangle();           break;
      case itemImage:              newItem = new cDisplayImage();               break;
      case itemImageFile:          newItem = new cDisplayImageFile();           break;
      case itemCalibrationCursor:  newItem = new cDisplayCalibrationCursor();   break;
      case itemMessage:            newItem = new cDisplayMessage();             break;
      case itemMenuTitle:          newItem = new cDisplayMenuTitle();           break;
      case itemMenuText:           newItem = new cDisplayMenuText();            break;
      case itemMenuTextBackground: newItem = new cDisplayMenuTextBackground();  break;

      case itemMenu:               newItem = new cDisplayMenu();                break;
      case itemMenuSelected:       newItem = new cDisplayMenuSelected();        break;
      case itemColumn:             newItem = new cDisplayMenuColumn();          break;
      case itemColumnSelected:     newItem = new cDisplayMenuColumnSelected();  break;
      case itemSpectrumAnalyzer:   newItem = new cDisplaySpectrumAnalyzer();    break;
      case itemPartingLine:        newItem = new cDisplayPartingLine();         break;
      case itemSysinfo:            newItem = new cDisplaySysinfo();             break;
      case itemBackground:         newItem = new cDisplayBackground();          break;

      case itemSymRecording:       newItem = new cDisplaySymRecording();        break;
      case itemRecording:          newItem = new cDisplayRecording();           break;

      case itemVolumeMuteSymbol:   newItem = new cDisplayVolumeMuteSymbol();    break;
      case itemVolumebar:          newItem = new cDisplayVolumebar();           break;

      case itemLogo:               newItem = new cDisplayLogo();                break;
      case itemChannel:            newItem = new cDisplayChannelName();         break;
      case itemChannelOnly:        newItem = new cDisplayChannelOnly();         break;
      case itemChannelNumber:      newItem = new cDisplayChannelNumber();       break;
      case itemTimebar:            newItem = new cDisplayTimebar();             break;

      case itemSym2ch:         
      case itemSymDD:       
      case itemSymVTX:       
      case itemSymCrypt:
      case itemMailSymbol:          newItem = new  cDisplaySymbol();           break;
      case itemMailCount:           newItem = new  cDisplayMailCount();        break;

      case itemReplayProgressbar:  newItem = new cDisplayReplayProgressBar();  break;
      case itemReplayName:         newItem = new cDisplayReplayName();         break;
      case itemReplayCurrent:      newItem = new cDisplayReplayCurrent();      break;
      case itemReplayTotal:        newItem = new cDisplayReplayTotal();        break;
      case itemReplayRemaining:    newItem = new cDisplayReplayRemaining();    break;

      case itemReplaySymbolPlay:
      case itemReplaySymbolPause:
      case itemReplaySymbolFF:
      case itemReplaySymbolFB:
      case itemReplaySymbolSF:
      case itemReplaySymbolSB:               
         newItem = new  cDisplaySymbol();
         break;

      case itemMenuButtonRed:
      case itemMenuButtonGreen:
      case itemMenuButtonYellow:
      case itemMenuButtonBlue:
         newItem = new cDisplayMenuButton();
         break;

      case itemMenuButtonBackgroundRed:
      case itemMenuButtonBackgroundGreen:
      case itemMenuButtonBackgroundYellow:
      case itemMenuButtonBackgroundBlue:
         newItem = new cDisplayMenuButtonBackground();
         break;

   };

   if (newItem)
      newItem->_item = item;

   return newItem;
}

//***************************************************************************
// Parse Properties
//***************************************************************************

bool cThemeItem::ParseText(string toParse)
{
   ParseVar(toParse, "x", &_x);
   ParseVar(toParse, "y", &_y);
   ParseVar(toParse, "width", &_width);
   ParseVar(toParse, "height", &_height);
   ParseVar(toParse, "red", &_red);
   ParseVar(toParse, "green", &_green);
   ParseVar(toParse, "blue", &_blue);
   ParseVar(toParse, "transparent", &_transparent);
   ParseVar(toParse, "lines", &_lines);
   ParseVar(toParse, "font", &_font);
   ParseVar(toParse, "size", &_size);
   ParseVar(toParse, "text", &_text);
   ParseVar(toParse, "switch", &_switch);         // bool
   ParseVar(toParse, "align", &_align);
   ParseVar(toParse, "name", &_path);
   ParseVar(toParse, "file", &_path2);
   ParseVar(toParse, "type", &_type);
   ParseVar(toParse, "format", &_format);
   ParseVar(toParse, "animated", &_animated);     // bool
   ParseVar(toParse, "delay", &_delay);
   ParseVar(toParse, "count", &_count);
   ParseVar(toParse, "image_map", &_image_map);   // bool
   ParseVar(toParse, "stat_pic", &_stat_pic);     // bool
   ParseVar(toParse, "stat_text", &_stat_text);   // bool
   ParseVar(toParse, "stat_width", &_stat_width);
   ParseVar(toParse, "stat_height", &_stat_height);
   ParseVar(toParse, "stat_x", &_stat_x);
   ParseVar(toParse, "stat_y", &_stat_y);
   ParseVar(toParse, "bg_x", &_bg_x);
   ParseVar(toParse, "bg_y", &_bg_y);
   ParseVar(toParse, "bg_width", &_bg_width);
   ParseVar(toParse, "bg_height", &_bg_height);
   ParseVar(toParse, "bg_red", &_bg_red);
   ParseVar(toParse, "bg_green", &_bg_green);
   ParseVar(toParse, "bg_blue", &_bg_blue);
   ParseVar(toParse, "bg_transparent", &_bg_transparent);
   ParseVar(toParse, "number", &_number);
   ParseVar(toParse, "spacing", &_spacing);
   ParseVar(toParse, "scroll", &_scroll);
   ParseVar(toParse, "factor", &_factor);
   ParseVar(toParse, "unit", &_unit);
   ParseVar(toParse, "reference", &_reference);
   ParseVar(toParse, "scroll_count", &_scroll_count);
   ParseVar(toParse, "dots", &_dots);
   ParseVar(toParse, "permanent", &_permanent);

   ParseVar(toParse, "fit", &_fit);
   ParseVar(toParse, "aspect_ratio", &_aspect_ratio);

   ParseVar(toParse, "on_click", &_onClick);
   ParseVar(toParse, "on_dblclick", &_onDblClick);

   ParseVar(toParse, "path2", &_path2);
   ParseVar(toParse, "pathON", &_path);
   ParseVar(toParse, "pathOFF", &_path2);
   ParseVar(toParse, "condition", &_condition);

   // now some properties with special handling

   string tmp;

   if (ParseVar(toParse,"bar_height", &tmp))
   {
      _bar_height = atoi(tmp.c_str());

      if (strchr(tmp.c_str(), '%'))
         _bar_height_unit = iuPercent;
      else
         _bar_height_unit = iuAbsolute;
   }

   if (ParseVar(toParse, "path", &_path))
   {
      int e, s;
      e = s = 0;

      do
      {
         e = _path.find(':', s);

         pathList[pathCount] = _path.substr(s, e == na ? _path.length()-s : e-s);
         tell(4, "path[%d] = '%s'", pathCount, pathList[pathCount].c_str());
         pathCount++;

         s = e+1;
      } while (s > 0 && pathCount < maxPathCount);
   }

   return true;
}

//***************************************************************************
// Search the int parameter
//***************************************************************************

bool cThemeItem::ParseVar(string toParse, string name, int* value)
{
   string val;
   int status;

   if ((status = ParseVar(toParse, name, &val)))
   {
      if (val == "yes" || val == "true")
         *value = 1;
      else if (val == "no" || val == "false")
         *value = 0;
      else
         *value = atoi(val.c_str());
   }

   return status;
}

//***************************************************************************
// Search the string parameter
//***************************************************************************

bool cThemeItem::ParseVar(string toParse, string name, string* value)
{
   int posA, posB, end;

   name += "=";

   if ((posA = toParse.find("," + name)) < 0)
      if ((posA = toParse.find(" " + name)) < 0)
         if ((posA = toParse.find(name)) != 0)
            return false;

   if (posA)  posA++;
   posB = posA;

   while (posB < (int)toParse.length())
   {
      if ((end = toParse.find(",", posB)) < 0)  
         if ((end = toParse.find(";", posB)) < 0)
            return false;

      // if at first pos or without mask sign, 
      // then we have found the ent of the item
      // -> break the loop

      if (end == 0 || toParse[end-1] != '\\')
         break;

      // in other case search again behind the sign

      posB = end+1;
   }

   *value = toParse.substr(posA + name.size(), end-posA-name.size());

   // de mask '\' sign

   if (value->find("\\", 0) >= 0)
   {
      char* buf; char* s; char* d;

      asprintf(&buf, "%s", value->c_str());

      s = d = buf;

      while (*s)
      {
         if (*s != '\\')
            *d++ = *s;

         s++;
      }
      
      *d = 0; *value = buf;
      free(buf);
   }

   return true;
}

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

bool cThemeItem::ParseDirective(string toParse, string name, string* value)
{
   int posA;

   *value = "";

   if ((posA = toParse.find(name)) != 0)
      return fail;

   if (toParse.length() > posA + name.size() + 1)
      *value = toParse.substr(posA + name.size() + 1);

   return success;
}

//***************************************************************************
// Class cThemeSections
//***************************************************************************

//***************************************************************************
// Get Section By Name
//***************************************************************************

cThemeSection* cThemeSections::getSection(string name)
{
   for (cThemeSection* p = First(); p; p = Next(p)) 
      if (p->getName() == name)
         return p;

   return 0;
}


//***************************************************************************
// Class cThemeSection
//***************************************************************************

uint64_t cThemeSection::getNextUpdateTime()
{
   uint64_t next = cTimeMs::Now() + SECONDS(300); // 5 minutes
   cDisplayItem* pNext = 0;

   if (logLevel >= 1)
   {
      uint64_t now = cTimeMs::Now();
      string date = *TimeToString(now/1000);
      tell(2, "now it is '%s,%03d'", date.substr(11,8).c_str(), now%1000);
   }

   for (cDisplayItem* p = First(); p; p = Next(p)) 
   {
      if (p->getNextDraw() && p->getNextDraw() < next)
      {
         string date = *TimeToString(p->getNextDraw()/1000);

         tell(0, "setting next to '%-20.20s'(%d) at '%s,%03d'",
           p->nameOf(), p->Item(),
           date.substr(11,8).c_str(), p->getNextDraw()%1000);

         pNext = p;
         next = p->getNextDraw();
      }
   }

   if (pNext)
   {
      int updateIn = next - cTimeMs::Now();

      if (updateIn < 0)
         updateIn = 0;

      unsigned int s = updateIn/1000; 
      unsigned int us = updateIn%1000;

      tell(2, "schedule next for '%s' (%d) in %d,%03d seconds",
        pNext->nameOf(), pNext->Item(), s, us);
   }

   return next < cTimeMs::Now() ? cTimeMs::Now() : next;
}

int cThemeSection::updateGroup(int group)
{
   tell(2, "update item group (%d)", group);

   for (cDisplayItem* p = First(); p; p = Next(p))
   {
      if (p->groupOf() & group)
      {
         p->reset();
         p->setNextDraw();
      }
   }

   return done;
}

int cThemeSection::reset()
{
   tell(1, "reset items");

   for (cDisplayItem* p = First(); p; p = Next(p)) 
      p->reset();

   return done;
}

//***************************************************************************
// Class cDisplayItems
//***************************************************************************
//***************************************************************************
// Get Item By ID
//***************************************************************************

cDisplayItem* cDisplayItems::getItem(int id)
{
   for (cDisplayItem* p = First(); p; p = Next(p)) 
   {
      if (p->Item() == id)
         return p;
   }

   return 0;
}

//***************************************************************************
// Class GraphTFTTheme
//***************************************************************************

cGraphTFTTheme::cGraphTFTTheme()
{ 
   initialized = no; 

   width = 720;
   height = 576;
   memset(normalModes, 0, sizeof(normalModes));
   normalModesCount = 0; 
}

//***************************************************************************
// Init
//***************************************************************************

int cGraphTFTTheme::init()
{
   cThemeSection* s;
   cThemeSection* sec;
   cDisplayItem* newItem;
   cDisplayItem* item;
   cDisplayItem* p;

   if (initialized)
      exit();

   // add items of 'included' sections

   // loop over sections
   
   for (s = FirstSection(); s; s = NextSection(s))
   {
      // loop over sections items

      for (p = s->First(); p; p = s->Next(p)) 
      {
         if (p->Item() == itemSectionInclude && (sec = getSection(p->SectionInclude())))
         {
            for (item = sec->First(); item; item = sec->Next(item))
            {
               if (item->Item() >= itemBegin && item->Item() < itemComplex
                   && (newItem = cThemeItem::newDisplayItem(item->Item())))
               {
                  *newItem = *item;  // copy constructor
                  s->Ins(newItem, p); // s->First());
               }
            }
         }
      }
   }

   int x = 0, xs = 0;

   // init image map
   // init column positions
   // init ...

   // loop over all sections

   for (s = FirstSection(); s; s = NextSection(s)) 
   {
      // loop over items of this section

      x = xs = 0;

      for (p = s->First(); p; p = s->Next(p)) 
      {
         cDisplayItem* dspItem = p;

         if (!dspItem)
            continue;

         // calculate x position of the column items

         if (dspItem->Item() == itemColumn)
         {
            if (dspItem->X() == na)
               dspItem->X(x);                                     // not configured, take calculated
            else if (dspItem->X() < na && x + dspItem->X() > 0)
               dspItem->X(x + dspItem->X());                      // go back
            else
               x = dspItem->X();                                  // take configured value

            if (dspItem->Width())
               x += dspItem->Width() + dspItem->Spacing();        // calc next x
            else 
               dspItem->Width(theTheme->getWidth() - dspItem->X());
         }

         if (dspItem->Item() == itemColumnSelected)
         {
            if (dspItem->X() == na)
               dspItem->X(xs);                                    // not configured, take calculated
            else if (dspItem->X() < na && xs + dspItem->X() > 0)
               dspItem->X(xs + dspItem->X());                     // go back
            else
               xs = dspItem->X();                                 // take configured value
         
            if (dspItem->Width())
               xs += dspItem->Width() + dspItem->Spacing();       // calc next x
            else 
               dspItem->Width(theTheme->getWidth() - dspItem->X());
         }
      }
   }

   // set normal view

   int i = 0;

   while (normalModes[i])
   {
      if (normalModes[i] == GraphTFTSetup.normalMode)
         break;
      
      i++;
   }

   if (!normalModes[i])
      GraphTFTSetup.normalMode = "Standard";

   // at leat reset all items

   for (s = FirstSection(); s; s = NextSection(s)) 
      s->reset();

   initialized = yes;

   return success;
}

//***************************************************************************
// Exit
//***************************************************************************

int cGraphTFTTheme::exit()
{
   int i = 0;
   cThemeSection* s;

   if (!initialized)
      return done;

   tell(3, "destroy theme '%s'", getName().c_str());

   for (s = FirstSection(); s; s = NextSection(s))
      s->getItems()->Clear();
   
   sections.Clear();

   while (normalModes[i] && i < 100)
      free(normalModes[i++]);

   // delete of this object's is done by 
   // cConfig before new Load() or in destructor!

   cThemeItem::resetDefines();

   initialized = no;
   
   return done;
}

//***************************************************************************
// Check
//***************************************************************************

int cGraphTFTTheme::check(const char* theVersion)
{
   // check if the theme fit to current version
      
   if (strcmp(version.c_str(), theVersion) != 0)
   {
      tell(0, "Warning: graphtft - themeversion dosen't match");
      return fail;
   }

   return success;
}

//***************************************************************************
// Load
//***************************************************************************

int cGraphTFTTheme::load(const char* path)
{
   exit();
   Load(path);
   
   return init();
}

//***************************************************************************
// Add Normal Section
//***************************************************************************

void cGraphTFTTheme::addNormalSection(string sectionName)
{
   if (normalModesCount >= 100)
      return ;

   if (sectionName.find("Normal") != 0 || sectionName.length() <= strlen("Normal"))
      return ;

   if (sectionName == "NormalRadio")
      return ;

   if (sectionName == "NormalTV")
      asprintf(&normalModes[normalModesCount], "%s", 
               "Standard");
   else
      asprintf(&normalModes[normalModesCount], "%s", 
               sectionName.substr(strlen("Normal")).c_str());

   normalModesCount++;
}

//***************************************************************************
// Is Normal Section
//***************************************************************************

int cGraphTFTTheme::isNormalMode(const char* modeName)
{
   for (int i = 0; i < normalModesCount; i++)
   {
      if (strcmp(normalModes[i], modeName) == 0)
         return yes;
   }

   return no;
}

string cGraphTFTTheme::getPathFromImageMap(const char* name)
{
   char buf[1000];
   char* c = buf;

   strcpy(buf, name);

   // skip leading blanks and numbers

   while (*c && (isdigit(*c) || *c == ' ')) c++;

   string temp = c;
   string temp2 = "";

   tell(2, "checking imagemap for menu entry '%s'", c);

   for (cDisplayItem* p = mapSection.First(); p; p = mapSection.Next(p)) 
   {
      if (p->Item() == itemMenuImageMap)
      {
         // init translated name at first usage
         
         if (p->Name() == "")
            p->Name(trVDR(p->Path().c_str()));

         // check

         if (p->Name() == temp)
            temp2 = p->Path2();
      }
   }

   if (temp2 != "")
   {
      tell(2, "MenuImageMap: Picture for '%s' is '%s'",
        name, temp2.c_str());
   }

   return temp2;
}

//***************************************************************************
// Calss cGraphTFTThemes
//***************************************************************************
//***************************************************************************
// Get Theme
//***************************************************************************

cGraphTFTTheme* cGraphTFTThemes::getTheme(string aTheme)
{
   cGraphTFTTheme* t;

   tell(3, "looking for theme '%s'", aTheme.c_str());

   for (t = First(); t; t = Next(t))
      if (t->getName() == aTheme)
         return t;

   return 0;
}
