/**
 *  GraphTFT plugin for the Video Disk Recorder 
 * 
 *  theme.h - 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.h,v 1.13 2007/12/03 19:58:20 root Exp $
 *
 **/

#ifndef __GTFT_THEME_H
#define __GTFT_THEME_H

#include <string>

#include <vdr/config.h>
#include <vdr/recording.h>

#include <common.h>
#include <renderer.h>

#define SECONDS(x) (((uint64_t)x)*1000)

using std::string;

class cDisplayItem;
class cGraphTFTDisplay;
class cThemeSection;

//***************************************************************************
// Token Scanner
//***************************************************************************

class Scan
{
   public:

      Scan(const char* buf)    { asprintf(&buffer, buf); reset(); }
      ~Scan()                  { free(buffer); }

      void reset()             { p = buffer; }
      const char* lastString() { return _last; }
      int lastInt()            { return atoi(_last); }

      int isEmpty()            { return _last[0] == 0; }
      int isString()           { return isString(*_last); }
      int isNum()              { return isNum(*_last); }
      int isOperator()         { return isOperator(*_last); }

      int eat()
      { 
         int i = 0;

         skipWs();

         while (p && !isDelimiter(*p))
         {
            if (i)
            {
               if (isOperator(*_last) && !isOperator(*p))
                  break;

               if (isNum(*_last) && !isNum(*p))
                  break;

               if (isString(*_last) && !isString(*p))
                  break;
            }
            
            _last[i++] = *p;

            p++;

            if (i == 100)
               break;
         }

         _last[i] = 0;

         return i == 0 ? fail : success;
      }

   private:

      void skipWs()            { while (p && isWhiteSpace(*p)) p++; }

      int isDelimiter(char c)  { return strchr(delimiters, c) != 0; }
      int isWhiteSpace(char c) { return strchr(whitespace, c) != 0; }
      int isString(char c)     { return isalpha(c); }
      int isNum(char c)        { return isdigit(c); }
      int isOperator(char c)   { return strchr(operators, c) != 0; }

      // data

      const char* p;
      char* buffer;
      char _last[100+TB];

      static const char* delimiters;
      static const char* operators;
      static const char* whitespace;
};

//***************************************************************************
// Theme Service
//***************************************************************************

class cThemeService
{
   public:

      enum ItemUnit
      {
         iuUnknown = na,
         iuAbsolute,
         iuRelative,
         iuPercent
      };

      enum MenuItemType
      {
         itNormal,
         itPartingLine
      };

      // groups

      enum eThmeGroup
      {
         groupUnknown   = 1,

         groupChannel   = 2,
         groupVolume    = 4,
         groupButton    = 8,
         groupRecording = 16,
         groupReplay    = 32,
         groupMessage   = 64,
         groupMenu      = 128
      };

      // items

      enum eThemeItem
      {
         itemUnknown = na,

         itemSectionInclude,                // = 0
         itemTheme,

         itemBegin,                         // = 2
         itemText = itemBegin,              // = 2
         itemImage,                         // = 3
         itemImageFile,
         itemChannel,
         itemChannelOnly,                   // = 
         itemChannelNumber,
         itemRectangle,                     // = 
         itemLogo,
         itemTimebar,

         itemRecording,
         itemSymRecording,
         itemSym2ch,
         itemSymDD,
         itemSymVTX,
         itemSymCrypt,
         itemMailSymbol,
         itemMailCount,

         itemReplaySymbolPlay,
         itemReplaySymbolPause,             // = 
         itemReplaySymbolFF,
         itemReplaySymbolFB,                // = 
         itemReplaySymbolSF,
         itemReplaySymbolSB,                // = 

         itemMessage,
         itemVolumeMuteSymbol,
         itemVolumebar,

         itemReplayName,                    // = 
         itemReplayProgressbar,             // = 
         itemReplayTotal,
         itemReplayCurrent,
         itemReplayRemaining,               // = 

         itemMenuTitle,
         itemMenu,
         itemMenuSelected,
         itemMenuStaticPicture,
         itemMenuStaticX,                   // = 
         itemMenuStaticY,                   // = 

         itemMenuButton,
         itemMenuButtonRed = itemMenuButton, // = 
         itemMenuButtonGreen,
         itemMenuButtonYellow,
         itemMenuButtonBlue,

