#include "channelcolumn.h"
#include "dummygrid.h"

cChannelColumn::cChannelColumn(int num, const cChannel *channel, cMyTime *myTime) {
    this->channel = channel;
    this->num = num;
    this->myTime = myTime;
#if VDRVERSNUM < 20301
    hasTimer = channel->HasTimer();
#endif
    hasSwitchTimer = SwitchTimers.ChannelInSwitchList(channel);
#if VDRVERSNUM < 20301
    schedulesLock = new cSchedulesLock(false, 100);
#endif
    header = NULL;
}

cChannelColumn::~cChannelColumn(void) {
    if (header)
        delete header;
    grids.Clear();
#if VDRVERSNUM < 20301
    delete schedulesLock;
#endif
}

void cChannelColumn::clearGrids() {
    grids.Clear();
}

void cChannelColumn::createHeader() {
    header = new cHeaderGrid();
    header->createBackground(num);
    header->drawChannel(channel);
}

void cChannelColumn::drawHeader() {
    header->setPosition(num);
}

bool cChannelColumn::readGrids() {
#if VDRVERSNUM >= 20301
    const cSchedules* schedules;
    {
    LOCK_SCHEDULES_READ;
    schedules = Schedules;
    }
#else
    schedules = cSchedules::Schedules(*schedulesLock);
#endif
    const cSchedule *Schedule = NULL;
    Schedule = schedules->GetSchedule(channel);
    if (!Schedule) {
        addDummyGrid(myTime->GetStart(), myTime->GetEnd(), NULL, false);
        return true;
    }
    bool eventFound = false;
    bool dummyAtStart = false;
    const cEvent *startEvent = Schedule->GetEventAround(myTime->GetStart());
    if (startEvent != NULL) {
        eventFound = true;
    } else {
        for (int i=1; i<6; i++) {
            startEvent = Schedule->GetEventAround(myTime->GetStart()+i*5*60);
            if (startEvent) {
                eventFound = true;
                dummyAtStart = true;
                break;
            }
        }
    } 
    if (eventFound) {
        bool col = true;
        if (dummyAtStart) {
            addDummyGrid(myTime->GetStart(), startEvent->StartTime(), NULL, col);
            col = !col;
        }
        bool dummyNeeded = true;
        bool toFarInFuture = false;
        time_t endLast = myTime->GetStart();
        const cEvent *event = startEvent;
        const cEvent *eventLast = NULL;
        for (; event; event = Schedule->Events()->Next(event)) {
            if (endLast < event->StartTime()) {
                //gap, dummy needed
                time_t endTime = event->StartTime();
                if (endTime > myTime->GetEnd()) {
                    endTime = myTime->GetEnd();
                    toFarInFuture = true;
                }
                addDummyGrid(endLast, endTime, NULL, col);
                col = !col;
            }
            if (toFarInFuture) {
                break;
            }
            addEpgGrid(event, NULL, col);
            col = !col;
            endLast = event->EndTime();
            if (event->EndTime() > myTime->GetEnd()) {
                dummyNeeded = false;
                break;
            }
            eventLast = event;
        }
        if (dummyNeeded) {
            addDummyGrid(eventLast->EndTime(), myTime->GetEnd(), NULL, col);
        }
        return true;
    } else {
        addDummyGrid(myTime->GetStart(), myTime->GetEnd(), NULL, false);
        return true;
    }
    return false;
}

void cChannelColumn::drawGrids() {
    for (cGrid *grid = grids.First(); grid; grid = grids.Next(grid)) {
        grid->SetViewportHeight();
        grid->PositionPixmap();
        grid->Draw();
    }
}

int cChannelColumn::getX() {
    return geoManager.timeLineWidth + num*geoManager.colWidth;
}

int cChannelColumn::getY() {
    return geoManager.statusHeaderHeight +  geoManager.timeLineHeight + num*geoManager.rowHeight;
}

cGrid * cChannelColumn::getActive() {
    cMyTime t;
    t.Now();
    for (cGrid *grid = grids.First(); grid; grid = grids.Next(grid)) {
        if (grid->Match(t.Get()))
            return grid;
    }
    return grids.First();
}

