/*
 * loadepg.c: A plugin for the Video Disk Recorder
 *
 * See the README file for copyright information and how to reach the author.
 *
 * $Id$
 */

#include "loadepg.h"

//-- cLoadepgFilter -------------------------------------------------------

cLoadepgFilter::cLoadepgFilter( void )
{
    Set( 0x14, 0x73 );
    Set( 0xd3, 0x91 );
    Set( 0xd3, 0x92 );
    Set( 0xd2, 0x90 );
    Set( 0xd3, 0x90 );
}

void cLoadepgFilter::Process( u_short Pid, u_char Tid, const u_char *Data, int Length )
{
    if( Status == STATUS_INIT )
    {
	fprintf( stderr, "scan provider: %s\n", EpgProviderTitles[MenuItem] );
	fprintf( stderr, "found %i equivalences channels\n", NumEpgEquivChannels );
	Status = STATUS_GET_EPG_TIME;
	VdrEquivChannelId = -1;
    }
    
    if( Status != STATUS_NULL && Status != STATUS_ACTIVE )
    {
	// Get time
	if( Pid == 0x14 && Tid == 0x73 )
	{
	    GetEpgTime( Data, Length );
	}
	
        // Get channels
	if( Pid == 0xd3 && Tid == 0x91 )
	{
	    GetEpgChannels( Data, Length );
	}
	
	// Get themes
	if( Pid == 0xd3 && Tid == 0x92 )
	{
	    GetEpgThemes( Data, Length );
	}
	
	// Get programs
	if( Pid == 0xd2 && Tid == 0x90 )
	{
	    GetEpgPrograms( Data, Length );
	}
	
	//Get summaries
	if( Pid == 0xd3 && Tid == 0x90 )
	{
	    GetEpgSummaries( Data, Length );
	}
    }
}

time_t cLoadepgFilter::LocalTime2Utc( time_t Time )
{
    struct tm *TmTime;
    TmTime = gmtime( &Time );
    TmTime->tm_isdst = -1;
    return mktime( TmTime );
}

time_t cLoadepgFilter::Utc2LocalTime( time_t Time )
{
    return ( 2 * Time ) - LocalTime2Utc( Time );
}

void cLoadepgFilter::GetEpgTime( const unsigned char *Data, int Length )
{
    if( Status == STATUS_GET_EPG_TIME )
    {
	struct tm *TmTime;
	time_t Time;
	Time = Utc2LocalTime( time( NULL ) - 86400 );
	TmTime = gmtime( &Time );
	Yesterday = TmTime->tm_wday;
	TmTime->tm_hour = 0;
	TmTime->tm_min = 0;
	TmTime->tm_sec = 0;
	TmTime->tm_isdst = -1;
	YesterdayEpoch = Utc2LocalTime( mktime( TmTime ) );
	CurrentTime = time( NULL );
	Status = STATUS_GET_EPG_CHANNELS;
	StatusProgressBar += 3;
    }
}

void cLoadepgFilter::GetEpgChannels( const unsigned char *Data, int Length )
{
    if( Status == STATUS_GET_EPG_CHANNELS )
    {
	int Id, Sid, Tid, Nid, Rid;
	char *Dummy;
	char FileName[255];
	FILE *File;
	EpgChannelType *EpgChannelData = ( EpgChannelType * ) ( Data + 4 );
	NumEpgChannels = ( Length - 4 ) / sizeof( EpgChannelType );
	memset( FileName, 0, sizeof( FileName ) );
	snprintf( FileName, sizeof( FileName ), "/tmp/%s.channels", EpgProviderTitles[MenuItem] );
	File = fopen( FileName, "w" );
	if( sscanf( EpgProviderChannels[MenuItem], "%a[^:]:%a[^:]:%a[^:]:%a[^:]:%a[^:]", &Dummy, &Dummy, &Dummy, &EpgSourceName, &Dummy ) == 5 )
	{
	    EpgSourceId = cSource::FromString( EpgSourceName );
	}
	for( Id = 0; Id < NumEpgChannels; Id ++ )
	{
	    Sid = ( ( EpgChannelData->ChannelIdHigh << 8 ) | EpgChannelData->ChannelIdLow ) & 0xffff;
	    Tid = ( ( EpgChannelData->TransponderIdHigh << 8 ) | EpgChannelData->TransponderIdLow ) & 0xffff;
	    Nid = ( ( EpgChannelData->NetworkIdHigh << 8 ) | EpgChannelData->NetworkIdLow ) & 0xffff;
	    Rid = 0;
	    tChannelID EpgChannelId = tChannelID( EpgSourceId, Nid, Tid, Sid, Rid );
	    cChannel *EpgChannel = Channels.GetByChannelID( EpgChannelId, true );
	    memset( EpgChannels[Id], 0, sizeof( EpgChannels[Id] ) );
	    if( EpgChannel )
	    {
		snprintf( EpgChannels[Id], sizeof( EpgChannels[Id] ), "%s-%i-%i-%i-%i %s", EpgSourceName, EpgChannel->Nid(), EpgChannel->Tid(), EpgChannel->Sid(), EpgChannel->Rid(), EpgChannel->Name() );
		if( File != NULL )
		{
		    fprintf( File, "%s\n", EpgChannels[Id] );
		}
	    }
	    EpgChannelData ++;
	}
	if( File != NULL )
	{
	    fclose( File );
	    fprintf( stderr, "epg channels list saved in %s\n", FileName );
	}
	fprintf( stderr, "found %i channels\n", NumEpgChannels );
	NumChannels = NumEpgChannels + NumEpgEquivChannels;
	Status = STATUS_GET_EPG_THEMES;
	StatusProgressBar += 3;
    }
}

