#include "imageloader.h"
#include "timeline.h"

cTimeLine::cTimeLine(cTimeManager *timeManager) {
    this->timeManager = timeManager;
    lastClock = "";
    timeBase = NULL;
    int x11, x21, y11, y21, x12, x22, y12, y22;
    if (config.displayMode == eVertical) {
        x11 = 0;
        x21 = geoManager.dateVieverWidth;
        y11 = geoManager.statusHeaderHeight + geoManager.clockHeight;
        y21 = geoManager.dateVieverHeight;
        x12 = 0;
        x22 = geoManager.timeLineWidth;
        y12 = geoManager.statusHeaderHeight + geoManager.channelHeaderHeight + geoManager.channelGroupsHeight;
        y22 = geoManager.osdHeight - geoManager.statusHeaderHeight - geoManager.channelHeaderHeight - geoManager.channelGroupsHeight - geoManager.footerHeight;
    } else if (config.displayMode == eHorizontal) {
        x11 = geoManager.clockWidth;
        x21 = geoManager.dateVieverWidth;
        y11 = geoManager.statusHeaderHeight;
        y21 = geoManager.dateVieverHeight;
        x12 = geoManager.channelHeaderWidth + geoManager.channelGroupsWidth;
        x22 = geoManager.osdWidth - geoManager.channelHeaderWidth - geoManager.channelGroupsWidth;
        y12 = geoManager.statusHeaderHeight;
        y22 = geoManager.timeLineHeight;
    }
    dateViewer = new cStyledPixmap(osdManager.requestPixmap(1, cRect(x11, y11, x21, y21)));
    timeline = osdManager.requestPixmap(2, cRect(x12, y12, x22, y22));
    clock = new cStyledPixmap(osdManager.requestPixmap(3, cRect(0, 
                                                                geoManager.statusHeaderHeight, 
                                                                geoManager.clockWidth, 
                                                                geoManager.clockHeight)));
}

cTimeLine::~cTimeLine(void) {
    if (clock)
       delete clock;
    osdManager.releasePixmap(timeBase);
    osdManager.releasePixmap(timeline);
    if (dateViewer)
        delete dateViewer;
}

void cTimeLine::DrawDateViewer(void) {
    cString weekDay = timeManager->GetWeekday();
    cString date = timeManager->GetDate();   
    if (config.style != eStyleGraphical) {
        dateViewer->setColor(theme.Color(clrHeader), theme.Color(clrHeaderBlending));
        dateViewer->drawBackground();
        dateViewer->drawBorder();
        
    } else {
        cImage *imgBack = imgCache.GetOsdElement(oeDateViewer);
        if (imgBack)
            dateViewer->DrawImage(cPoint(0,0), *imgBack);
        else
            dateViewer->Fill(clrTransparent);
    }
    tColor colorFont = theme.Color(clrButtonYellow);
    tColor colorFontBack = (config.style == eStyleFlat) ? theme.Color(clrHeader) : clrTransparent;

    if (config.displayMode == eVertical) {
        int textHeightWeekday = fontManager.FontTimeLineWeekday->Height();
        int textHeightDate = fontManager.FontTimeLineDate->Height();
        int weekdayWidth = fontManager.FontTimeLineWeekday->Width(*weekDay);
        int dateWidth = fontManager.FontTimeLineDate->Width(*date);
        int y = ((geoManager.dateVieverHeight - textHeightWeekday - textHeightDate) / 2);
        dateViewer->DrawText(cPoint((geoManager.timeLineWidth - weekdayWidth) / 2, y), *weekDay, colorFont, colorFontBack, fontManager.FontTimeLineWeekday);
        dateViewer->DrawText(cPoint((geoManager.timeLineWidth - dateWidth) / 2, y + textHeightWeekday), *date, colorFont, colorFontBack, fontManager.FontTimeLineDate);
    } else if (config.displayMode == eHorizontal) {
        cString strDate = cString::sprintf("%s %s", *weekDay, *date);
        int x = ((dateViewer->Width() - fontManager.FontTimeLineDateHorizontal->Width(*strDate)) / 2);
        int y = ((dateViewer->Height() - fontManager.FontTimeLineDateHorizontal->Height()) / 2);
        dateViewer->DrawText(cPoint(x, y), *strDate, colorFont, colorFontBack, fontManager.FontTimeLineDateHorizontal);
    }
}