         itemMenuButtonBackground,           // = 
         itemMenuButtonBackgroundRed = itemMenuButtonBackground,  // = 
         itemMenuButtonBackgroundGreen,
         itemMenuButtonBackgroundYellow,
         itemMenuButtonBackgroundBlue,

         itemMenuText,                       // = 
         itemMenuTextBackground,
         itemMenuImageMap,                   // = 
         itemSpectrumAnalyzer,
         itemPartingLine,                    // = 
         itemSysinfo,
         itemBackground,

         itemClickArea,
         itemMenuNavigationArea,
         itemCalibrationCursor,

         itemComplex,                        // = 
         itemColumn = itemComplex,
         itemColumnSelected,

         itemDefaults,

         itemCount
      };

      enum Misc
      {
         maxPathCount = 10
      };

      static const char* toName(eThemeItem aItem);
      static eThemeItem toItem(const char* aName);
      static int isValid(eThemeItem aItem)
         { return aItem > itemUnknown && aItem < itemCount; }

      static const char* items[itemCount+1];
};

typedef cThemeService Ts;

//***************************************************************************
// Class cThemeItem
//  - represents a theme item
//***************************************************************************

class cThemeItem : public cListObject, public cThemeService
{  
   public:

      cThemeItem();
      virtual ~cThemeItem();
      
      bool Parse(const char *s);
      int parseDirectives(string& toParse);

      // statics

      static cDisplayItem* newDisplayItem(int item);
      static void resetDefines() { defineCount = 0; skipContent = no; pendingIfs = 0; }

   protected:

      // functions

      bool ParseText(string toParse);
      bool ParseVar(string toParse, string name, int* value);
      bool ParseVar(string toParse, string name, string* value);
      bool ParseDirective(string toParse, string name, string* value);

      // item properties

      int _item;
      string _sectionInclude;

      int _x,_y,_width,_height,_red,_green,_blue,_transparent,_lines,_size,_switch;
      int _bg_x,_bg_y,_bg_width,_bg_height;
      int _bg_red,_bg_green,_bg_blue,_bg_transparent;
      int _stat_pic,_stat_x,_stat_y, _image_map;
      int _stat_text, _stat_width, _stat_height;
      int _align, _animated, _delay, _count, _number;
      int _index, _spacing, _bar_height, _bar_height_unit;
      int _scroll, _scroll_count, _dots;
      int _permanent, _factor, _aspect_ratio, _fit;

      string _unit;
      string _reference;
      string _onClick;
      string _onDblClick;
      string _font;
      string _name;
      string _path;
      string _path2;
      string _type;
      string _format;
      string _text;
      string _condition;

      int pathCount;
      string pathList[maxPathCount];

      static string defines[100];
      static int defineCount;
      static int skipContent;
      static int pendingIfs;
};

//***************************************************************************
// Display Items
//***************************************************************************

class cDisplayItem : public cThemeItem
{
   public:

      cDisplayItem();
      virtual ~cDisplayItem();

      // functions

      virtual const char* nameOf()  { return toName((eThemeItem)_item); }
      virtual int groupOf()         { return groupUnknown; }

      virtual int clear();
      virtual int draw();
      virtual int refresh();
      virtual int reset();

      virtual string channelLogoPath(const char* channel);

      virtual int drawText(const char* text, int y = 0, 
                           int height = 0, int clear = yes, int skipLines = 0);
      virtual int drawRectangle();
      virtual int drawBackRect(int y = 0, int height = 0);
      virtual int drawImage(const char* path = 0, int fit = na, 
                            int aspectRatio = na);
      virtual int drawImageOnBack(const char* path = 0, int fit = no);
      virtual int drawProgressBar(double current, double total, 
                                  string path, int y = na, 
                                  int height = na, int withFrame = yes);
      virtual int drawPartingLine(string text, int y, int height);
     
      virtual void setNextDraw(int aTime = na) 
      { nextDraw = aTime != na ? aTime : cTimeMs::Now(); }
      
      virtual uint64_t getNextDraw()     { return nextDraw; }
      virtual int isForegroundItem()     { return no; }

      void setSection(cThemeSection* s)  { section = s; } 


      // statics

      static uint64_t scheduleForce(uint64_t aTime);