void cLoadepgFilter::GetEpgThemes( const unsigned char *Data, int Length )
{
    if( Status == STATUS_GET_EPG_THEMES )
    {
	int Id, Theme, Offset;
	const unsigned char *EpgThemesIndex = Data + 3;
	typedef unsigned char EpgThemeType[15];
	EpgThemeType *EpgThemeData = ( EpgThemeType * ) ( Data + 19 );
	Theme = 0;
	Offset = 0;
	NumThemes = ( Length - 19 ) / 15;
	for( Id = 0; Id < NumThemes; Id ++ )
	{
	    if( EpgThemesIndex[Theme] == Id )
	    {
		Offset = ( Offset + 15 ) & 0xf0;
		Theme ++;
	    }
	    memcpy( EpgThemes[Offset], EpgThemeData, 15 );
	    EpgThemes[Offset][15] = 0;
	    CleanString( EpgThemes[Offset] );
	    Offset ++;
	    EpgThemeData ++;
	}
	fprintf( stderr, "found %i themes\n", NumThemes );
	Status = STATUS_GET_EPG_PROGRAM_INITIAL;
	StatusProgressBar += 3;
    }
}


void cLoadepgFilter::GetEpgPrograms( const unsigned char *Data, int Length )
{
    int Day, Hours, Id;
    EpgProgramType *EpgProgramData = ( EpgProgramType * ) Data;
    if( Length == EPG_PROGRAM_LENGTH )
    {
	if( Status == STATUS_GET_EPG_PROGRAM_INITIAL && EpgProgramData->ChannelId != 0xff )
	{
	    for( Id = 0; Id < ( 24 * 7 ); Id ++ )
	    {
		HoursIndex[Id] = -1;
	    }
	    NumEpgPrograms = 0;
	    memcpy( EpgProgramInitial, EpgProgramData, EPG_PROGRAM_LENGTH );
	    Status = STATUS_GET_EPG_PROGRAMS;
	    goto EndGetEpgPrograms;
	}
	if( Status == STATUS_GET_EPG_PROGRAMS )
	{
	    Day = EpgProgramData->Day;
	    if( Day == 7 )
	    {
		Day = 0;
	    }
	    Hours = EpgProgramData->Hours;
	    if( Hours > 15 )
	    {
	        Hours = Hours - 4;
	    }
	    else if( Hours > 7 )
	    {
	        Hours = Hours - 2;
	    }
	    else
	    {
	        if( Day == 6 )
		{
		    //Day = 0;
		}
		else
		{
		    //Day = Day + 1;
		}
	    }
	    if( EpgProgramData->ChannelId == 0xff )
	    {
	        HoursIndex[ ( Day * 24 ) + Hours ] = NumEpgPrograms;
	    }
	    else
	    {
		struct EpgProgram *EpgProgram = ( struct EpgProgram * ) malloc( sizeof( struct EpgProgram ) );
		EpgPrograms[NumEpgPrograms++] = EpgProgram;
		EpgProgram->ChannelId = EpgProgramData->ChannelId - 1;
		EpgProgram->ThemeId = EpgProgramData->ThemeId;
		EpgProgram->Time = ( ( Day - Yesterday ) * 86400 ) + ( Hours * 3600 ) + ( EpgProgramData->Minutes * 60 );
		if( EpgProgram->Time < ( 6 * 3600 ) )
		{
		    EpgProgram->Time = EpgProgram->Time + ( 7 * 86400 );
		}
		EpgProgram->Time = EpgProgram->Time + YesterdayEpoch;
		EpgProgram->Time = cLoadepgFilter::LocalTime2Utc( EpgProgram->Time );
		EpgProgram->SummaryAvailable = EpgProgramData->SummaryAvailable;
		EpgProgram->Duration = HILO16( EpgProgramData->Duration ) * 60;
		memcpy( EpgProgram->Title, &EpgProgramData->Title, 23 );
		EpgProgram->Title[23] = 0;
		CleanString( EpgProgram->Title );
		EpgProgram->PpvId = HILO32( EpgProgramData->PpvId );
		EpgProgram->ProgramId = HILO32( EpgProgramData->ProgramId );
	    }
	    if( ! memcmp( EpgProgramInitial, Data, EPG_PROGRAM_LENGTH ) )
	    {
		fprintf( stderr, "found %i programs\n", NumEpgPrograms );
	        Status = STATUS_GET_EPG_SUMMARY_INITIAL;
		StatusProgressBar += 6;
	    }
	}
    }
    EndGetEpgPrograms:;
}

