/* This Demo shows the Developer how to use dithern colors.
 * You can define how many base colors should used.
 * The number of the base colors are definde by
 * static const int COLORS = 8;
 * You can use 2 upto MAXCOLORS Colors.
 * More then 64 are untestetd and will only work with the xine or softdevice Plugin
 * The first 16 Colors are defined by static values. More the 16 colors will be randomize colors.
 * 
 * OK will toggel to curses/dithern mode.
 * If you are in the DithernMode, you can change the BaseColors by pressing the Keys 1..9
 * If you are in the CourseMode you can change the Colors by pressing 1 and 2, 
 * left, right will change the width, up/down the high of the Courses.
 * 
 * Back will exit the Plugin.
 * 
 * If you have some questions please ask me at Patrick_Fischer@gmx.de
 * 
 */
#include <vdr/plugin.h>
#include <vdr/osd.h>
#include <math.h>

static const char *VERSION = "0.0.3";
static const char *DESCRIPTION = "Only a Demo how dithernColors could look like";
static const char *MAINMENUENTRY = "Ditherndemo";

static const int OSDWidth = 500 & ~0x07;
static const int OSDHigh = 300;
static const int MAXCOLORS = 32;// number of defined colors, tested with 64
static const int COLORS = 8;    // Colors to use 2...64 (64 only work in xine- or softdevice-plugin)

//Set this line meens that this Plugin use the dithernPatch for VDR
//commit it out meens that this Plugin use his own dithern algo. It is much slower.
//#define USEVDRWITHPATCH


class cDithernOsd:public cOsdObject {
  private:
    cOsd * osd;
    tColor colors[MAXCOLORS];
    int color[COLORS];
    cPalette *palette;
    int dithernstate;
    int courseswidth;
    int courseshigh;

    void DrawCourse(int x1, int y1, int x2, int y2, tColor Color, tColor Color2);
    void DrawRectangle(int x1, int y1, int x2, int y2, tColor Color, tColor Color2);
  public:
        cDithernOsd();
        virtual ~ cDithernOsd();
    virtual void Show(void);
    virtual eOSState ProcessKey(eKeys Key);
    void PaintColors();
    void PaintCourses();

};

cDithernOsd::cDithernOsd()
{
    dithernstate = 0;
    courseswidth = OSDWidth;
    courseshigh = OSDHigh / 3;

    int needbpp = (int)ceil(log(COLORS) / log(2));
    int usebpp;
    if (needbpp <= 2)
        usebpp = 2;
    else if (needbpp <= 4)
        usebpp = 4;
    else if (needbpp <= 8)
        usebpp = 8;
    else
        usebpp = 16;
    dsyslog("%d Colors need %dbpp but we will use %dbpp", COLORS, needbpp, usebpp);

    dsyslog("create a new cPalette with %d Colorsdepth", usebpp);
    palette = new cPalette(usebpp);

    osd = NULL;

    dsyslog("init colorslist...");
    colors[0] = 0x7F000000;
    colors[1] = clrRed;
    colors[2] = clrGreen;
    colors[3] = clrBlue;
    colors[4] = clrYellow;
    colors[5] = clrBlack;
    colors[6] = clrMagenta;
    colors[7] = clrCyan;
    colors[8] = clrTransparent;
    colors[9] = clrGray50;
    colors[10] = clrWhite;
    colors[11] = 0xFFFF0000;
    colors[12] = 0xFF00FF00;
    colors[13] = 0xFF0000FF;
    colors[14] = 0x7FFF0000;
    colors[15] = 0x7F00FF00;
    colors[16] = 0x7F0000FF;
    for (int a = 17; a < MAXCOLORS; a++) {
        colors[a] = rand() * 0xFFFFFFFF;
    }

    dsyslog("init default colors and palette.");
    for (int i = 0; i < COLORS; i++) {
        color[i] = i;
        palette->SetColor(i, colors[i]);
    }
}

cDithernOsd::~cDithernOsd()
{
    dsyslog("destroy osd and palette");
    if (osd)
        delete osd;
    if (palette)
        delete palette;
}

