/**
 *  GraphTFT plugin for the Video Disk Recorder 
 * 
 *  theme.h - A plugin for the Video Disk Recorder
 *
 *  (c) 2004 Lars Tegeler, Sascha Volkenandt
 *  (c) 2006-2008 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 <map>
#include <stack>

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

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

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

using std::string;
using std::map;

class cDisplayItem;
class cGraphTFTDisplay;
class cThemeSection;

//***************************************************************************
// Variable Provider
//***************************************************************************

class VariableProvider
{
   public:
      
      VariableProvider() {};
      virtual ~VariableProvider() {};

      virtual string channelLogoPath(const char* channel, 
                                     const char* format = 0, int classic = yes);

      virtual int variableOf(string& name, const char* expression, char*& e);
      virtual int evaluate(string& buf, const char* var);
      virtual const char* splitFormatValue(const char* data, 
                                   char* value, char* format);

      virtual const char* lookupVariable(const char* name, 
                                         const char* fmt = 0) = 0;

};

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

class cThemeService
{
   public:

      enum ReplayMode
      {
         rmUnknown = na,
         rmPlay,
         rmForward,
         rmSpeed
      };

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

      enum ConditionArgumentType
      {
         catUnknown,
         catInteger,
         catString
      };

      enum MenuItemType
      {
         itNormal,
         itPartingLine
      };

      // groups

      enum eThmeGroup
      {
         groupUnknown   = 1,

         groupChannel   = 2,
         groupVolume    = 4,
         groupButton    = 8,
         groupRecording = 16,
         groupReplay    = 32,
         groupMessage   = 64,
         groupMenu      = 128,
         groupMusic     = 256,
         groupTextList  = 512,
         groupCalibrate = 1024,

         groupAll = 0xFFFF
      };

      // items

      enum eThemeItem
      {
         itemUnknown = na,

         itemSectionInclude,                // = 0
         itemTheme,

         itemBegin,                         // = 2
         itemText = itemBegin,              // = 2
         itemImage,                         // = 3
         itemImageFile,
         itemRectangle,                     // = 
         itemTimebar,

         itemSym2ch,
         itemSymDD,
         itemSymVTX,
         itemSymCrypt,
         itemMailSymbol,
         itemMailCount,

         itemMessage,
         itemVolumeMuteSymbol,
         itemVolumebar,

         itemMenu,
         itemMenuSelected,

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

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

         itemMenuImageMap,                   // = 

         itemSpectrumAnalyzer,
         itemPartingLine,                    // = 
         itemSysinfo,
         itemBackground,
         itemTextList,
         itemProgressbar,

         itemClickArea,
         itemMenuNavigationArea,
         itemCalibrationCursor,

         itemColumn,
         itemColumnSelected,

         itemEventColumn,
         itemEventColumnSelected,

         itemDefaults,

         itemCount
      };

      enum Misc
      {
         maxPathCount = 10
      };

      enum Align
      {
         algLeft,
         algCenter, 
         algRight
      };

      enum ScrollModes
      {
         smOff,
         smMarquee,
         smTicker
      };

      struct Translation
      {
         int denum;
         const char* name;
      };

      static int toDenum(Translation t[], const char* value);
      static Translation alignments[];
      static Translation scrollmodes[];

      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 VariableProvider
{  
   public:

      cThemeItem();
      virtual ~cThemeItem();
      
      bool Parse(const char *s);
      int parseDirectives(string& toParse);
      int parseVariable(string& toParse, cThemeSection* section = 0);

      //

      const char* lookupVariable(const char* name, const char* fmt = 0);
      int setVariable(const char* name, int value);

      // statics

      static cDisplayItem* newDisplayItem(int item);


      // temporary buffer used by the parser ::Parse()

      static cThemeSection* currentSection;
      static string lineBuffer;
      static string condition;

   protected:

      // functions

      bool ParseText(string toParse);
      bool ParseVar(string toParse, string name, int* value);
      bool ParseVarTime(string toParse, string name, uint64_t* value);
      bool ParseVar(string toParse, string name, string* value);
      bool ParseVarExt(string toParse, string name, string* value);
      bool ParseVarExt(string toParse, string name, int* value);

      bool ParseDirective(string toParse, string name, string* value);
      bool ParseVar(string toParse, string name, int* value, Translation t[]);

      // item properties

      int _item;
      string _sectionInclude;

      int _x,_y,_width,_height,_red,_green,_blue,_transparent;
      int _lines,_line,_size,_switch;
      string _start_line;
      int _menu_x, _menu_y, _menu_width, _menu_height;
      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, _align_v, _animated, _count, _number;
      uint64_t _delay;
      int _index, _spacing, _bar_height, _bar_height_unit;
      int _scroll, _scroll_count, _dots;
      int _permanent, _factor, _aspect_ratio, _fit, _foreground;
      int _whipe_res;

      string _value;
      string _total;
      string _unit;
      string _reference;
      string _onClick;
      string _onDblClick;
      string _onUp;
      string _onDown;
      string _font;
      string _name;
      string _path;
      string _focus;
      string _path2;
      string _type;
      string _format;
      string _text;
      string _condition;

      int pathCount;
      string pathList[maxPathCount];
      cThemeSection* section;   // my section
};

//***************************************************************************
// 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, int classic = yes);

      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, int clear = yes);
      virtual int drawPartingLine(string text, int y, int height);
     
      virtual void setBackgroundItem(cDisplayItem* p) { backgroundItem = p; }
      virtual void setNextDraw(int aTime = na) 
      { nextDraw = aTime != na ? aTime : cTimeMs::Now(); }
      
      virtual uint64_t getNextDraw()     { return nextDraw; }
      virtual int isForegroundItem()     { return no; }
      uint64_t scheduleDraw(uint64_t aTime);

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

      // statics

      static uint64_t scheduleForce(uint64_t aTime);

      static void clearSelectedItem()             { selectedItem = 0; } 
      static void setForce(int flag)              { forceDraw = flag; }
      static int getForce()                       { return forceDraw; }
      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; }

      // item options

      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 StartLine(int value = na) { return optionVariable(_start_line.c_str(), value); }
      int Line()              { return _line; }
      int Size()              { return _size; }  
      int Switch()            { return _switch; }
      int Align()             { return _align; }
      int AlignV()            { return _align_v; }
      uint64_t Delay()        { return _delay; }
      int Foreground()        { return _foreground; }
      int Count()             { return _count; }
      int Number()            { return _number; }
      int Spacing()           { return _spacing; }
      int BarHeight()         { return _bar_height; }
      int BarHeightUnit()     { return _bar_height_unit; }
      int Fit()               { return _fit; }
      int AspectRatio()       { return _aspect_ratio; }
      int Scroll()            { return _scroll; }
      int MenuX()             { return _menu_x; }
      int MenuY()             { return _menu_y; }
      int MenuWidth()         { return _menu_width; }
      int MenuHeight()        { return _menu_height; }
      int WhipeRes()          { return _whipe_res; }

      string Value()          { return _value; }
      string Total()          { return _total; }
      string Font()           { return _font; }
      string Focus()          { return _focus; }
      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 OnUp()           { return _onUp; }
      string OnDown()         { return _onDown; }
      string Condition()      { return _condition; } 
      string GetPath(int i)   { return i < pathCount ? pathList[i] : ""; }
      string SectionInclude() { return _sectionInclude; }

      // 

      int optionVariable(const char* exp, int value = na)
      { 
         string p;
         string name;
         char* e;

         if (value != na && variableOf(name, exp, e) == success)
            setVariable(name.c_str(), value);

         if (evaluate(p, exp) == success)
            return atoi(p.c_str());

         return 0;
      }

      int evaluateCondition(int recurse = no);
      string evaluatePath();
      const char* lookupVariable(const char* name, const char* fmt = 0);
      int lineCount()                { return actLineCount; }

   protected:

      const char* formatDateTime(time_t time, const char* fmt, char* date, int len, int absolut = no);
      int replayModeValue(ReplayMode rm);
      int condition(int left, int right, const char* op);
      int condition(string* left, string* right, const char* op);

      // data

      uint64_t nextDraw;
      cDisplayItem* backgroundItem;
      int visible;

      // scroll mode

      int marquee_active;
      int marquee_left;
      unsigned int marquee_idx;
      int marquee_count;
      int marquee_strip;
      int animation;
      uint64_t nextAnimationAt;
      string animationImage;
      int actLineCount;
      int lastConditionState;

      // statics

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

class cDisplayText : public cDisplayItem
{
   public:

      cDisplayText() { lastText = ""; }

      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;
         if (strstr(_condition.c_str(), "{volume"))
            group |= groupVolume;
         if (strstr(_condition.c_str(), "{calibration"))
            group |= groupCalibrate;

         return group;
      }

      int draw();

   protected:

      string lastText;
};

class cDisplayTextList : public cDisplayItem
{
   public:

      int groupOf()  
      { 
         int group = groupTextList;

         if (strstr(_text.c_str(), "{music"))
            group |= groupMusic;

         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 groupOf()  { return groupCalibrate; }
      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:

      cDisplayMessage() : cDisplayItem() { visible = no; }
      int groupOf()           { return groupMessage; }
      int isForegroundItem()  { return yes; }

      int draw();
};

class cDisplayMailCount : public cDisplayItem
{
   public:

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

class cDisplayVolumeMuteSymbol : public cDisplayItem
{
   public:

      cDisplayVolumeMuteSymbol() : cDisplayItem() { visible = no; } 
      int groupOf()           { return groupVolume; }
      int isForegroundItem()  { return yes; }

      int draw();
};

class cDisplayVolumebar : public cDisplayItem
{
   public:

      cDisplayVolumebar() : cDisplayItem() { visible = no; } 
      int groupOf()           { return groupVolume; }
      int isForegroundItem()  { return yes; }

      int draw();
};

class cDisplayTimebar : public cDisplayItem
{
   public:

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

class cDisplayProgressBar : public cDisplayItem
{
   public:

      int draw();
};

class cDisplaySymbol : 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 cDisplayMenuEventColumn : public cDisplayItem
{
   public:

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

class cDisplayMenuEventColumnSelected : 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); item->setSection(this); }

      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;
      }

      const char* lookupVar(string name) 
      { 
         map<string,string>::iterator iter = variables.find(name);

         if (iter != variables.end())
            return iter->second.c_str();

         return 0; 
      }

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

      map<string, string> variables;

   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);
      int checkViewMode();

      // get

      string getName()                     { return name; }
      string getThemeVersion()             { return themeVersion; } 
      string getSyntaxVersion()            { return syntaxVersion; }
      string getDir()                      { return dir; }
      string getStartImage()               { return startImage; }
      string getEndImage()                 { return endImage; }
      int getWidth()                       { return width; }
      int getHeight()                      { return height; }
      string getFontPath()                 { return fontPath; }
      int isInitialized()                  { return initialized; }

      // set

      void setName(string aValue)          { name = aValue; }

      void setThemeVersion(string aValue)  { themeVersion = aValue; }
      void setSyntaxVersion(string aValue) { syntaxVersion = 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 setFontPath(string aValue)      { fontPath = aValue; }

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

      // 

      void addNormalSection(string section);
      int isNormalMode(const char* modeName);
      const char* nextNormalMode(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);

      void resetDefines() { defineCount = 0; clearIfdefs();}
      void clearIfdefs()  { while (!skipContent.empty()) skipContent.pop(); }
      int isSkipContent() { return !skipContent.empty() && skipContent.top(); }

      const char* lookupVar(string name) 
      { 
         map<string,string>::iterator iter;
         
         iter = menuVariables.find(name);

         if (iter != menuVariables.end())
            return iter->second.c_str();            

         iter = variables.find(name);

         if (iter != variables.end())
            return iter->second.c_str();            

         return 0; 
      }

      // data

      string defines[100];
      int defineCount;
      std::stack<int> skipContent;

      map<string, string> variables;
      map<string, string> menuVariables;

   protected:

      // data

      int width;
      int height;
      string dir;
      string name;
      string themeVersion;
      string syntaxVersion;
      string startImage;
      string endImage;
      string fontPath;
      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);

      static cGraphTFTTheme* theTheme;

   protected:
};

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

typedef cGraphTFTThemes Thms;

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

extern cGraphTFTThemes themes;

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