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

#include <sys/ioctl.h>
//#include <stdio.h>
#include <vdr/plugin.h>
#include <vdr/channels.h>
#include <string>
#include <vdr/osd.h>
#include <vdr/sources.h>
#include <vdr/tools.h>

static const char *VERSION        = "0.0.1";
static const char *DESCRIPTION    = "CSF Channel Sort&Filter";
static const char *MAINMENUENTRY  = "CSF Channel Manager";
struct sSatelites	{
        	char nombre[50];
        	int codigo;	};
sSatelites  satelites[100];
int satelitesmax, posfiltro, posorden, postipofiltro;
bool mostrarfiltro;


// -- cCSF --------------------------------------------------------

class cCSF : public cOsdObject 
{
private:
	int canalesmax, posmenux,posmenuy,comando, colorFG, colorBG, tipofiltromax;
	cChannel *Channel1, *Channel2;
	bool tengoosd, teclaapretada, ascendente, problema, singrupos, af, ao;
	cOsd *osd;
	struct slista	{
        	char nombre[30];};
	slista  tipofiltro[20];
	slista tipoorden[20];
	
	
public:
	cCSF(void);
	~cCSF();
	virtual void Show(void);
	virtual eOSState ProcessKey(eKeys Key);
	void DisplayOnOsd(char buf[1024]);
	bool EliminarGrupos(void);
	bool OrdenarAlfabetico(void); 
	bool OrdenarAlfabeticoIns(void); 
	bool Filtrar(int arg);
	bool OrdenarSatFrec(void);
};

cCSF::cCSF(void)
{
	canalesmax=Channels.MaxNumber(); // numero de canales a procesar. para todos canales usar Channels.MaxNumber()
	teclaapretada=true;
	tengoosd=singrupos=false;
	posmenux=1;
	posmenuy=2;
	comando=99;
	tipofiltromax=1;
	af=ao=false;
	sprintf(tipofiltro[0].nombre,"Eliminar filtro");
	sprintf(tipofiltro[1].nombre,"     Source    ");
	sprintf(tipofiltro[2].nombre," Solo Video  ");
	sprintf(tipofiltro[3].nombre,"  Solo Radio  ");
	sprintf(tipoorden[0].nombre," No Ordenar ");
	sprintf(tipoorden[1].nombre,"      A~Z      ");
	sprintf(tipoorden[2].nombre,"      Z~A      ");
	sprintf(tipoorden[3].nombre,"  Sat/Frec    ");
	osd=NULL;
}

cCSF::~cCSF()
{
	delete osd;
}