void cDithernOsd::Show(void)
{
    dsyslog("create OSD and set Areas");
    osd = cOsdProvider::NewOsd(100, 100);
    if (osd) {
        int bpp = palette->Bpp();
        dsyslog("need %d bytes per Pixel(only 2,4,8 are posible?)", bpp);
        tArea Area = { 0, 0, OSDWidth - 1, OSDHigh - 1, bpp };
        osd->SetAreas(&Area, 1);
    }
    PaintColors();
}

eOSState cDithernOsd::ProcessKey(eKeys Key)
{
    eOSState state = cOsdObject::ProcessKey(Key);
    int k;
    if (state == osUnknown) {
        switch (Key & ~k_Repeat) {
        case k1...k9:
            k = (Key & ~k_Repeat) - 12;
            if (k < COLORS) {
                color[k] = ++color[k] % MAXCOLORS;
                dsyslog("Change Color Nummber %d to 0x%x", k, colors[color[k]]);
                palette->SetColor(k, colors[color[k]]);
            }
            break;
        case kLeft:
            if (courseswidth > 10)
                courseswidth = courseswidth - 10;
            break;
        case kRight:
            if (courseswidth < OSDWidth - 10)
                courseswidth = courseswidth + 10;
            break;
        case kUp:
            if (courseshigh < OSDHigh / 3 - 10)
                courseshigh = courseshigh + 2;
            break;
        case kDown:
            if (courseshigh > 10)
                courseshigh = courseshigh - 2;
            break;
        case kOk:
            dithernstate = dithernstate ? 0 : 1;
            break;
        case kBack:
            return osEnd;
        default:
            return state;
        }
        state = osContinue;
    }
    if (dithernstate)
        PaintCourses();
    else
        PaintColors();

    return state;
}


/*
 * My DrawRectangel
 * 	This is a Slow Version. Just a Demo
 * 
 * Base Code is the 
 * 	XOR with mod2
 */
void cDithernOsd::DrawRectangle(int x1, int y1, int x2, int y2, tColor Color, tColor Color2)
{
    for (int y = y1; y <= y2; y++) {
        for (int x = x1; x <= x2; x++) {
            if (((x % 2) && (y % 2)) || (!(x % 2) && !(y % 2)))
                osd->DrawPixel(x, y, Color);
            else
                osd->DrawPixel(x, y, Color2);
        }
    }
}


void cDithernOsd::PaintColors()
{
    //Workaround for EM84xx
    if (osd)
        delete osd;
    dsyslog("create OSD and set Areas");
    osd = cOsdProvider::NewOsd(100, 100);
    if (osd) {
        int bpp = palette->Bpp();
        dsyslog("need %d bytes per Pixel(only 2,4,8 are posible?)", bpp);
        tArea Area = { 0, 0, OSDWidth - 1, OSDHigh - 1, bpp };
        osd->SetAreas(&Area, 1);
    }
    //End Workaround

    if (osd) {
        osd->GetBitmap(0)->Replace(*palette);

        //Backgroundcolor
        //osd->DrawRectangle(0, 0, OSDWidth-1, OSDHigh-1, palette->Color(0));

        for (int i = 0; i < COLORS; i++) {
            //the first Line shows the Base Colors
            osd->DrawRectangle(int (OSDWidth * i / COLORS), 0, int (OSDWidth * (i + 1) / COLORS), OSDHigh / (COLORS + 1), palette->Color(i));
        }

        for (int a = 0; a < COLORS; a++) {
            for (int b = 0; b < COLORS; b++) {
                //show all colors mixed with each color
                //call my own DrawRectangel
                int x1 = int (OSDWidth * b / COLORS);
                int y1 = OSDHigh / (COLORS + 1) * (a + 1);
                int x2 = int (OSDWidth * (b + 1) / COLORS);
                int y2 = OSDHigh / (COLORS + 1) * (a + 2);

#ifdef USEVDRWITHPATCH
                osd->DrawRectangle(x1, y1, x2, y2, palette->Color(a), palette->Color(b));
#else
                DrawRectangle(x1, y1, x2, y2, palette->Color(a), palette->Color(b));
#endif
            }
        }
        osd->Flush();
    } else
        dsyslog("OSD is to big:-)");
}

