/***************************************************************************
 *                                                                         *
 *   displaybase.h - Base class for rendering a teletext cRenderPage to    *
 *                   an actual VDR OSD.                                    *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   Changelog:                                                            *
 *     2005-03    initial version (c) Udo Richter                          *
 *                                                                         *
 ***************************************************************************/

#ifndef OSDTELETEXT_DISPLAYBASE_H_
#define OSDTELETEXT_DISPLAYBASE_H_

#include "txtrender.h"
#include <vdr/osd.h>

//#define timingdebug
// Enables some time measure debugging code

#ifdef timingdebug
    #include <sys/timeb.h>
    
    class cTime {
        // Debugging: Simple class to measure time
        timeb start;
    public:
        void Start() {
            ftime(&start);
        }
        void Stop(char *txt) {
            timeb t;
            ftime(&t);
            int s=t.time-start.time;
            int ms=t.millitm-start.millitm;
            if (ms<0) {
                s--;
                ms+=1000;
            }
            printf("%s: %i.%03i\n",txt,s,ms);
        }
    };
#endif


class cDisplay : public cRenderPage {
    // Class that extends the virtual cRenderPage with the capability
    // to render its contents to an OSD of variable size.
    // Renders incrementally - just changes
    // plus adds some more display features like message display.
    
public:
    enum enumZoom {
        // Zoom up upper/lower half of page
        Zoom_Off,
        Zoom_Upper,
        Zoom_Lower
    } Zoom;

protected:
    bool Concealed;
    // Hidden text internal state
    
    bool Blinked;
    // Blinking text internal state
    
    int FlushLock;
    // Lock counter for bundeling OSD flushes
    
    bool Boxed;
    // Page is 'boxed mode' transparent
    
    int Width;
    int Height;
    // OSD pixel dimension
    
    tColor Background;
    // Color to be used for black background
    // - allow transparency

    cOsd *osd;
    // The osd object. If creation fails, may be NULL
    
    int ScaleX,ScaleY;
    int OffsetX,OffsetY;
    // Virtual coordinate system, see InitScaler
    
    const cFont *MessageFont;
    int MessageX,MessageY,MessageW,MessageH;
    // Message overlay window, position and font

    class cBox {
        // helper class. Represents a character's box in virtual coordinates
    public:
        int XMin,YMin,XMax,YMax;
        inline void SetToCharacter(int x, int y);
    };
    friend class cBox;
    
    class cVirtualCoordinate {
        // helper class. Represents a coordinate in virtual display space
        // and in OSD pixel coordinates.
    public:
        int OsdX,OsdY;
        int VirtX,VirtY;
        inline void VirtualToPixel(cDisplay *Display, int x, int y);
        inline void IncPixelX(cDisplay *Display);
        inline void IncPixelY(cDisplay *Display);
    };
    friend class cVirtualCoordinate;
    
public:
    cDisplay(int width, int height);
    virtual ~cDisplay();
    bool Valid() { return (osd!=NULL); }
    // After creation, check for Valid(). Destroy, if not valid.
        
protected:  
    void InitScaler();
    // Initialize transformation for OSD->Virtual coordinates
    // Some words about scaling:
    
    // OSD display is variable width x height, with 3 pixels border
    // on all sides. There is a virtual coordinate system projected
    // on this, with (3,3) mapped to (0,0) and (width-3,height-3) 
    // mapped to (480<<16,250<<16).
    // The idea is, that each font pixel uses a virtual rectangle
    // of (1<<16,1<<16) size.
    
    // ScaleX,ScaleY represent the (virtual) width and height of a 
    // physical OSD pixel.
    // OffsetX,OffsetY default to 3,3 to represent the border offset,
    // but may be used differently.

public:
    bool GetBlink() { return Blinked; }
    bool SetBlink(bool blink);
    // Switch blink frequently to get blinking chars
    // Returns true if there are blinking characters.
    
    bool GetConceal() { return Concealed; }
    bool SetConceal(bool conceal);
    // Hidden text. Set to true to see hidden text.
    // Returns true if there are concealed characters.
    
    enumZoom GetZoom() { return Zoom; }
    void SetZoom(enumZoom zoom);
    // Zoom to upper/lower half of page

    void SetBackgroundColor(tColor c);
    tColor GetBackgroundColor() { return Background; }
    // Set the background color for black. Allows transparent black.