void cLoadepgFilter::GetEpgSummaries( const unsigned char *Data, int Length )
{
    int Id, Offset;
    time_t UnixTime;
    EpgSummaryType *EpgSummaryData = ( EpgSummaryType * ) Data;
    EpgSummaryReplayType *EpgSummaryReplayData = ( EpgSummaryReplayType * ) ( EpgSummaryData + 1 );
    if( EpgSummaryData->Byte7 == 0xff && EpgSummaryData->Byte8 == 0xff && EpgSummaryData->Byte9 == 0xff && EpgSummaryData->NumReplays < EPG_SUMMARY_NUM_REPLAYS && Length > EPG_SUMMARY_LENGTH )
    {
	if( Status == STATUS_GET_EPG_SUMMARY_INITIAL )
	{
	    NumEpgSummaries = 0;
	    memcpy( EpgSummaryInitial, EpgSummaryData, EPG_SUMMARY_LENGTH );
	    Status = STATUS_GET_EPG_SUMMARIES;
	    goto EndGetEpgSummaries;
	}
	if( Status == STATUS_GET_EPG_SUMMARIES )
	{
	    struct EpgSummary *EpgSummary = ( struct EpgSummary * ) malloc( sizeof( struct EpgSummary ) );
	    EpgSummaries[NumEpgSummaries++] = EpgSummary;
	    EpgSummary->ProgramId = HILO32( EpgSummaryData->ProgramId );
	    EpgSummary->NumReplays = EpgSummaryData->NumReplays;
	    Offset = EPG_SUMMARY_LENGTH + ( EpgSummaryData->NumReplays * EPG_SUMMARY_REPLAY_LENGTH );
	    EpgSummary->Text = ( unsigned char * ) malloc( Length + 1 - Offset );
	    Id = 0;
	    do
	    {
		if( Data[Offset+Id] == 0x0a )
		{
		    EpgSummary->Text[Id] = 0x7c;
		}
		else if( Data[Offset+Id] < 0x20 || ( Data[Offset+Id] > 0x7e  && Data[Offset+Id] < 0xa0 ) )
		{
		    EpgSummary->Text[Id] = 0x20;
		}
		else
		{
		    EpgSummary->Text[Id] = Data[Offset+Id];
		}
		Id ++;
	    }
	    while( ( Offset + Id ) < Length );
	    EpgSummary->Text[Id] = 0;
	    for( Id = 0; Id < EpgSummaryData->NumReplays; Id ++ )
	    {
		UnixTime = ( HILO16( EpgSummaryReplayData->ReplayMjd ) - 40587 ) * 86400;
		UnixTime = UnixTime + ( ( ( ( ( EpgSummaryReplayData->ReplayTimeHours >> 4 ) & 0x0f ) * 10 ) + ( EpgSummaryReplayData->ReplayTimeHours & 0x0f ) ) * 3600 );
		UnixTime = UnixTime + ( ( ( ( ( EpgSummaryReplayData->ReplayTimeMinutes >> 4 ) & 0x0f ) * 10 ) + ( EpgSummaryReplayData->ReplayTimeMinutes & 0x0f ) ) * 60 );
		UnixTime = UnixTime + ( ( ( ( EpgSummaryReplayData->ReplayTimeSeconds >> 4 ) & 0x0f ) * 10 ) + ( EpgSummaryReplayData->ReplayTimeSeconds & 0x0f ) );
		EpgSummary->Time[Id] = cLoadepgFilter::LocalTime2Utc( UnixTime );
		EpgSummary->ChannelId[Id] = EpgSummaryReplayData->ChannelId - 1;
		EpgSummary->SubtitlesAvailable[Id] = EpgSummaryReplayData->SubtitlesAvailable;
		EpgSummary->LastReplay[Id] = EpgSummaryReplayData->LastReplay;
		EpgSummaryReplayData ++;
	    }
	    if( ! memcmp( EpgSummaryInitial, EpgSummaryData, EPG_SUMMARY_LENGTH ) )
	    {
		fprintf( stderr, "found %i summaries\n", NumEpgSummaries );
		Status = STATUS_LOADEPG;
		StatusProgressBar += 6;
		kProgressBar = ( float ) ( ( float( 79 ) ) / ( ( float ) ( NumChannels ) ) );
	    }
	}
    }
    EndGetEpgSummaries:;
}

void cLoadepgFilter::CleanString( unsigned char *String )
{
    unsigned char *Begin;
    unsigned char *End;
    Begin = String;
    End = String;
    while( *String )
    {
	if( ( *String ) == 0x0a )
	{
	    *String = 0x7c;
	}
	else if( ( *String ) < 0x20 || ( ( *String ) > 0x7e && ( *String ) < 0xa0 ) )
	{
	    *String = 0x20;
	}
	String ++;
    }
    String = Begin;
    while( *String )
    {
	if( ( *String ) != 0x20 )
	{
	    End = String + 1;
	}
	String ++;
    }
    *End = 0;
}

//-------------------------------------------------------------------------

//-- cLoadepgOsd ----------------------------------------------------------