cGrid * cChannelColumn::getNext(cGrid *activeGrid) {
    if (activeGrid == NULL)
        return NULL;
    cGrid *next = grids.Next(activeGrid);
    if (next)
        return next;
    return NULL;
}

cGrid * cChannelColumn::getPrev(cGrid *activeGrid) {
    if (activeGrid == NULL)
        return NULL;
    cGrid *prev = grids.Prev(activeGrid);
    if (prev)
        return prev;
    return NULL;
}

cGrid * cChannelColumn::getNeighbor(cGrid *activeGrid) {
    if (!activeGrid)
        return NULL;
    cGrid *neighbor = NULL;
    int overlap = 0;
    int overlapNew = 0;
    cGrid *grid = NULL;
    grid = grids.First();
    if (grid) {
        for (; grid; grid = grids.Next(grid)) {
            if ( (grid->StartTime() == activeGrid->StartTime()) ) {
                neighbor = grid;
                break;
            }
            overlapNew = activeGrid->calcOverlap(grid);
            if (overlapNew > overlap) {
                neighbor = grid;
                overlap = overlapNew;
            }
        }
    }
    if (!neighbor)
        neighbor = grids.First();
    return neighbor;
}

bool cChannelColumn::isFirst(cGrid *grid) {
    if (grid == grids.First())
        return true;
    return false;
}

void cChannelColumn::AddNewGridsAtStart() {
    cGrid *firstGrid = NULL;
    firstGrid = grids.First();
    if (firstGrid == NULL)
        return;
    //if first event is long enough, nothing to do.
    if (firstGrid->StartTime() <= myTime->GetStart()) {
        return;
    }
    //if not, i have to add new ones to the list
#if VDRVERSNUM >= 20301
    const cSchedules* schedules;
    {
    LOCK_SCHEDULES_READ;
    schedules = Schedules;
    }
#else
    schedules = cSchedules::Schedules(*schedulesLock);
#endif
    const cSchedule *Schedule = NULL;
    Schedule = schedules->GetSchedule(channel);
    if (!Schedule) {
        if (firstGrid->isDummy()) {
            firstGrid->SetStartTime(myTime->GetStart());
            firstGrid->SetEndTime(myTime->GetEnd());
        }
        return;
    }
    bool col = !(firstGrid->IsColor1());
    bool dummyNeeded = true;
    for (const cEvent *event = Schedule->GetEventAround(firstGrid->StartTime()-60); event; event = Schedule->Events()->Prev(event)) {
        if (!event)
            break;
        if (event->EndTime() < myTime->GetStart()) {
            break;
        }
        cGrid *grid = addEpgGrid(event, firstGrid, col);
        col = !col;
        firstGrid = grid;
        if (event->StartTime() <= myTime->GetStart()) {
            dummyNeeded = false;
            break;
        }
    }
    if (dummyNeeded) {
        firstGrid = grids.First();
        if (firstGrid->isDummy()) {
            firstGrid->SetStartTime(myTime->GetStart());
            if (firstGrid->EndTime() >= myTime->GetEnd())
                firstGrid->SetEndTime(myTime->GetEnd());
        } else {
            addDummyGrid(myTime->GetStart(), firstGrid->StartTime(), firstGrid, col);
        }
    }
}

void cChannelColumn::AddNewGridsAtEnd() {
    cGrid *lastGrid = NULL;
    lastGrid = grids.Last();
    if (lastGrid == NULL)
        return;
    //if last event is long enough, nothing to do.
    if (lastGrid->EndTime() >= myTime->GetEnd()) {
        return;
    }
    //if not, i have to add new ones to the list
#if VDRVERSNUM >= 20301
    const cSchedules* schedules;
    {
    LOCK_SCHEDULES_READ;
    schedules = Schedules;
    }
#else
    schedules = cSchedules::Schedules(*schedulesLock);
#endif
    const cSchedule *Schedule = NULL;
    Schedule = schedules->GetSchedule(channel);
    if (!Schedule) {
        if (lastGrid->isDummy()) {
            lastGrid->SetStartTime(myTime->GetStart());
            lastGrid->SetEndTime(myTime->GetEnd());
        }
        return;
    }
    bool col = !(lastGrid->IsColor1());
    bool dummyNeeded = true;
    for (const cEvent *event = Schedule->GetEventAround(lastGrid->EndTime()+60); event; event = Schedule->Events()->Next(event)) {
        if (!event)
            break;
        if (event->StartTime() > myTime->GetEnd()) {
            break;
        }
        addEpgGrid(event, NULL, col);
        col = !col;
        if (event->EndTime() > myTime->GetEnd()) {
            dummyNeeded = false;
            break;
        }
    }
    if (dummyNeeded) {
        lastGrid = grids.Last();
        if (lastGrid->isDummy()) {
            lastGrid->SetEndTime(myTime->GetEnd());
            if (lastGrid->StartTime() <= myTime->GetStart())
                lastGrid->SetStartTime(myTime->GetStart());
        } else {
            addDummyGrid(lastGrid->EndTime(), myTime->GetEnd(), NULL, col);
        }
    }
}