      static void setForce(int flag)              { forceDraw = flag; }
      static void setRenderer(Renderer* aRender)  { render = aRender; }
      static void setVdrStatus(cGraphTFTDisplay* aVdrStatus) { vdrStatus = aVdrStatus; }

      // Item getter/setter

      int Item()                { return _item; }
      int Index(int value = na) { if (value != na) _index = value; return _index; }
      int X(int value = na)     { if (value != na) _x = value; return _x; }
      int Width(int value = na) { if (value != na) _width = value; return _width; }
      string Name(const char* value = 0) { if (value) _name = value; return _name; }

      int BgX()               { return _bg_x; }
      int Y()                 { return _y; }
      int BgY()               { return _bg_y; }
      int ImageMap()          { return _image_map; }
      int StaticPicture()     { return _stat_pic; }
      int StaticText()        { return _stat_text; }
      int StaticWidth()       { return _stat_width; }
      int StaticHeight()      { return _stat_height; }
      int StaticX()           { return _stat_x; }
      int StaticY()           { return _stat_y; }
      int BgWidth()           { return _bg_width; }
      int Height()            { return _height; }
      int BgHeight()          { return _bg_height; }
      int Red()               { return _red; }
      int BgRed()             { return _bg_red; }
      int Green()             { return _green; }
      int BgGreen()           { return _bg_green; }
      int Blue()              { return _blue; }
      int BgBlue()            { return _bg_blue; }
      int Transparent()       { return _transparent; }
      int BgTransparent()     { return _bg_transparent; }
      int Lines()             { return _lines; }
      int Size()              { return _size; }  
      int Switch()            { return _switch; }
      int Align()             { return _align; }
      int Animated()          { return _animated; }
      int Delay()             { return _delay; }
      int Count()             { return _count; }
      int Number()            { return _number; }
      int Spacing()           { return _spacing; }
      int BarHeight()         { return _bar_height; }
      int BarHeightUnit()     { return _bar_height_unit; }
      int Scroll()            { return _scroll; }
      string Font()           { return _font; }
      string Path()           { return _path; }
      string Path2()          { return _path2; }
      string Type()           { return _type; }
      string Format()         { return _format; }
      string Text()           { return _text; }
      string OnClick()        { return _onClick; }
      string OnDblClick()     { return _onDblClick; }
      string Condition()      { return _condition; } 
      string GetPath(int i)   { return i < pathCount ? pathList[i] : ""; }

      string SectionInclude() { return _sectionInclude; }

      int evaluateCondition(const char* cond, int value);

   protected:

      const char* formatDateTime(time_t time, const char* fmt, char* date, int len);

      const char* evaluateExpression(const char* value);
      int evaluate(string& buf, const char* var);

      // data

      uint64_t nextDraw;
      cThemeSection* section;   // my section

      // scroll mode

      int marquee_active;
      int marquee_left;
      unsigned int marquee_idx;
      int marquee_count;
      int marquee_strip;
      
      // statics

      static int forceDraw;
      static Renderer* render;
      static cGraphTFTDisplay* vdrStatus;
      static cDisplayItem* selectedItem;
      static uint64_t nextForce;
};

class cDisplayText : public cDisplayItem
{
   public:

      int groupOf()  
      { 
         int group = groupUnknown;

         if (strstr(_text.c_str(), "{replay"))
            group |= groupReplay;
         if (strstr(_text.c_str(), "{recording"))
            group |=  groupRecording;
         if (strstr(_text.c_str(), "{event"))
            group |=  groupChannel;
         if (strstr(_text.c_str(), "{present"))
            group |=  groupChannel;
         if (strstr(_text.c_str(), "{following"))
            group |=  groupChannel;

         return group;
      }

      int draw();
};

class cDisplayRectangle : public cDisplayItem
{
   public:

      int draw();
};

class cDisplayImage : public cDisplayItem
{
   public:

      int groupOf()  
      { 
         int group = groupUnknown;

         if (strstr(_path.c_str(), "{replay"))
            group |= groupReplay;
         if (strstr(_path.c_str(), "{recording"))
            group |=  groupRecording;
         if (strstr(_path.c_str(), "{event"))
            group |=  groupChannel;
         if (strstr(_path.c_str(), "{present"))
            group |=  groupChannel;
         if (strstr(_path.c_str(), "{following"))
            group |=  groupChannel;

         return group;
      }