cLoadepgOsd::cLoadepgOsd( void )
{
    Osd = NULL;
    Filter = new cLoadepgFilter();
    //cDevice::PrimaryDevice()->AttachFilter( Filter );
    cDevice::GetDevice( LoadepgConfig.DeviceNumber - 1 )->AttachFilter( Filter );
    Status = STATUS_ACTIVE;
    MenuItem = 0;
    VdrChannelId = GetCurrentChannelId();
}

cLoadepgOsd::~cLoadepgOsd( void )
{
    Status = STATUS_NULL;
    MenuItem = 0;
    //cDevice::PrimaryDevice()->Detach( Filter );
    cDevice::GetDevice( LoadepgConfig.DeviceNumber - 1 )->Detach( Filter );
    delete Filter;
    delete Osd;
}

void cLoadepgOsd::Show( void )
{
    Osd = cOsdProvider::NewOsd( 160, 88 );
    if( Osd )
    {
	tArea Area = { 0, 0, 399, 399, 4 };
	Osd->SetAreas( &Area, 1 );
    }
    Osd->DrawRectangle( 0, 0, 399, 399, clrTransparent );
    DrawOsd();
}

eOSState cLoadepgOsd::ProcessKey( eKeys Key )
{
    eOSState state = cOsdObject::ProcessKey( Key );
    StatusKey = 0;
    if( state == osUnknown )
    {
	switch( Key )
	{
	    case kOk:
		StatusKey = 1;
		if( Status == STATUS_ACTIVE )
		{
		    EpgChannelId = NewChannel( EpgProviderChannels[MenuItem] );
		    if( EpgChannelId != 0 )
		    {
			if( SwitchToChannelId( EpgChannelId ) == false )
			{
			    return osEnd;
			}
		    }
		    else
		    {
			Status = STATUS_NULL;
			return osEnd;
		    }
		    Status = STATUS_INIT;
		    Osd->DrawRectangle( 0, 0, 399, 399, clrTransparent );
		    StatusProgressBar = 0;
		}
		break;
	    case kUp:
		StatusKey = 1;
		if( Status == STATUS_ACTIVE )
		{
		    MenuItem = MenuItem - 1;
		    if( MenuItem < 0 )
		    {
			MenuItem = 0;
		    }
		}
		break;
	    case kDown:
		StatusKey = 1;
		if( Status == STATUS_ACTIVE )
		{
		    MenuItem = MenuItem + 1;
		    if( MenuItem >= EpgProviders )
		    {
			MenuItem = EpgProviders - 1;
		    }
		}
		break;
	    case kBack:
		StatusKey = 1;
		if( Status != STATUS_NULL && Status != STATUS_ACTIVE )
		{
		    if( SwitchToChannelId( VdrChannelId ) )
		    {
			DelChannel( EpgChannelId );
		    }
		}
		Status = STATUS_NULL;
		Osd->DrawRectangle( 0, 0, 399, 399, clrTransparent );
		return osEnd;
	    default:
		break;
	}
	Actions();
	state = osContinue;
	if( Status == STATUS_END )
	{
	    if( SwitchToChannelId( VdrChannelId ) )
	    {
		DelChannel( EpgChannelId );
	    }
	    Status = STATUS_NULL;
	    Osd->DrawRectangle( 0, 0, 399, 399, clrTransparent );
	    return osEnd;
	}
    }
    return state;
}