void cDithernOsd::PaintCourses()
{
	//Workaround for EM84xx
    if (osd)
        delete osd;
    dsyslog("create OSD and set Areas");
    osd = cOsdProvider::NewOsd(100, 100);
    if (osd) {
        int bpp = palette->Bpp();
        dsyslog("need %d bytes per Pixel(only 2,4,8 are posible?)", bpp);
        tArea Area = { 0, 0, OSDWidth - 1, OSDHigh - 1, 2 };
        osd->SetAreas(&Area, 1);
    }
    //END Workaround EM84xx
    
    //Backgroundcolor
    osd->DrawRectangle(0, 0, OSDWidth - 1, OSDHigh - 1, 0x00000000);

	//from left to right
    DrawCourse(0, 0, courseswidth, courseshigh, palette->Color(0), palette->Color(1));

	//from left and rigth to center
    DrawCourse(0, courseshigh, courseswidth / 2, courseshigh * 2, palette->Color(1), palette->Color(0));
    DrawCourse(courseswidth / 2, courseshigh, courseswidth, courseshigh * 2, palette->Color(0), palette->Color(1));

	//From rigth to left
    DrawCourse(0, courseshigh * 2, courseswidth, courseshigh * 3, palette->Color(1), palette->Color(0));
    osd->Flush();

}