void cCSF::Show(void)
{
	char texto[1024];
	int pos=posmenux*10+posmenuy;
	if (!tengoosd)
	{
		osd = cOsdProvider::NewOsd(40,50);
	        tArea Area[] =  {{0,0, 655, 80, 4},
			   	{ 0, 81,655,400, 2},
				{ 0, 401,655,470,2}};
		osd->SetAreas(Area, sizeof(Area) / sizeof(tArea));			
		osd->Flush();
		if(osd)	
		{
			sprintf(texto,"Channel Sort & Filter (CSF)  Plugin");
			osd->DrawRectangle(0,0,655,80,clrGray50);  // fondo titulo
			osd->DrawText(10, 3,texto,clrYellow,clrTransparent, cFont::GetFont(fontOsd)); // titulo
			osd->DrawRectangle(0,81,655,400,clrGray50); // fondo area botones total
			tengoosd=true;
		}
	}
	if(osd && teclaapretada)
 	{
		for (int i=0; i<=2; i++)
			for (int j=1; j<=1; j++)
				osd->DrawRectangle(5+205*j,128+80*i,205+205*j,163+80*i,clrBlack); // fondo botones
       		sprintf(texto,"Filtrar"); 
		osd->DrawText(10,130,texto,clrWhite, clrGray50,cFont::GetFont(fontOsd)); // Filtrar
       		sprintf(texto,"Ordenar"); 
		osd->DrawText(10,210,texto,clrWhite, clrGray50,cFont::GetFont(fontOsd)); // Ordenar
		sprintf(texto,"NADA"); 
		osd->DrawText(10,290,texto,clrWhite, clrGray50,cFont::GetFont(fontOsd)); // 
		sprintf(texto,"Salir"); 
		osd->DrawText(210,290,texto,clrWhite, clrBlack,cFont::GetFont(fontOsd)); // Salir
		osd->DrawText(210,130,tipofiltro[posfiltro].nombre,clrWhite, clrBlack,cFont::GetFont(fontOsd)); // que filtro tiene
		osd->DrawRectangle(410,128,650,163,clrGray50);           // fondo area tipofiltro 
		if  (mostrarfiltro)
		{
				sprintf(texto,"<- %s ->",satelites[postipofiltro].nombre); 
				osd->DrawText(410,130,texto,clrWhite, clrGray50,cFont::GetFont(fontOsd)); // tipo filtro aplicado
		}
		osd->DrawText(210,210,tipoorden[posorden].nombre,clrWhite, clrBlack,cFont::GetFont(fontOsd)); // que orden tiene
       		if (posmenux<2)
			osd->DrawRectangle(5+205*posmenux,128+80*posmenuy,205+205*posmenux,163+80*posmenuy,clrCyan); // fondo boton seleccionado
		else
			osd->DrawRectangle(410,128,650,163,clrCyan);                                                                                  // fondo filtro activo
		switch (pos)
		{
			case 0:
	        		sprintf(texto,"Filtrar"); 
				osd->DrawText(10,130,texto,clrBlack, clrCyan,cFont::GetFont(fontOsd)); // boton filtrar
				break;
			case 1:
		       		sprintf(texto,"Ordenar"); 
				osd->DrawText(10,210,texto,clrBlack, clrCyan,cFont::GetFont(fontOsd)); // boton ordenar
				break;
			case 2:
				sprintf(texto," "); 
				osd->DrawText(10,290,texto,clrBlack, clrCyan,cFont::GetFont(fontOsd)); // boton no usado
				break;
			case 10:
				sprintf(texto,"<- %s ->",tipofiltro[posfiltro].nombre);
				osd->DrawText(210,130,texto,clrBlack, clrCyan,cFont::GetFont(fontOsd)); // que filtro tiene
				break;
			case 11:
				sprintf(texto,"<- %s ->",tipoorden[posorden].nombre);
				osd->DrawText(210,210,texto,clrBlack, clrCyan,cFont::GetFont(fontOsd)); // que orden tiene
				break;
			case 12:
				sprintf(texto,"Salir"); 
				osd->DrawText(210,290,texto,clrBlack, clrCyan,cFont::GetFont(fontOsd)); // boton salir
				break;
			case 20:
				sprintf(texto,"<- %s ->",satelites[postipofiltro].nombre); 
				osd->DrawText(410,130,texto,clrBlack, clrCyan,cFont::GetFont(fontOsd)); // boton tipo filtro aplicado
				break;
		}
		teclaapretada=false;				
		osd->Flush();
	}	
}