void cLoadepgOsd::SaveEpg( void )
{
    int ProgramId, SummaryId, Offset, FirstProgram;
    struct EpgProgram *EpgProgram;
    FILE *File;
    Offset = ( Yesterday * 24 ) + 6;
    for( ProgramId = 0; ProgramId < ( 24 * 7 ); ProgramId ++ )
    {
	FirstProgram = HoursIndex[( ProgramId + Offset ) % ( 24 * 7 ) ];
	if( FirstProgram != -1 )
	{
	    break;
	}
    }
    if( FirstProgram == -1 )
    {
	fprintf( stderr, "first program not found\n" );
	Status = STATUS_END;
	goto EndSaveEpg;
    }
    if( ( File = fopen( "/tmp/epg.tmp", "w" ) ) != NULL )
    {
	if( VdrEquivChannelId == -1 )
	{
	    fprintf( File, "C %s\n", EpgChannels[NumEpgChannels-1] );
	}
	else
	{
	    fprintf( File, "C %s\n", EpgEquivChannels[VdrEquivChannelId][1] );
	    EpgEquivChannels[VdrEquivChannelId][0][0] = 0;
	}
	for( ProgramId = 0; ProgramId < NumEpgPrograms; ProgramId ++ )
	{
	    EpgProgram = EpgPrograms[ ( FirstProgram + ProgramId ) % NumEpgPrograms ];
	    if( EpgProgram->ChannelId == ( unsigned int ) ( NumEpgChannels - 1 ) )
	    {
		if( ( EpgProgram->Time + EpgProgram->Duration ) > ( unsigned int ) CurrentTime )
		{
		    fprintf( File, "E %d %d %d 90\n", ProgramId, ( int ) ( EpgProgram->Time ), EpgProgram->Duration );
		    fprintf( File, "T %s\n", EpgProgram->Title );
		    fprintf( File, "S %s - %d\n", EpgThemes[EpgProgram->ThemeId], ( EpgProgram->Duration / 60 ) );
		    if( EpgProgram->SummaryAvailable == 1 )
		    {
			for( SummaryId = 0; SummaryId < NumEpgSummaries; SummaryId ++ )
			{
			    if( EpgProgram->ProgramId == EpgSummaries[SummaryId]->ProgramId )
			    {
				fprintf( File, "D %s\n", EpgSummaries[SummaryId]->Text );
				break;
			    }
			}
		    }
		    fprintf( File, "e\n" );
		}
	    }
	}
	fprintf( File, "c\n" );
	fclose( File );
    }
    if( ( File = fopen( "/tmp/epg.tmp", "r" ) ) != NULL )
    {
	rewind( File );
	if( cSchedules::Read( File ) )
	{
	    cSchedules::Cleanup( true );
	}
	fclose( File );
    }
    if( LoadepgConfig.UseFileEquivalences == true )
    {
	for( VdrEquivChannelId = 0; VdrEquivChannelId < NumEpgEquivChannels; VdrEquivChannelId ++ )
	{
	    if( ! memcmp( EpgChannels[NumEpgChannels-1], EpgEquivChannels[ VdrEquivChannelId ][0], sizeof( EpgChannels[NumEpgChannels-1] ) ) )
	    {
		goto EndEpgChannel;
	    }
	}
    }
    VdrEquivChannelId = -1;
    NumEpgChannels = NumEpgChannels - 1;
    if( NumEpgChannels < 1 )
    {
	fprintf( stderr, "epg saved\n" );
	Status = STATUS_END;
    }
    EndEpgChannel:;
    NumChannels = NumChannels - 1;
    StatusProgressBar = 21 + ( 79 - ( ( int ) ( ( ( float ) kProgressBar ) * ( ( float ) NumChannels ) ) ) );
    EndSaveEpg:;
}

void cLoadepgOsd::Actions( void )
{
    if( StatusKey == 1 )
    {
	DrawOsd();
	StatusKey = 0;
    }
    if( Status > STATUS_ACTIVE )
    {
	DrawOsd();
    }
    if( Status == STATUS_LOADEPG )
    {
	SaveEpg();
    }
}