    // Color mapping interface.
    virtual tColor GetColorRGB(enumTeletextColor ttc, int Area);
    // Map a teletext color to an OSD color in #Area.

    virtual tColor GetColorRGBAlternate(enumTeletextColor ttc, int Area);
    // For color collision:
    // Map this teletext color to an OSD color in #Area, but dont
    // return same as GetColorRGB(). Used to solve conflicts if
    // foreground and background are mapped to same color.
    // Defaults to 1:1 identity. Not needed if all colors actually 
    // supported by OSD.

    int GetColorIndex(enumTeletextColor ttc, int Area) {
        // Map this teletext color to an OSD color index in #Area.
        if (!osd) return 0;
        cBitmap *bm=osd->GetBitmap(Area);
        if (!bm) return 0;
        return bm->Index(GetColorRGB(ttc,Area));
    }
        
    int GetColorIndexAlternate(enumTeletextColor ttc, int Area) {
        // Map this teletext color to an OSD color index in #Area.
        if (!osd) return 0;
        cBitmap *bm=osd->GetBitmap(Area);
        if (!bm) return 0;
        return bm->Index(GetColorRGBAlternate(ttc,Area));
    }

        
    
protected:
    void InitPalette();
    // Initialize palette(s) for OSD

    void DrawDisplay();
    // Draw all dirty characters from cRenderPage buffer to OSD
    
    void CleanDisplay();
    // Clean OSD completely
    
    virtual void DrawChar(int x, int y, cTeletextChar c);
    // Draw a single character to OSD
    

public:
    void HoldFlush() { FlushLock++; }
    // Hold all OSD flush updates to bundle operations.
    
    void ReleaseFlush() { FlushLock--; Flush(); }
    // Release hold of flush updates. After last release,
    // the flush will be done

protected:
    void Flush() {
        // Commit all changes from OSD internal bitmaps to device
        // All draw operations inside cDisplay should call it,
        // no one outside should need to call it.
        
        if (FlushLock>0) return;
        if (!osd) return;
        if (IsDirty()) DrawDisplay();

        #ifdef timingdebug
            cTime t;
            t.Start();
        #endif

        osd->Flush();

        #ifdef timingdebug
            t.Stop("osd Flush");
        #endif
    }

public: 
    void RenderTeletextCode(unsigned char *PageCode);
    // Interprete teletext code referenced by PageCode
    // and draw the whole page content into OSD.
    // PageCode must be a 40*24+12 bytes buffer

    void DrawText(int x, int y, const char *text, int len);
    // Draw some characters in teletext page.
    // Max len chars, fill up with spaces
    
    void DrawClock();
    // Draw current time to OSD
    
    void DrawPageId(const char *text)   
        { DrawText(0,0,text,8); }
    // Draw Page ID string to OSD   
        
    void DrawMessage(const char *txt);
    // Draw a framed, centered message box to OSD
    
    void ClearMessage();
    // Remove message box and redraw hidden content
};



inline void cDisplay::cBox::SetToCharacter(int x, int y) {
    // Virtual box area of a character
    XMin=(x*12)<<16;
    YMin=(y*10)<<16;
    XMax=XMin+(12<<16)-1;
    YMax=YMin+(10<<16)-1;
}

inline void cDisplay::cVirtualCoordinate::VirtualToPixel(cDisplay *Display, int x, int y) {
    // Map virtual coordinate to OSD pixel  
    OsdX=x/Display->ScaleX+Display->OffsetX;
    OsdY=y/Display->ScaleY+Display->OffsetY;
    
    // map OSD pixel back to virtual coordinate, use center of pixel
    VirtX=(OsdX-Display->OffsetX)*Display->ScaleX+Display->ScaleX/2;
    VirtY=(OsdY-Display->OffsetY)*Display->ScaleY+Display->ScaleY/2;
}

inline void cDisplay::cVirtualCoordinate::IncPixelX(cDisplay *Display) {
    // Move one OSD pixel
    OsdX++;
    VirtX+=Display->ScaleX;
}
inline void cDisplay::cVirtualCoordinate::IncPixelY(cDisplay *Display) {
    // Move one OSD pixel
    OsdY++;
    VirtY+=Display->ScaleY;
}

#endif