      int draw();
};

class cDisplayBackground : public cDisplayImage
{
   public:

      int groupOf()         { return groupUnknown; }
      int draw();
};

class cDisplayImageFile : public cDisplayItem
{
   public:

      int groupOf()  { return groupReplay; }
      int draw();
};

class cDisplayCalibrationCursor : public cDisplayItem
{
   public:

      int draw();
};

class cDisplayMenuButton : public cDisplayItem
{
   public:

      int groupOf()  { return groupButton; }
      int draw();
};

class cDisplayMenuButtonBackground : public cDisplayItem
{
   public:

      int groupOf()  { return groupButton; }
      int draw();
};

class cDisplayMessage : public cDisplayItem
{
   public:

      int groupOf()           { return groupMessage; }
      int isForegroundItem()  { return yes; }

      int draw();
};

class cDisplaySymRecording : public cDisplayItem
{
   public:

      int groupOf()  { return groupRecording; }
      int draw();
};

class cDisplayRecording : public cDisplayItem
{
   public:

      int groupOf()  { return groupRecording; }
      int draw();
};

class cDisplayMailCount : public cDisplayItem
{
   public:

      int groupOf()  { return groupChannel; }
      int draw();
};

class cDisplayMenuTitle : public cDisplayItem
{
   public:

      int draw();
};

class cDisplayMenuText : public cDisplayItem
{
   public:

      int draw();
};

class cDisplayMenuTextBackground : public cDisplayItem
{
   public:

      int draw();
};

class cDisplayChannelName : public cDisplayItem
{
   public:

      int groupOf()  { return groupChannel; }
      int draw();
};

class cDisplayChannelOnly : public cDisplayItem
{
   public:

      int groupOf()  { return groupChannel; }
      int draw();
};

class cDisplayChannelNumber : public cDisplayItem
{
   public:

      int groupOf()  { return groupChannel; }
      int draw();
};

class cDisplayVolumeMuteSymbol : public cDisplayItem
{
   public:

      int groupOf()           { return groupVolume; }
      int isForegroundItem()  { return yes; }

      int draw();
};

class cDisplayVolumebar : public cDisplayItem
{
   public:

      int groupOf()           { return groupVolume; }
      int isForegroundItem()  { return yes; }

      int draw();
};

class cDisplayTimebar : public cDisplayItem
{
   public:

      int groupOf()  { return groupChannel; }
      int draw();
};

class cDisplayReplayProgressBar : public cDisplayItem
{
   public:

      int draw();
};

class cDisplayReplayName : public cDisplayItem
{
   public:

      int groupOf()  { return groupReplay; }
      int draw();
};

class cDisplayReplayCurrent : public cDisplayItem
{
   public:

      int groupOf()  { return groupReplay; }
      int draw();
};

class cDisplayReplayTotal : public cDisplayItem
{
   public:

      int groupOf()  { return groupReplay; }
      int draw();
};

class cDisplayReplayRemaining : public cDisplayItem
{
   public:

      int groupOf()  { return groupReplay; }
      int draw();
};

class cDisplaySymbol : public cDisplayItem
{
   public:

      int groupOf()  { return groupChannel; }
      int draw();
};

class cDisplayLogo : public cDisplayItem
{
   public:

      int groupOf()  { return groupChannel; }
      int draw();
};

class cDisplayMenu : public cDisplayItem
{
   public:

      int draw();
};

class cDisplayMenuSelected : public cDisplayItem
{
   public:

      int draw();
};

class cDisplayMenuColumn : public cDisplayItem
{
   public:

      int groupOf()  { return groupMenu; }
      int draw();
};

class cDisplayMenuColumnSelected : public cDisplayItem
{
   public:

      int groupOf()  { return groupMenu; }
      int draw();
};

class cDisplayPartingLine : public cDisplayItem
{
   public:

      int draw() { return cDisplayItem::draw(); }
};

class cDisplaySpectrumAnalyzer : public cDisplayItem
{
   public:

      int draw();
};

class cDisplaySysinfo : public cDisplayItem
{
   public:

      int draw();

   protected:

      int lastCpuLoad;
      unsigned long lastUsedMem;
      unsigned long lastUsedDisk;
};

//***************************************************************************
// Lists
//***************************************************************************