void cLoadepgOsd::DrawOsd( void )
{
    int Id;
    const cFont *Font = cFont::GetFont( fontOsd );
    Margin = 10;
    Padding = 1;
    if( Osd )
    {
	if( Status == STATUS_ACTIVE )
	{
	    LineHeight = Font->Height() + ( Padding * 2 );
	    LineWidth = Font->Width( tr( "Loadepg" ) );
	    if( Font->Width( tr( "Select a provider and press Ok" ) ) > LineWidth )
	    {
		LineWidth = Font->Width( tr( "Select a provider and press Ok" ) );
	    }
	    LineWidth = LineWidth + ( Margin * 2 );
	    if( LineWidth > 399 )
	    {
		LineWidth = 399;
	    }
	    Left = ( 399 - LineWidth ) / 2;
	    Top = ( 399 - ( ( LineHeight * ( EpgProviders + 2 ) ) + ( 2 * Margin ) ) ) / 2;
	    
	    // Draw text top
	    TextTopX1 = Left;
	    TextTopY1 = Top;
	    TextTopX2 = TextTopX1 + LineWidth;
	    TextTopY2 = TextTopY1 + LineHeight;
	    Osd->DrawRectangle( TextTopX1, TextTopY1, TextTopX2, TextTopY2, clrBgTextTop );
	    Osd->DrawText( TextTopX1 + Margin, TextTopY1 + Padding, tr( "Loadepg" ), clrFgTextTop, clrBgTextTop, Font );
	    
	    // Draw text scroll
	    TextScrollX1 = Left;
	    TextScrollX2 = TextScrollX1 + LineWidth;
	    Osd->DrawRectangle( TextScrollX1, TextTopY2, TextScrollX2, TextTopY2 + ( LineHeight * EpgProviders ) + ( 2 * Margin ), clrBgTextScroll );
	    for( Id = 0; Id < EpgProviders; Id ++ )
	    {
		TextScrollY1 = TextTopY2 + ( LineHeight * Id ) + Margin;
		TextScrollY2 = TextScrollY1 + LineHeight;
		if( MenuItem == Id )
		{
		    Osd->DrawRectangle( TextScrollX1 + Margin , TextScrollY1, TextScrollX2 - Margin, TextScrollY2, clrBgMenuItem );
		    Osd->DrawText( TextScrollX1 + ( Margin * 2 ), TextScrollY1 + Padding, EpgProviderTitles[Id], clrFgMenuItem, clrBgMenuItem, Font );
		}
		else
		{
		    Osd->DrawText( TextScrollX1 + ( Margin * 2 ), TextScrollY1 + Padding, EpgProviderTitles[Id], clrFgTextScroll, clrBgTextScroll, Font );
		}
	    }
	    
	    // Draw text bottom
	    TextBottomX1 = Left;
	    TextBottomY1 = TextScrollY2 + Margin;
	    TextBottomX2 = TextBottomX1 + LineWidth;
	    TextBottomY2 = TextBottomY1 + LineHeight;
	    Osd->DrawRectangle( TextBottomX1, TextBottomY1, TextBottomX2, TextBottomY2, clrBgTextBottom );
	    Osd->DrawText( TextBottomX1 + Margin, TextBottomY1 + Padding, tr( "Select a provider and press Ok" ), clrFgTextBottom, clrBgTextBottom, Font );
	    
	}
	if( Status > STATUS_ACTIVE )
	{
	    if( LoadepgConfig.DeviceNumber == 1 )
	    {
		LineHeight = Font->Height() + ( Padding * 2 );
		LineWidth = 399;
		Left = 0;
		Top = ( 399 - ( LineHeight * 3 ) - PROGRESS_BAR_HEIGHT ) / 2;
	    
		// Draw text top
		TextTopX1 = Left;
		TextTopY1 = Top;
		TextTopX2 = TextTopX1 + LineWidth;
		TextTopY2 = TextTopY1 + LineHeight;
		Osd->DrawRectangle( TextTopX1, TextTopY1, TextTopX2, TextTopY2, clrBgTextTop );
		Osd->DrawText( TextTopX1 + Margin, TextTopY1 + Padding, tr( "Loadepg" ), clrFgTextTop, clrBgTextTop, Font );
	    
		// Draw progress-bar
		ProgressBarX1 = Left;
		ProgressBarY1 = TextTopY2;
		ProgressBarX2 = ProgressBarX1 + LineWidth;
		ProgressBarY2 = ProgressBarY1 + LineHeight + PROGRESS_BAR_HEIGHT;
		Osd->DrawRectangle( ProgressBarX1, ProgressBarY1, ProgressBarX2, ProgressBarY2, clrBgProgressBar1 );
		Osd->DrawText(  ProgressBarX1 + Margin, ProgressBarY1 + Padding, EpgProviderTitles[MenuItem], clrBlack, clrBgProgressBar1, Font );
		ProgressBarX1 = Left + 3;
		ProgressBarY1 = TextTopY2 + LineHeight + 3;
		ProgressBarX2 = ProgressBarX1 + LineWidth - 6;
		ProgressBarY2 = ProgressBarY1 + PROGRESS_BAR_HEIGHT - 6;
		Osd->DrawRectangle( ProgressBarX1, ProgressBarY1, ProgressBarX2, ProgressBarY2, clrFgProgressBar1 );
		ProgressBarX1 = ProgressBarX1 + 2;
		ProgressBarY1 = ProgressBarY1 + 2;
		ProgressBarX2 = ProgressBarX2 - 2;
		ProgressBarY2 = ProgressBarY2 - 2;
		Osd->DrawRectangle( ProgressBarX1, ProgressBarY1, ProgressBarX2, ProgressBarY2, clrBgProgressBar2 );
		ProgressBarX1 = ProgressBarX1 + 4;
		ProgressBarY1 = ProgressBarY1 + 4;
		ProgressBarX2 = ( int ) ( ( ( ( ( float ) ( ProgressBarX2 ) ) - ( float ) ( 4 ) ) / ( ( float ) ( 100 ) ) ) * ( ( float ) ( StatusProgressBar ) ) );
		ProgressBarY2 = ProgressBarY2 - 4;
		Osd->DrawRectangle( ProgressBarX1, ProgressBarY1, ProgressBarX2, ProgressBarY2, clrFgProgressBar2 );
	    
		// Draw text bottom
		TextBottomX1 = Left;
		TextBottomY1 = TextTopY2 + LineHeight + PROGRESS_BAR_HEIGHT;
		TextBottomX2 = TextBottomX1 + LineWidth;
		TextBottomY2 = TextBottomY1 + LineHeight;
		Osd->DrawRectangle( TextBottomX1, TextBottomY1, TextBottomX2, TextBottomY2, clrBgTextBottom );
		Osd->DrawText( TextBottomX1 + ( LineWidth - Font->Width( tr( "Wait load epg data" ) ) ) / 2, TextBottomY1 + Padding, tr( "Wait load epg data" ), clrFgTextBottom, clrBgTextBottom, Font );
	    }
	    else
	    {
		memset( TextBuffer, 0, sizeof( TextBuffer ) );
		snprintf( TextBuffer, sizeof( TextBuffer ), "%s - %02i %%", tr( "Loadepg" ), ( int ) ( StatusProgressBar ) );
		LineHeight = Font->Height() + ( Padding * 2 );
		LineWidth = Font->Width( TextBuffer ) + ( Margin * 2 );
		Left = 399 - ( LineWidth + 1 );
		Top = 399 - ( LineHeight + 1 );
		TextTopX1 = Left;
		TextTopY1 = Top;
		TextTopX2 = TextTopX1 + LineWidth;
		TextTopY2 = TextTopY1 + LineHeight;
		Osd->DrawRectangle( TextTopX1, TextTopY1, TextTopX2, TextTopY2, clrBgTextTop );
		Osd->DrawText( TextTopX1 + Margin, TextTopY1 + Padding, TextBuffer, clrFgTextTop, clrBgTextTop, Font );
	    }
	}
	Osd->Flush();
    }
}