eOSState cCSF::ProcessKey(eKeys Key)
{
  comando=99;
  int pos=posmenux*10+posmenuy;
  af=ao=false;
  eOSState state = cOsdObject::ProcessKey(Key);
	if (state == osUnknown) 
	{
		switch(Key) 
		{
			case kUp:
        			if (posmenuy>0)
                			posmenuy--;
				else
					posmenuy=2;
				teclaapretada=true;
		                break;
			case kDown: 
        			if (posmenuy<2)                             // 2 = num opciones verticales -1
	              			posmenuy++;
	        	        else
					posmenuy=0; 
				teclaapretada=true;
				break;
			case kRight:
				teclaapretada=true;
				switch (pos)
				{
					case 10:
						if (posfiltro<3)
							posfiltro++;
						else
							posfiltro=0;
						af=true;
						break;
					case 11:
						if (posorden<3)
							posorden++;
						else
							posorden=0; 
						ao=true;                            
	              				break;
					case 12:
						teclaapretada=false;
						break;
					case 20:
						if (postipofiltro<=satelitesmax)
							postipofiltro++;
						else
							postipofiltro=0;
						af=true;
						break;
				}
				break;
			case kLeft:
				teclaapretada=true;
				switch (pos)
				{
					case 10:
						if (posfiltro>0)
							posfiltro--;
						else
							posfiltro=3;
						af=true;
						break;
				
					case 11:
						if (posorden>0)
							posorden--;
						else
							posorden=3; 
						ao=true;                            
	              				break;
					case 12:
						teclaapretada=false;
					case 20:
						if (postipofiltro>0)
							postipofiltro--;
						else
							postipofiltro=satelitesmax;
						af=true;
						break;
				}
				break;
	    		case kOk:
        	     		comando = 10*posmenux+posmenuy;             // comando es 11~14 21~24 31~34
				break;
			case kOk|k_Repeat: 
        	        	break;
	    		case kBack: 
				if (posmenux!=2)
        	        		return osEnd;
				else
				{
					teclaapretada=true;
					posmenux=1;
				}
				break;
		        default:
        		        Show();
                		return state;
    		}
		switch (comando)
		{		
			case 10:
				switch (posfiltro)
				{
					case 0:
						problema=Filtrar(0);
						mostrarfiltro=false;
						postipofiltro=0;
						break;
					case 1:
						posmenux=2;
						mostrarfiltro=true;
						break;
					case 2:
						problema=Filtrar(0);
						mostrarfiltro=true;
						postipofiltro=0;
						break;
					case 3:
						problema=Filtrar(0);
						mostrarfiltro=true;
						postipofiltro=0;
						break;
				}
				teclaapretada=true;
				Show();
				comando=99;
				break;
			case 11:
				switch (posorden)
				{
					case 0:	
						system("cp /video/channels.conf.csf /video/channels.conf");	
						problema=Channels.Load("/video/channels.conf.csf", false, true);       
						break;
					case 1:
						ascendente=true;
					  	problema= EliminarGrupos();
			  			problema= OrdenarAlfabetico();
						break;
					case 2:
						ascendente=false;
					  	problema= EliminarGrupos();
					  	problema= OrdenarAlfabetico();
						break;
					case 3:
						problema=EliminarGrupos();
						problema=OrdenarSatFrec();
						break;
				}
				comando=99;
				break;
			case 12:
		        	return osEnd;	
				break;
			case 20:
				int source=satelites[postipofiltro].codigo;
				problema=EliminarGrupos();
				problema=Filtrar(source);
				posmenux=1;
				teclaapretada=true;
				Show();
				comando=99;
				break;
		}
	}
	Show();
	return state;
}

void cCSF::DisplayOnOsd(char buf[1024])
{
     	if(osd)
      	{
        	osd->DrawRectangle(0,401,655,470,clrGray50);
		osd->DrawText(10, 403,buf,clrWhite,clrGray50, cFont::GetFont(fontOsd));
		osd->Flush();
	} 
} 

bool cCSF::EliminarGrupos(void)  //Elimina todos los grupos
{
int grupo=0;
if (singrupos)
	return false;
fprintf(stderr, "CSF: Eliminado Grupos\n");
for (int i=1; i<100; i++)
	if (grupo>=0)
	{
		int GuardarGrupo =grupo;
		grupo = Channels.GetNextGroup(grupo) ;
            	if (grupo < 0)
			grupo=GuardarGrupo;
               	Channel1 = Channels.Get(grupo);
            	if (Channel1) 
		{
               		if (!Channel1->GroupSep())
                  		grupo = -1;
			else
			{
				Channels.Del(Channel1);
				Channels.ReNumber();
			grupo=0;
			}
               	}
        }
singrupos=true;
return false;  // ToDo poner codigo para control de errores
}