//***************************************************************************
// Class cThemeItems
//***************************************************************************

class cDisplayItems : public cList<cDisplayItem>
{
   public:

      cDisplayItem* getItem(int id);

   protected:

};

//***************************************************************************
// Class cThemeSection
//  - represents a section of a theme
//***************************************************************************

class cThemeSection : public cListObject, cThemeService
{
   public:

      cThemeSection(string aName = "unnamed")   { name = aName; defaultsItem = 0; }
      ~cThemeSection()                          { if (defaultsItem) delete defaultsItem; }

      string getName()                          { return name; }
      cDisplayItems* getItems()                 { return &items; }

      cDisplayItem* getItem(int itemId)         { return items.getItem(itemId); }

      cDisplayItem* First()                     { return items.First(); }
      cDisplayItem* Next(cDisplayItem* item)    { return items.Next(item); }

      void Ins(cDisplayItem* item, cDisplayItem* before = 0) 
      { items.Ins(item, before); }

      void Add(cDisplayItem* item, cDisplayItem* after = 0)  
      { items.Add(item, after); }

      cDisplayItem* getDefaultsItem()           { return defaultsItem; }

      void setDefaultsItem(cDisplayItem* item)  
      {  
         if (defaultsItem) 
            delete defaultsItem;

         defaultsItem = item;
      }

      uint64_t getNextUpdateTime();
      int reset();
      int updateGroup(int group);

   protected:

      string name;
      cDisplayItems items;

      cDisplayItem* defaultsItem;  // Item holding default values
};

//***************************************************************************
// Class cThemeSections
//    - holding all sections
//***************************************************************************

class cThemeSections : public cList<cThemeSection>
{
   public:

      cThemeSection* getSection(string name);

   protected:

};

//***************************************************************************
// Class cGraphTFTTheme
//   - holding all Items included in the theme configuration file
//***************************************************************************

class cGraphTFTTheme : public cConfig<cThemeItem>, public cListObject, cThemeService
{
   public:

      cGraphTFTTheme();
      virtual ~cGraphTFTTheme()   { exit(); }

      // functions

      int init();
      int exit();
      int check(const char* theVersion);
      int load(const char* path);

      // get

      string getName()                    { return name; }
      string getVersion()                 { return version; }
      string getDir()                     { return dir; }
      string getStartImage()              { return startImage; }
      string getEndImage()                { return endImage; }
      int getWidth()                      { return width; }
      int getHeight()                     { return height; }
      int isInitialized()                 { return initialized; }

      // set

      void setName(string aValue)         { name = aValue; }
      void setVersion(string aValue)      { version = aValue; }
      void setDir(string aValue)          { dir = aValue; }
      void setStartImage(string aValue)   { startImage = aValue; }
      void setEndImage(string aValue)     { endImage = aValue; }
      void setWidth(int aValue)           { width = aValue; }
      void setHeight(int aValue)          { height = aValue; }

      void AddMapItem(cDisplayItem* item, cDisplayItem* after = 0)
      { mapSection.Add(item, after); }

      // 

      void addNormalSection(string section);
      int isNormalMode(const char* modeName);
      char** getNormalSections()          { return normalModes; }
      int getNormalSectionsCount()        { return normalModesCount; }

      // the sections

      cThemeSection*  getSection(string name) { return sections.getSection(name); }
      cThemeSections* getSections()           { return &sections; }

      cThemeSection* FirstSection()              
      { return sections.First(); }

      cThemeSection* NextSection(cThemeSection* sect)
      { return sections.Next(sect); }

      string getPathFromImageMap(const char* name);

   protected:

      // data

      int width;
      int height;
      string dir;
      string name;
      string version;
      string startImage;
      string endImage;
      int initialized;
      cThemeSections sections;
      cThemeSection mapSection;
      char* normalModes[100];
      int normalModesCount;
};

//***************************************************************************
// Class cGraphTFTThemes
//    - holding all themes
//***************************************************************************

class cGraphTFTThemes : public cList<cGraphTFTTheme>
{
   public:

      cGraphTFTTheme* getTheme(string aTheme);

   protected:

};

//***************************************************************************
// External
//***************************************************************************

extern cGraphTFTTheme* theTheme;
extern cGraphTFTThemes themes;

//***************************************************************************
#endif //__GTFT_THEME_H