int cLoadepgOsd::GetCurrentChannelId( void )
{
    //cChannel *Channel = Channels.GetByNumber( cDevice::PrimaryDevice()->CurrentChannel() );
    cChannel *Channel = Channels.GetByNumber( cDevice::GetDevice( LoadepgConfig.DeviceNumber - 1 )->CurrentChannel() );
    if( Channel )
    {
	return Channel->Number();
    }
    return 0;
}

bool cLoadepgOsd::SwitchToChannelId( int ChannelId )
{
    cChannel *Channel = Channels.GetByNumber( ChannelId );
    if( Channel )
    {
	//if( cDevice::PrimaryDevice()->SwitchChannel( Channel, true ) )
	if( cDevice::GetDevice( LoadepgConfig.DeviceNumber - 1 )->SwitchChannel( Channel, true ) )
	{
	    return true;
	}
	else
	{
	    fprintf( stderr, "error switch to channel %i\n", ChannelId );
	    return false;
	}
    }
    else
    {
	fprintf( stderr, "error switch to channel %i\n", ChannelId );
	return false;
    }
}

int cLoadepgOsd::NewChannel( char *Data )
{
    cChannel Channel;
    if( Channel.Parse( Data ) )
    {
	if( Channels.HasUniqueChannelID( &Channel ) )
	{
	    cChannel *NewChannel = new cChannel;
	    *NewChannel = Channel;
	    Channels.Add( NewChannel );
	    Channels.ReNumber();
	    Channels.SetModified( true );
	    return NewChannel->Number();
	}
	else
	{
	    fprintf( stderr, "epg channel is not unique\n" );
	    return 0;
	}
    }
    else
    {
	fprintf( stderr, "epg channel parsing error\n" );
	return 0;
    }
}

bool cLoadepgOsd::DelChannel( int ChannelId )
{
    cChannel *Channel = Channels.GetByNumber( ChannelId );
    if( Channel )
    {
	Channels.Del( Channel );
	Channels.ReNumber();
	Channels.SetModified( true );
	return true;
    }
    else
    {
	fprintf( stderr, "error delete channel %i\n", ChannelId );
	return false;
    }
}

cLoadepgSetup::cLoadepgSetup( void )
{
    Clear();
    Add( new cMenuEditBoolItem( tr( "Use file channels equivalences" ), &LoadepgConfig.UseFileEquivalences, tr( "No" ), tr( "Yes" ) ) );
    Add( new cMenuEditIntItem( tr( "DVB-S device for scanning" ), &LoadepgConfig.DeviceNumber, 1, cDevice::NumDevices() ) );
    Display();
}

void cLoadepgSetup::Store( void )
{
    SetupStore( "UseFileEquivalences", LoadepgConfig.UseFileEquivalences );
    SetupStore( "DeviceNumber", LoadepgConfig.DeviceNumber );
}

//-------------------------------------------------------------------------

cPluginLoadepg::cPluginLoadepg(void)
{
  // Initialize any member variables here.
  // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
  // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
  LoadepgConfig.UseFileEquivalences = false;
  LoadepgConfig.DeviceNumber = 1;
}

cPluginLoadepg::~cPluginLoadepg()
{
  // Clean up after yourself!
}

const char *cPluginLoadepg::CommandLineHelp(void)
{
  // Return a string that describes all known command line options.
  return NULL;
}

bool cPluginLoadepg::ProcessArgs(int argc, char *argv[])
{
  // Implement command line argument processing here if applicable.
  return true;
}

bool cPluginLoadepg::Initialize(void)
{
  // Initialize any background activities the plugin shall perform.
  return true;
}

bool cPluginLoadepg::Start(void)
{
  // Start any background activities the plugin shall perform.
  RegisterI18n( LoadepgPhrases );
  Status  = STATUS_NULL;
  return true;
}

void cPluginLoadepg::Housekeeping(void)
{
  // Perform any cleanup or other regular tasks.
}