bool cCSF::OrdenarAlfabetico(void) //Ordeno nombre Alfabeticamente  por bubble
{
	int bandera =1;
	int indice1 =1;
	int indice2=1;
	char texto[1024];
	char texto2[100];
	std::string nombre1;
	std::string nombre2;

	fprintf(stderr, "CSF: Iniciando Orden Alfabetico Bubble \n");
	for (int i=1; bandera && (i<=50000);i++ ) // Como si fuera un while bandera
	{
		bandera=0;
		for (int contador=1; contador<canalesmax; contador++) 
		{
      			indice1=contador;
			Channel1 = Channels.GetByNumber(indice1,0);
		     	indice2=indice1+1;
			Channel2 = Channels.GetByNumber(indice2,0);
			nombre1=Channel1->Name();
			nombre2=Channel2->Name();
	//		fprintf(stderr,"CSF: %d %s         %d %s \n",indice1, Channel1->Name(), indice2, Channel2->Name() );
	    		if (((nombre1 > nombre2)&&ascendente) || ((nombre1 < nombre2)&& !ascendente))
		    	{
				Channels.Move(indice2-1,indice1-1);
				Channel1->SetNumber(indice2);
				Channel2->SetNumber(indice1);
				bandera=1;
			}
		}
	}
	if (bandera==0)
	{
		if (ascendente)
			sprintf(texto2, "Ascendente %d canales",canalesmax);
		else
			sprintf(texto2, "Descendente %d canales",canalesmax);
		sprintf(texto,"Ordenado Alfabeticamente %s",texto2);
		DisplayOnOsd(texto);
		fprintf(stderr,"CSF: %d canales Ordenados Terminado\n",canalesmax);
		Channels.ReNumber();	
	}
	else
	{
		sprintf(texto,"Error, no se pudo Ordenar");
		DisplayOnOsd(texto);
	}
	if (bandera==0)
		return false;
	else
		return true;
}

bool cCSF::OrdenarAlfabeticoIns(void) //Ordeno nombre Alfabeticamente  por insercion
{
	int mejor=1;
	char texto[1024];
	char texto2[100];
	std::string nombre1;
	std::string nombre2;

	fprintf(stderr, "CSF: Iniciando Orden Alfabetico por insercion\n");
	for (int i=1; (i<canalesmax-1);i++ ) // 
	{
		mejor=i;
		for (int j=i; j<=canalesmax; j++) 
		{
			Channel1 = Channels.GetByNumber(mejor,0);
			Channel2 = Channels.GetByNumber(j,0);
			nombre1=Channel1->Name();
			nombre2=Channel2->Name();
	    		if (((nombre1 > nombre2)&&ascendente) || ((nombre1 < nombre2)&& !ascendente))
		    		mejor=j;
		}
		if (mejor!=i)
		{
			Channels.Move(mejor-1,i-1);
			Channels.Move(i, mejor-1);
				Channel1->SetNumber(mejor);
				Channel2->SetNumber(i);

//				Channels.ReNumber();
		}
	}
	if (ascendente)
		sprintf(texto2, "Ascendente %d canales",canalesmax);
	else
		sprintf(texto2, "Descendente %d canales",canalesmax);
	sprintf(texto,"Ordenado Alfabeticamente %s",texto2);
	DisplayOnOsd(texto);
	fprintf(stderr,"CSF: %d canales Ordenados Terminado\n",canalesmax);	
	Channels.ReNumber();
	return true;
}

bool cCSF::OrdenarSatFrec(void) //Ordeno nombre Alfabeticamente  por bubble
{
	int bandera =1;
	int indice1 =1;
	int indice2=1;
	int tp1, tp2;
	char texto [256];

	fprintf(stderr, "CSF: Iniciando Orden por Source -> Frecuencia \n");
	for (int i=1; bandera && (i<=50000);i++ ) // Como si fuera un while bandera
	{
		bandera=0;
		for (int contador=1; contador<canalesmax; contador++) 
		{
      			indice1=contador;
			Channel1 = Channels.GetByNumber(indice1,0);
		     	indice2=indice1+1;
			Channel2 = Channels.GetByNumber(indice2,0);
			tp1=Channel1->Transponder();
			tp2=Channel2->Transponder();
		//	fprintf(stderr,"CSF: %d %d %s         %d %d %s \n",indice1, tp1, Channel1->Name(), indice2, tp2, Channel2->Name() );
	    		if (tp1 > tp2)
		    	{
				Channels.Move(indice2-1,indice1-1);
				Channel1->SetNumber(indice2);
				Channel2->SetNumber(indice1);
				bandera=1;
			}
		}
	}
	if (bandera==0)
	{
		sprintf(texto,"Ordenado por Transponder %d canales",canalesmax);
		DisplayOnOsd(texto);
		fprintf(stderr,"CSF: %d canales Ordenados Terminado\n",canalesmax);
		Channels.ReNumber();	
	}
	else
	{
		sprintf(texto,"Error, no se pudo Ordenar");
		DisplayOnOsd(texto);
	}
	if (bandera==0)
		return false;
	else
		return true;
}