void cTimeLine::DrawTimeline(void) {
//    timeline->SetTile(true);
    timeline->Fill(clrTransparent);
    tColor colorFont, colorBackground;
    
    int imgWidth = geoManager.timeLineGridWidth;
    int imgHeight = geoManager.timeLineGridHeight;
    const cImage *img1 = NULL;
    const cImage *img2 = NULL;
    if (config.style == eStyleGraphical) {
        img1 = imgCache.GetOsdElement(oeTimeline1);
        img2 = imgCache.GetOsdElement(oeTimeline2);
    } else {
        img1 = CreateBackgroundImage(imgWidth, imgHeight, theme.Color(clrTimeline1), theme.Color(clrTimeline1Blending));
        img2 = CreateBackgroundImage(imgWidth, imgHeight, theme.Color(clrTimeline2), theme.Color(clrTimeline2Blending));
    }
    const cImage *img = NULL;
    if (!img1 || !img2)
        return;
    int textWidth, posX, posY;
    char timetext[10];
    
    int halfHours;
    if (config.displayMode == eVertical)
        halfHours = config.displayTime / 30 + 1;
    else
        halfHours = config.displayHorizontalTime / 30 + 1;

    time_t tStart = timeManager->GetStart();
    tm *t = localtime ( &tStart );

    int x = 2 * t->tm_hour + ((t->tm_min == 0) ? 0 : 1);
    for (int j = x; j < (x + halfHours); j++) {
        int i = (j >= 48) ? (j - 48) : j;
        if (i % 2 == 0) {
            img = img1;
            colorFont = theme.Color(clrTimeline2);
            colorBackground = (config.style == eStyleFlat)?theme.Color(clrTimeline1):clrTransparent;
            if (config.timeFormat == e12Hours) {
                if (i == 0)
                    sprintf(timetext, "12:00 PM");
                else if (i/2 < 13)
                    sprintf(timetext, "%d:00 AM", i / 2);
                else 
                    sprintf(timetext, "%d:00 PM", i / 2 - 12);
            } else {
                sprintf(timetext, "%d:00", i / 2);
            }
        } else {
            img = img2;
            colorFont = theme.Color(clrTimeline1);
            colorBackground = (config.style == eStyleFlat)?theme.Color(clrTimeline2):clrTransparent;
            if (config.timeFormat == e12Hours) {
                if (i == 1)
                    sprintf(timetext, "12:30 PM");
                else if (i/2 < 13)
                    sprintf(timetext, "%d:30 AM", i / 2);
                else 
                    sprintf(timetext, "%d:30 PM", i / 2 - 12);
            } else {
                sprintf(timetext, "%d:30", i / 2);
            }
        }
        if (config.displayMode == eVertical) {
            posY = (j - x) * geoManager.minutePixel * 30;
            timeline->DrawImage(cPoint(0, posY), *img);
            if (config.style != eStyleGraphical) {
                DecorateTile(0, posY, imgWidth + 2, imgHeight);
            }
            textWidth = fontManager.FontTimeLineTime->Width(timetext);
            timeline->DrawText(cPoint((geoManager.timeLineWidth-textWidth) / 2, posY + 5), timetext, colorFont, colorBackground, fontManager.FontTimeLineTime);
        } else if (config.displayMode == eHorizontal) {
            posX = (j - x) * geoManager.minutePixel * 30;
            timeline->DrawImage(cPoint(posX, 0), *img);
            if (config.style != eStyleGraphical) {
                DecorateTile(posX, 0, imgWidth, imgHeight + 2);
            }
            timeline->DrawText(cPoint(posX + 15, (dateViewer->Height() - fontManager.FontTimeLineTimeHorizontal->Height()) / 2), timetext, colorFont, colorBackground, fontManager.FontTimeLineTimeHorizontal);
       }
    }
    DrawTimeIndicator();
    if (config.style != eStyleGraphical) {
        delete img1;
        delete img2;
    }
}

void cTimeLine::DecorateTile(int posX, int posY, int tileWidth, int tileHeight) {
    timeline->DrawRectangle(cRect(posX,posY,tileWidth,2), clrTransparent);          //top
    timeline->DrawRectangle(cRect(posX,posY,2,tileHeight), clrTransparent);         //left
    timeline->DrawRectangle(cRect(posX,posY + tileHeight-2,tileWidth,2), clrTransparent);   //bottom
    timeline->DrawRectangle(cRect(posX + tileWidth-2,posY,2,tileHeight), clrTransparent);   //right

    timeline->DrawRectangle(cRect(2+posX,posY+2,tileWidth-4,1), theme.Color(clrBorder));            //top
    timeline->DrawRectangle(cRect(2+posX,posY+2,1,tileHeight-4), theme.Color(clrBorder));           //left
    timeline->DrawRectangle(cRect(2+posX,posY+tileHeight-3,tileWidth-4,1), theme.Color(clrBorder));     //bottom
    timeline->DrawRectangle(cRect(posX+tileWidth-3,posY+2,1,tileHeight-4), theme.Color(clrBorder));     //right
    
    if (config.roundedCorners) {
        int borderRadius = 12;
        DrawRoundedCorners(posX, posY, tileWidth, tileHeight, borderRadius);
    }
}