void cDithernOsd::DrawCourse(int x1, int y1, int x2, int y2, tColor Color, tColor Color2)
{
    int difx = (x2 - x1) / 3;
    int dify = (y2 - y1) / 3;
    for (int i = 0; i < difx; i++) {
        for (int j = 0; j < dify; j++) {
            int x = x1 + i * 3;
            int y = y1 + j * 3;
            int val = 10 * i / difx;
            switch (val) {
            case 0:
                osd->DrawPixel(x - 1, y - 1, Color);
                osd->DrawPixel(x, y - 1, Color);
                osd->DrawPixel(x + 1, y - 1, Color);
                osd->DrawPixel(x - 1, y, Color);
                osd->DrawPixel(x, y, Color);
                osd->DrawPixel(x + 1, y, Color);
                osd->DrawPixel(x - 1, y + 1, Color);
                osd->DrawPixel(x, y + 1, Color);
                osd->DrawPixel(x + 1, y + 1, Color);
                break;
            case 1:
                osd->DrawPixel(x - 1, y - 1, Color);
                osd->DrawPixel(x, y - 1, Color);
                osd->DrawPixel(x + 1, y - 1, Color);
                osd->DrawPixel(x - 1, y, Color);
                osd->DrawPixel(x, y, Color2);
                osd->DrawPixel(x + 1, y, Color);
                osd->DrawPixel(x - 1, y + 1, Color);
                osd->DrawPixel(x, y + 1, Color);
                osd->DrawPixel(x + 1, y + 1, Color);
                break;
            case 2:
                osd->DrawPixel(x - 1, y - 1, Color);
                osd->DrawPixel(x, y - 1, Color2);
                osd->DrawPixel(x + 1, y - 1, Color);
                osd->DrawPixel(x - 1, y, Color);
                osd->DrawPixel(x, y, Color2);
                osd->DrawPixel(x + 1, y, Color);
                osd->DrawPixel(x - 1, y + 1, Color);
                osd->DrawPixel(x, y + 1, Color);
                osd->DrawPixel(x + 1, y + 1, Color);
                break;
            case 3:
                osd->DrawPixel(x - 1, y - 1, Color);
                osd->DrawPixel(x, y - 1, Color2);
                osd->DrawPixel(x + 1, y - 1, Color);
                osd->DrawPixel(x - 1, y, Color2);
                osd->DrawPixel(x, y, Color2);
                osd->DrawPixel(x + 1, y, Color);
                osd->DrawPixel(x - 1, y + 1, Color);
                osd->DrawPixel(x, y + 1, Color);
                osd->DrawPixel(x + 1, y + 1, Color);
                break;
            case 4:
                osd->DrawPixel(x - 1, y - 1, Color);
                osd->DrawPixel(x, y - 1, Color2);
                osd->DrawPixel(x + 1, y - 1, Color);
                osd->DrawPixel(x - 1, y, Color2);
                osd->DrawPixel(x, y, Color2);
                osd->DrawPixel(x + 1, y, Color2);
                osd->DrawPixel(x - 1, y + 1, Color);
                osd->DrawPixel(x, y + 1, Color);
                osd->DrawPixel(x + 1, y + 1, Color);
                break;
            case 5:
                osd->DrawPixel(x - 1, y - 1, Color);
                osd->DrawPixel(x, y - 1, Color2);
                osd->DrawPixel(x + 1, y - 1, Color);
                osd->DrawPixel(x - 1, y, Color2);
                osd->DrawPixel(x, y, Color2);
                osd->DrawPixel(x + 1, y, Color2);
                osd->DrawPixel(x - 1, y + 1, Color);
                osd->DrawPixel(x, y + 1, Color2);
                osd->DrawPixel(x + 1, y + 1, Color);
                break;
            case 6:
                osd->DrawPixel(x - 1, y - 1, Color);
                osd->DrawPixel(x, y - 1, Color2);
                osd->DrawPixel(x + 1, y - 1, Color2);
                osd->DrawPixel(x - 1, y, Color2);
                osd->DrawPixel(x, y, Color2);
                osd->DrawPixel(x + 1, y, Color2);
                osd->DrawPixel(x - 1, y + 1, Color);
                osd->DrawPixel(x, y + 1, Color2);
                osd->DrawPixel(x + 1, y + 1, Color);
                break;
            case 7:
                osd->DrawPixel(x - 1, y - 1, Color);
                osd->DrawPixel(x, y - 1, Color2);
                osd->DrawPixel(x + 1, y - 1, Color2);
                osd->DrawPixel(x - 1, y, Color2);
                osd->DrawPixel(x, y, Color2);
                osd->DrawPixel(x + 1, y, Color2);
                osd->DrawPixel(x - 1, y + 1, Color);
                osd->DrawPixel(x, y + 1, Color2);
                osd->DrawPixel(x + 1, y + 1, Color2);
                break;
            case 8:
                osd->DrawPixel(x - 1, y - 1, Color);
                osd->DrawPixel(x, y - 1, Color2);
                osd->DrawPixel(x + 1, y - 1, Color2);
                osd->DrawPixel(x - 1, y, Color2);
                osd->DrawPixel(x, y, Color2);
                osd->DrawPixel(x + 1, y, Color2);
                osd->DrawPixel(x - 1, y + 1, Color2);
                osd->DrawPixel(x, y + 1, Color2);
                osd->DrawPixel(x + 1, y + 1, Color2);
                break;
            case 9:
                osd->DrawPixel(x - 1, y - 1, Color2);
                osd->DrawPixel(x, y - 1, Color2);
                osd->DrawPixel(x + 1, y - 1, Color2);
                osd->DrawPixel(x - 1, y, Color2);
                osd->DrawPixel(x, y, Color2);
                osd->DrawPixel(x + 1, y, Color2);
                osd->DrawPixel(x - 1, y + 1, Color2);
                osd->DrawPixel(x, y + 1, Color2);
                osd->DrawPixel(x + 1, y + 1, Color2);
                break;
            default:
                osd->DrawPixel(x - 1, y - 1, Color2);
                osd->DrawPixel(x, y - 1, Color2);
                osd->DrawPixel(x + 1, y - 1, Color2);
                osd->DrawPixel(x - 1, y, Color2);
                osd->DrawPixel(x, y, Color2);
                osd->DrawPixel(x + 1, y, Color2);
                osd->DrawPixel(x - 1, y + 1, Color2);
                osd->DrawPixel(x, y + 1, Color);
                osd->DrawPixel(x + 1, y + 1, Color2);
            }
        }
    }
}


class cPluginDitherndemo:public cPlugin {
  private:
    // Add any member variables or functions you may need here.
  public:
    cPluginDitherndemo(void);
    virtual ~ cPluginDitherndemo();
    virtual const char *Version(void) {
        return VERSION;
    } virtual const char *Description(void) {
        return DESCRIPTION;
    }
    virtual const char *MainMenuEntry(void) {
        return MAINMENUENTRY;
    }
    virtual cOsdObject *MainMenuAction(void);
};

cPluginDitherndemo::cPluginDitherndemo(void)
{
}

cPluginDitherndemo::~cPluginDitherndemo()
{
}

cOsdObject *cPluginDitherndemo::MainMenuAction(void)
{
    // Perform the action when selected from the main VDR menu.
    return new cDithernOsd();
}

VDRPLUGINCREATOR(cPluginDitherndemo);   // Don't touch this!