bool cCSF::Filtrar(int arg)      //filtro lista canales
{
	char texto[1024];
	int prefiltro=canalesmax;
	std::string nombre1;
	fprintf(stderr, "CSF: Iniciando filtrado\n");
	switch (posfiltro)
	{
		case 1:										//satelite
			for (int j=1; j<=canalesmax; j++)
			{	
				Channel1=Channels.GetByNumber(j,0);
				if (Channel1->Source()!=arg)
					Channels.Del(Channel1);
			}
			Channels.ReNumber();
			break;
		case 2:										// solo video
			for (int j=1; j<=canalesmax; j++)
			{	
				Channel1=Channels.GetByNumber(j,0);
				if (Channel1->Vpid()==0)
					Channels.Del(Channel1);
			}
			Channels.ReNumber();
			break;
		case 3:	
			for (int j=1; j<=canalesmax; j++)
			{	
				Channel1=Channels.GetByNumber(j,0);
				if (Channel1->Vpid()>0)
					Channels.Del(Channel1);
			}
			Channels.ReNumber();
			break;									//idioma
			break;
		case 5:										//alfabetico
			break;
		case 6:										//por CA
			break;
		case 0:										// mostrar todos
			system("cp /video/channels.conf.csf /video/channels.conf");
			problema=Channels.Load("/video/channels.conf", false, true);               
			mostrarfiltro=false;									
			break;
	}
	canalesmax=Channels.MaxNumber(); // numero de canales a procesar. para todos canales usar Channels.MaxNumber()
	fprintf(stderr,"CSF: Filtrado %d canales -> %d canales Terminado\n",prefiltro, canalesmax);
	sprintf(texto, "Filtrado %d canales -> %d canales",prefiltro, canalesmax);
	DisplayOnOsd(texto);
	return false;
}


// --- cPlugincsf ---------------------------------------------------------

class cPlugincsf : public cPlugin {
private:
  // Add any member variables or functions you may need here.
  	
public:
  cPlugincsf(void);
  virtual ~cPlugincsf();
  virtual const char *Version(void) { return VERSION; }
  virtual const char *Description(void) { return tr(DESCRIPTION); }
  virtual const char *CommandLineHelp(void);
  virtual bool ProcessArgs(int argc, char *argv[]);
  virtual bool Initialize(void);
  virtual bool Start(void);
  virtual void Housekeeping(void);
  virtual const char *MainMenuEntry(void) { return tr(MAINMENUENTRY); }
  virtual cOsdObject *MainMenuAction(void);
  virtual cMenuSetupPage *SetupMenu(void);
  virtual bool SetupParse(const char *Name, const char *Value);
};

cPlugincsf::cPlugincsf(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!
}

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

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

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

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

bool cPlugincsf::Start(void)   			
{
  // Start any background activities the plugin shall perform.
	posfiltro=posorden=postipofiltro=0;
	const cSource *source;
  	source=Sources.First();
  	satelitesmax=1;
	sprintf(satelites[0].nombre,"Mostrar Todos");
//	system("cp /video/channels.conf /video/channels.conf.csf");	
	while (true) 
	{
 		snprintf(satelites[satelitesmax].nombre, sizeof(satelites[satelitesmax].nombre),"%s",source->Description());   //tambien puedo usar source->Description() que es string cSource::ToString(source->Code()
                satelites[satelitesmax].codigo=source->Code();
		if (source->Next())
          		source = (cSource *)source->Next();
         	 else break;
		 satelitesmax++;
		 mostrarfiltro=false;
      	}
	return true;
}

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

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

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

bool cPlugincsf::SetupParse(const char *Name, const char *Value)
{
  // Parse your own setup parameters and store their values.
  return true;
}

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