void cTimeLine::DrawRoundedCorners(int posX, int posY, int width, int height, int radius) {
    timeline->DrawEllipse(cRect(posX+2,posY+2,radius,radius), theme.Color(clrBorder), -2);
    timeline->DrawEllipse(cRect(posX+1,posY+1,radius,radius), clrTransparent, -2);

    timeline->DrawEllipse(cRect(posX+width-radius - 2,posY+2,radius,radius), theme.Color(clrBorder), -1);
    timeline->DrawEllipse(cRect(posX+width-radius - 1,posY+1,radius,radius), clrTransparent, -1);
    
    if( height > 2*radius) {
        timeline->DrawEllipse(cRect(posX+2,posY+height-radius - 2,radius,radius), theme.Color(clrBorder), -3);
        timeline->DrawEllipse(cRect(posX+1,posY+height-radius - 1,radius,radius), clrTransparent, -3);
        
        timeline->DrawEllipse(cRect(posX+width-radius - 2,posY+height-radius - 2,radius,radius), theme.Color(clrBorder), -4);
        timeline->DrawEllipse(cRect(posX+width-radius - 1,posY+height-radius - 1,radius,radius), clrTransparent, -4);
    }
}

void cTimeLine::DrawTimeIndicator(void) {
    if (!config.displayTimeBase)
        return;
    if (!timeManager->NowVisible()) {
       if (timeBase)
          timeBase->Fill(clrTransparent);
       return;
    }
    int deltaTime = (time(0) - timeManager->GetStart()) / 60 * geoManager.minutePixel;
    osdManager.releasePixmap(timeBase);
    int x1, x2, y1, y2;
    if (config.displayMode == eVertical) {
       x1 = 0;
       y1 = geoManager.statusHeaderHeight + geoManager.channelGroupsHeight + geoManager.channelHeaderHeight + deltaTime - 2;
       x2 = geoManager.osdWidth;
       y2 = 4;
    } else {
       x1 = geoManager.channelGroupsWidth + geoManager.channelHeaderWidth + deltaTime - 2;
       y1 = geoManager.statusHeaderHeight;
       x2 = 4;
       y2 = geoManager.timeLineHeight + config.channelRows * geoManager.rowHeight;
    }
    timeBase = osdManager.requestPixmap(3, cRect(x1, y1, x2, y2));
    timeBase->Fill(clrTransparent);
    timeBase->DrawRectangle(cRect(0, 0, timeBase->ViewPort().Width(), timeBase->ViewPort().Height()), theme.Color(clrTimeBase));
}

cImage *cTimeLine::CreateBackgroundImage(int width, int height, tColor clrBgr, tColor clrBlend) {
    cImage *image = NULL;
    if (config.style == eStyleBlendingDefault) {
        image = new cImage(cSize(width, height));
        image->Fill(clrBgr);
        int stepY = 0.5*height / 64;
        int alpha = 0x00;
        tColor clr;
        for (int i = 0; i<64; i++) {
            clr = AlphaBlend(clrBgr, clrBlend, alpha);
            for (int y = i*stepY; y < (i+1)*stepY; y++) {
                for (int x=0; x<width; x++) {
                    image->SetPixel(cPoint(x,y), clr);
                }
            }
            alpha += 0x04;
        }
    } else  if (config.style == eStyleBlendingMagick) {
        cImageLoader imgLoader;
        if (imgLoader.DrawBackground(clrBgr, clrBlend, width, height)) {
            image = new cImage(imgLoader.GetImage());
        }
    } else {
        image = new cImage(cSize(width, height));
        image->Fill(clrBgr);
    }
    return image;
}

bool cTimeLine::DrawClock(void) {
    cString currentTime = timeManager->GetCurrentTime();
    if (strcmp(currentTime, lastClock)) {
       clock->Fill(clrTransparent);
       const cFont *font = (config.displayMode == eVertical) ? fontManager.FontTimeLineTime : fontManager.FontTimeLineTimeHorizontal;
       int textHeight = font->Height();
       int clockTextWidth = font->Width(*currentTime);
       tColor colorFontBack = (config.style == eStyleFlat) ? theme.Color(clrHeader) : clrTransparent;
       if (config.style == eStyleGraphical) {
          clock->drawBackgroundGraphical(bgClock);
       } else {
          clock->setColor(theme.Color(clrHeader), theme.Color(clrHeaderBlending));
          clock->drawBackground();
          clock->drawBorder();
       }
       clock->DrawText(cPoint((geoManager.clockWidth - clockTextWidth) / 2, (geoManager.clockHeight - textHeight) / 2), *currentTime, theme.Color(clrFont), colorFontBack, font);
       lastClock = currentTime;
       DrawTimeIndicator();
       return true;
    }
    return false;
}