void cChannelColumn::ClearOutdatedStart() {
    cGrid *firstGrid = NULL;
    while (true) {
        firstGrid = grids.First();
        if (!firstGrid)
            break;
        if (firstGrid->EndTime() <= myTime->GetStart()) {
            grids.Del(firstGrid);
            firstGrid = NULL;
        } else {
            if (firstGrid->isDummy()) {
                firstGrid->SetStartTime(myTime->GetStart());
                cGrid *next = getNext(firstGrid);
                if (next) {
                    firstGrid->SetEndTime(next->StartTime());
                } else {
                    firstGrid->SetEndTime(myTime->GetEnd());
                }
            }
            break;
        }
    }
}

void cChannelColumn::ClearOutdatedEnd() {
    cGrid *lastGrid = NULL;
    while (true) {
        lastGrid = grids.Last();    
        if (!lastGrid)
            break;
        if (lastGrid->StartTime() >= myTime->GetEnd()) {
            grids.Del(lastGrid);
            lastGrid = NULL;
        } else {
            if (lastGrid->isDummy()) {
                lastGrid->SetEndTime(myTime->GetEnd());
                cGrid *prev = getPrev(lastGrid);
                if (prev) {
                    lastGrid->SetStartTime(prev->EndTime());
                } else {
                    lastGrid->SetStartTime(myTime->GetStart());
                }
            }
            break;
        }
    }
}

cGrid *cChannelColumn::addEpgGrid(const cEvent *event, cGrid *firstGrid, bool color) {
    cGrid *grid = new cEpgGrid(this, event);
    grid->setText();
    grid->SetColor(color);
    if (!firstGrid)
        grids.Add(grid);
    else
        grids.Ins(grid, firstGrid);
    return grid;
}

cGrid *cChannelColumn::addDummyGrid(time_t start, time_t end, cGrid *firstGrid, bool color) {
    cGrid *dummy = new cDummyGrid(this, start, end);
    dummy->setText();
    dummy->SetColor(color);
    if (!firstGrid)
        grids.Add(dummy);
    else
        grids.Ins(dummy, firstGrid);
    return dummy;
}

void cChannelColumn::SetTimers() {
#if VDRVERSNUM < 20301
    hasTimer = channel->HasTimer();
#endif
    hasSwitchTimer = SwitchTimers.ChannelInSwitchList(channel);
    for (cGrid *grid = grids.First(); grid; grid = grids.Next(grid)) {
        bool gridHadTimer = grid->HasTimer();
        grid->SetTimer();
        if (gridHadTimer || gridHadTimer != grid->HasTimer())
            grid->SetDirty();
        bool gridHadSwitchTimer = grid->HasSwitchTimer();
        grid->SetSwitchTimer();
        if (gridHadSwitchTimer != grid->HasSwitchTimer())
            grid->SetDirty();
        grid->Draw();
    }
}

void cChannelColumn::dumpGrids() {
    esyslog("tvguide: ------Channel %s %d: %d entires ---------", channel->Name(), num, grids.Count());
    int i=1;
    for (cGrid *grid = grids.First(); grid; grid = grids.Next(grid)) {
        esyslog("tvguide: grid %d: start: %s, stop: %s", i, *cMyTime::printTime(grid->StartTime()), *cMyTime::printTime(grid->EndTime()));
        i++;
    }
}