cOsdObject *cPluginLoadepg::MainMenuAction(void)
{
  // Perform the action when selected from the main VDR menu.
  FILE *File;
  char Buffer[1024];
  char *Line;
  char *Dummy;
  char *Channel1;
  char *Channel2;
  char *SourceName1;
  char *SourceName2;
  int SourceId1, Nid1, Tid1, Sid1, Rid1;
  int SourceId2, Nid2, Tid2, Sid2, Rid2;
  EpgProviders = 0;
  asprintf( &Filename, "%s/%s", cPlugin::ConfigDirectory(), "loadepg.conf" );
  File = fopen( Filename, "r" );
  if( File )
  {
    while( ( Line = fgets( Buffer, sizeof( Buffer ), File ) ) != NULL )
    {
	if( ( strlen( Buffer ) - 1 ) >= 0 && Buffer[ strlen( Buffer ) - 1 ] == '\n' )
	{
	    Buffer[ strlen( Buffer ) - 1 ] = 0;
	}
	Line = compactspace( skipspace( stripspace( Line ) ) );
	if( ! isempty( Line ) )
	{
	    if( sscanf( Line, "PROVIDER=%a[^:]:%a[^\n]", &EpgProviderTitles[EpgProviders], &EpgProviderChannels[EpgProviders] ) == 2 )
	    {
		EpgProviderTitles[EpgProviders] = compactspace( skipspace( stripspace( EpgProviderTitles[EpgProviders] ) ) );
		EpgProviderChannels[EpgProviders] = compactspace( skipspace( stripspace( EpgProviderChannels[EpgProviders] ) ) );		asprintf( &EpgProviderChannels[EpgProviders], "LOADEPG_CHANNEL:%s:0:0:0:0:1234:1234:1234:1234", EpgProviderChannels[EpgProviders] );
		EpgProviders ++;
	    }
	}
    }
    fclose( File );
  }
  else
  {
    fprintf( stderr, "open file config \"%s\" failed\n", Filename );
    return NULL;
  }
  NumEpgEquivChannels = 0;

  if( LoadepgConfig.UseFileEquivalences == true )
  {
    asprintf( &Filename, "%s/%s", cPlugin::ConfigDirectory(), "loadepg.equiv" );
    File = fopen( Filename, "r" );
    if( File )
    {
	while( ( Line = fgets( Buffer, sizeof( Buffer ), File ) ) != NULL )
	{
	    if( ( strlen( Buffer ) - 1 ) >= 0 && Buffer[ strlen( Buffer ) - 1 ] == '\n' )
	    {
		Buffer[ strlen( Buffer ) - 1 ] = 0;
	    }
	    Line = compactspace( skipspace( stripspace( Line ) ) );
	    if( ! isempty( Line ) )
	    {
		if( sscanf( Line, "%a[^ ] %a[^ ] %a[^ ]\n", &Channel1, &Channel2, &Dummy ) == 3 )
		{
		    if( sscanf( Channel1, "%a[^-] -%i-%i-%i-%i", &SourceName1, &Nid1, &Tid1, &Sid1, &Rid1 ) >= 4 )
		    {
			SourceId1 = cSource::FromString( SourceName1 );
			tChannelID ChannelId1 = tChannelID( SourceId1, Nid1, Tid1, Sid1, Rid1 );
			cChannel *EpgChannel = Channels.GetByChannelID( ChannelId1, true );
			if( sscanf( Channel2, "%a[^-] -%i-%i-%i-%i", &SourceName2, &Nid2, &Tid2, &Sid2, &Rid2 ) >= 4 )
			{
			    SourceId2 = cSource::FromString( SourceName2 );
		    	    tChannelID ChannelId2 = tChannelID( SourceId2, Nid2, Tid2, Sid2, Rid2 );
			    cChannel *EpgEquivChannel = Channels.GetByChannelID( ChannelId2, true );
			    if( EpgChannel && EpgEquivChannel )
			    {
				snprintf( EpgEquivChannels[NumEpgEquivChannels][0], sizeof( EpgEquivChannels[NumEpgEquivChannels][0] ), "%s-%i-%i-%i-%i %s", SourceName1, EpgChannel->Nid(), EpgChannel->Tid(), EpgChannel->Sid(), EpgChannel->Rid(), EpgChannel->Name());
				snprintf( EpgEquivChannels[NumEpgEquivChannels][1], sizeof( EpgEquivChannels[NumEpgEquivChannels][0] ), "%s-%i-%i-%i-%i %s", SourceName1, EpgEquivChannel->Nid(), EpgEquivChannel->Tid(), EpgEquivChannel->Sid(), EpgEquivChannel->Rid(), EpgEquivChannel->Name());
				//fprintf( stderr, "%s-%i-%i-%i-%i %s\n", SourceName1, EpgEquivChannel->Nid(), EpgEquivChannel->Tid(), EpgEquivChannel->Sid(), EpgEquivChannel->Rid(), EpgEquivChannel->Name());
				NumEpgEquivChannels ++;
			    }
			    else
			    {
				fprintf( stderr, "error in loadepg.equiv for: %s\n", Line );
			    }
			}
		    }
		}
	    }
	}
	fclose( File );
    }
    else
    {
	fprintf( stderr, "open file equivalences \"%s\" failed\n", Filename );
	return NULL;
    }
  }
  return new cLoadepgOsd();
}

cMenuSetupPage *cPluginLoadepg::SetupMenu(void)
{
  // Return a setup menu in case the plugin supports one.
  return new cLoadepgSetup();
}

bool cPluginLoadepg::SetupParse(const char *Name, const char *Value)
{
  // Parse your own setup parameters and store their values.
  if( !strcasecmp( Name, "UseFileEquivalences" ) )
  {
    LoadepgConfig.UseFileEquivalences = atoi( Value );
  }
  else if( !strcasecmp( Name, "DeviceNumber" ) )
  {
    LoadepgConfig.DeviceNumber = atoi( Value );
  }
  else
  {
    return false;
  }
  return true;
}

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