/*  NXMMS2 - Ncurses client for XMMS2 audio player
 *  Copyright (C) 2006 by Marko Kotar
 *
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *  
 *  If you have any questions about the extending this program contact me on E-Mail:
 *  Kotarmarko@yahoo.com
 *  
 *  Some functions described in help don't work yet.
 */

#include <curses.h>
#include "xmmsclient/xmmsclient.h"
 #include <unistd.h>

int globalScreenLock;
int keyLock;
#define LOCKSCREEN while(globalScreenLock);globalScreenLock=1
#define UNLOCKSCREEN globalScreenLock=0
#define KEYLOCK keyLock
#define LOCKKEY keyLock=1
#define UNLOCKKEY keyLock=0
uint32_t vol;
int oldProgressPerc;


//Screen positions:
int posSong;
int posSongLen;
int posSongTotalTime;
int posSongTime;
int posPlayList;
int posPlayListLen;
int posProgress;
int posProgressLen;
int posPlayStatus;
struct song
{
	uint id;
	uint duration;
	char* title;
	char* url;
	char* artist;
	char* album;
	char* genre;
};

uint old_id;
struct song skl;
uint oldStatus;



uint getCurrentSongID(xmmsc_connection_t *c);
struct song  getSongInfo(xmmsc_connection_t *c,uint id);
uint getCurrentPlayPos(xmmsc_connection_t *c);
void initPos();
void titleLoop(xmmsc_connection_t *c);
void keyboardHandler(xmmsc_connection_t *c);
void printPlayList(xmmsc_connection_t *c);
void printDisp(char* d,int len);
void printProgress(uint pos,uint duration);
uint getStatus(xmmsc_connection_t *c);
void menuAdd(xmmsc_connection_t *c);
char ** getKeys(xmmsc_connection_t *c);
int menuAdd1(int* pi , int * psp,char **keys,int size, char * s, xmmsc_connection_t *c);
void menuAddSearch(char * value, char ** keys, int keyi,xmmsc_connection_t *c);
void destroyKeys(char** cl);
/*
struct xmmsc_connection_St {
        int ref;
        xmmsc_ipc_t *ipc;
        x_list_t *callbacks;
        x_list_t *replies;
        char *error;
        int timeout;
        void *data;
        uint32_t cookie;
        char *clientname;
};
*/
void reInitScreen()
{
	curs_set(0);
	clear();
	old_id=old_id+1;
        int i;
        for (i=0;i<posProgressLen;i++)
        {
                move(1,i+posProgress);
                addch('-');
        }
	oldStatus=oldStatus+1;
	move(LINES-1,0);
	printw("For help press F1");
}


int main(int argn, char ** argv)
{
	
	oldProgressPerc=0;
	UNLOCKSCREEN;
	LOCKSCREEN;
	initscr();
	keypad(stdscr, TRUE);
	nonl();
	cbreak();
	noecho();
	curs_set(0);
	if (has_colors())
	{
		start_color();
		
	        
		init_pair(1, COLOR_WHITE, COLOR_BLACK);
	        init_pair(COLOR_GREEN, COLOR_GREEN, COLOR_BLACK);
	        init_pair(COLOR_RED, COLOR_RED, COLOR_BLACK);
	        init_pair(COLOR_CYAN, COLOR_CYAN, COLOR_BLACK);
	        init_pair(COLOR_WHITE, COLOR_WHITE, COLOR_BLACK);
	        init_pair(COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLACK);
		init_pair(COLOR_BLUE, COLOR_BLUE, COLOR_BLACK);
		init_pair(COLOR_YELLOW, COLOR_YELLOW, COLOR_BLACK);
		
	}
	initPos();
	reInitScreen();
	UNLOCKSCREEN;
	vol=100;
	xmmsc_connection_t * connection=xmmsc_init("NcursesXmmsClient0Test");
	char* c=NULL;
	if (argn>1)
		c=argv[1];
	xmmsc_connect(connection,c);
	titleLoop(connection);
	/*
	while(1)
	{
		
		struct song so;
		uint a=getCurrentSongID(connection);
		so=getSongInfo(connection,a);
		printf("%d %d %s\n",getCurrentPlayPos(connection),so.duration,so.title);
		free(so.title);
		//free(so);
		sleep(1);
		
	}
	*/
	
	refresh();
	while (1);
	
	endwin();
	
	return(0);
}

uint getCurrentPlayPos(xmmsc_connection_t *c)
{
	xmmsc_result_t * rez=xmmsc_playback_playtime (c);
	xmmsc_result_wait(rez);
	uint r=0;
	xmmsc_result_get_uint (rez,&r);
	//xmmsc_result_free(rez);
	return r;
}
struct song  getSongInfo(xmmsc_connection_t *c,uint id)
{
	
        xmmsc_result_t * res=xmmsc_medialib_get_info (c,id);
        xmmsc_result_wait(res);
        
	struct song  s;
	s.title=malloc(sizeof(char)*1024);
	s.url=malloc(sizeof(char)*1024);
	s.artist=malloc(sizeof(char)*1024);
	s.album=malloc(sizeof(char)*1024);
	s.genre=malloc(sizeof(char)*1024);
	

	s.title[0]='\0';
	s.url[0]='\0';
	s.artist[0]='\0';
	s.album[0]='\0';
	s.genre[0]='\0';
	//xmmsc_entry_format (s.title, 255,"${author}", rez);
	
	xmmsc_result_get_dict_entry_int32 (res, "duration", &(s.duration));
	s.duration=s.duration/1000;
	char*str;
	if(xmmsc_result_get_dict_entry_str(res, "title", &str))
		strcpy(s.title,str);
	if(xmmsc_result_get_dict_entry_str(res, "url", &str))
		strcpy(s.url,str);
	if(xmmsc_result_get_dict_entry_str(res, "artist", &str))
		strcpy(s.artist,str);
	if(xmmsc_result_get_dict_entry_str(res, "album", &str))
		strcpy(s.album,str);
	if(xmmsc_result_get_dict_entry_str(res, "genre", &str))
		strcpy(s.genre,str);
	s.id=id;
	
	xmmsc_result_unref(res);
	
        return s;
}
void deleteSong(struct song s)
{
	free(s.title);
	free(s.url);
	free(s.artist);
	free(s.album);
	free(s.genre);
}
uint getCurrentSongID(xmmsc_connection_t *c)
{
        xmmsc_result_t * rez=xmmsc_playback_current_id (c);
        xmmsc_result_wait(rez);
        uint r=0;
        xmmsc_result_get_uint (rez,&r);
        //xmmsc_result_free(rez);
        return r;
}

uint getSongList(xmmsc_connection_t *c)
{
	
}
void initPos()
{
	        posSongTime=COLS-19;
		posSongTotalTime=COLS-10;
		posSong=0;
		posSongLen=COLS-19;
		posPlayList=2;
		posPlayListLen=LINES-3;
		posProgress=10;
		posProgressLen=COLS-10;
		posPlayStatus=0;
}

void titleLoop(xmmsc_connection_t *c)
{
	
	{
		while(1)
		{
			//struct song s;
			uint pos=getCurrentPlayPos(c)/1000;
			uint id=getCurrentSongID(c);
			char str[1024];
			uint status=getStatus(c);
			if (status==0)
				pos=0;
			sprintf(str,"%02d:%02d:%02d",pos/3600,pos/60%3600,pos%60);
			if (id!=old_id)
			{
				deleteSong(skl);
				skl= getSongInfo(c,id);
			}
			LOCKSCREEN;
			
			move(0,posSongTime);
			printw(str);
			if (id!=old_id)
			{
				sprintf(str,"Genre:%s    %s: %s",skl.genre,skl.artist,skl.title);
				move(0,posSong);
			 	printDisp(str,posSongLen);
			 	move(0,posSongTotalTime);
			 	sprintf(str,"%02d:%02d:%02d",skl.duration/3600,skl.duration/60%3600,skl.duration%60);
			 	printw(str);
				printPlayList(c);
				old_id=id;
			}
			move(1,posPlayStatus);
			if (status!=oldStatus)
			{
				switch (status)
				{
					case 0 :
						printw("Stoped ");
						break;
					case 1	:
						printw("Playing");
						break;
					case 2 :
						printw("Paused ");
						break;
					default:
						printw("Unknown");
						break;
				}
				oldStatus=status;
			}
			printProgress(pos,skl.duration);
			refresh();
			UNLOCKSCREEN;	
			keyboardHandler(c);
			
			//sleep(1);
		}
	}
}
void printProgress(uint pos,uint duration)
{
	int perc=((posProgressLen-1)*pos)/duration;
	
	if (oldProgressPerc==perc)
		return;
	move(1,posProgress+oldProgressPerc);
	addch('-');
	oldProgressPerc=perc;
	int i;
	move(1,posProgress+perc);
	addch((char)173);
}
uint getStatus(xmmsc_connection_t *c)
{
	uint r=0;
	xmmsc_result_t *res=xmmsc_playback_status (c);
	xmmsc_result_wait (res);
	xmmsc_result_get_uint (res,&r);
	xmmsc_result_unref (res);
	return r;
}
void keyboardHandler(xmmsc_connection_t *c)
{
		
			if (!KEYLOCK)
			{
				LOCKKEY;
				
				halfdelay(5);
				int co = getch();
				xmmsc_result_t * res;
				switch (co)
				{
					case KEY_RIGHT : 
						res=xmmsc_playback_seek_ms_rel(c,10000);
						xmmsc_result_wait (res);
						xmmsc_result_unref (res);
						
						break;
					case KEY_LEFT :
						res=xmmsc_playback_seek_ms_rel(c,-10000);
						xmmsc_result_wait (res);
						xmmsc_result_unref (res);
						
						break;
					case KEY_DOWN :
						res=xmmsc_playlist_set_next_rel (c, 1);
						xmmsc_result_wait (res);
						xmmsc_result_unref (res);
						res=xmmsc_playback_tickle (c);
						xmmsc_result_wait (res);
						xmmsc_result_unref (res);
						break;
					case KEY_UP :
                                               res=xmmsc_playlist_set_next_rel (c, -1);
                                               xmmsc_result_wait (res);
                                               xmmsc_result_unref (res);
                                               res=xmmsc_playback_tickle (c);
                                               xmmsc_result_unref (res);
					       break;
					case 'P' :
					case 'p' :
					       res=xmmsc_playback_start (c);
					       xmmsc_result_wait (res);
					       xmmsc_result_unref (res);
					       break;
					case ' ' :
					        res=xmmsc_playback_pause (c);
						xmmsc_result_wait (res);
						xmmsc_result_unref (res);
						break;
					case 's' :
					case 'S' :
						res=xmmsc_playback_stop (c);
						xmmsc_result_wait (res);
						xmmsc_result_unref (res);
						break;
					case 'q' :
					case 'Q' :
						clear();
						move(0,0);
						printw("Are you sure to stop music server(Y/N)?");
						cbreak();
						int ch=getch();
						if (ch!='y'&&ch!='Y')
						{
							reInitScreen();
							break;
						}
						res=xmmsc_quit(c);
						xmmsc_result_wait (res);
						xmmsc_result_unref (res);
						//countinue with exit
					case 'x' :
					case 'X' :
					case 27  :
						endwin();
						exit(0);
					case '+' :
						vol++;
						if (vol>100)
							vol=100;
						res=xmmsc_playback_volume_set(c,"left",vol);
						xmmsc_result_wait (res);
						xmmsc_result_unref (res);
						res=xmmsc_playback_volume_set(c,"right",vol);
						xmmsc_result_wait (res);
						xmmsc_result_unref (res);
						break;
					case '-' :
                                                vol--;
						if (vol<0)
							vol=0;
                                                res=xmmsc_playback_volume_set(c,"left",vol);
                                                xmmsc_result_wait (res);
                                                xmmsc_result_unref (res);
                                                res=xmmsc_playback_volume_set(c,"right",vol);
                                                xmmsc_result_wait (res);
                                                xmmsc_result_unref (res);
						break;																																  
					case KEY_NPAGE :
                                                res=xmmsc_playlist_set_next_rel (c, 10);
                                                xmmsc_result_wait (res);
                                                xmmsc_result_unref (res);
                                                res=xmmsc_playback_tickle (c);
                                                xmmsc_result_wait (res);
                                                xmmsc_result_unref (res);
                                                break;
																
					case KEY_PPAGE :
                                                res=xmmsc_playlist_set_next_rel (c, -10);
                                                xmmsc_result_wait (res);
                                                xmmsc_result_unref (res);
                                                res=xmmsc_playback_tickle (c);
                                                xmmsc_result_wait (res);
                                                xmmsc_result_unref (res);
                                                break;
					case 'a' :
					case 'A' :
						menuAdd(c);
						reInitScreen();
						break;
					case KEY_F(1) :
						clear();
						move(0,0);
						printw("To control player use following keys:");
						move(1,1);
						printw("F1  -- Displays This Help");
						move(2,1);
						printw("Esc,X -- Quits This Client Program");
						move(3,1);
						printw("Left, Right Arrow -- Seeks in Currently Playing Dong");
						move(4,1);
						printw("Up, Down Arrow -- Moves to Previous , Next Song");
						move(5,1);
						printw("PageUP, PageDown -- Moves 10 Songs Backward, Forward");
						move(6,1);
						printw("P -- Change State to Playing");
						move(7,1);
						printw("Space -- Change State to Pause");
						move(8,1);
						printw("S -- Change State to Stop");
						move(9,1);
						printw("A -- Opens Media Library Search");
						move(10,1);
						printw("C -- Clears Current Playlist");
						move(11,1);
						printw("W -- Writes Current PlayList to Library");
						move(12,1);
						printw("R -- Restores Playlist from Library");
						move(13,1);
						printw("+/- -- Changes Volume");
						move(14,0);
						printw("In Media Library Search use Up and Down Arrow to select Search Attribute and input Search String at the same time.");
						move(15,0);
						printw("In Search Results List use [A] to add all songs and [Enter] to add selected song");
						move(16,0);
						printw("Press any Key to Continue");
						cbreak();
						getch();
						reInitScreen();
				}
				
				UNLOCKKEY;
		
	
			}
	
}
void clearToPos(int p)
{
	while(0);
}
void printPlayList(xmmsc_connection_t *c)
{
	uint pos;
	xmmsc_result_t * res=xmmsc_playlist_current_pos (c);
	xmmsc_result_wait (res);
	xmmsc_result_get_uint (res, &pos);
	xmmsc_result_unref (res);

	
	res=xmmsc_playlist_list(c);
	xmmsc_result_wait (res);
	char ** plt=(char**) malloc(sizeof(char*)*posPlayListLen);
	int i;
	for (i=0;i<posPlayListLen;i++)
	{
		plt[i]=malloc(sizeof(char*));
		*(plt[i])='\0';
		//printw("delam");
		//refresh();
	}
	i=0;
	int ii=-1;
	int start=0;
	xmmsc_result_list_first(res);
	while(xmmsc_result_list_valid (res))
	{
		if (ii>=posPlayListLen/2-posPlayListLen%2)
			break;
		uint ui;
		xmmsc_result_get_uint (res, &ui);
		
		struct song s=getSongInfo(c,ui);
		
		free(plt[i%posPlayListLen]);
		
		plt[i%posPlayListLen]=(char*)malloc(sizeof(char)*1024);
		sprintf(*(plt+i%posPlayListLen),"|%s  %s: %s (%02d:%02d:%02d)",s.genre,s.artist,s.title,s.duration/3600,(s.duration/60)%60,s.duration%60);
		deleteSong(s);
		if (i==pos||ii>=0)
		{	
			ii++;
			if (i==pos)
				start=i%posPlayListLen;
		}
		i++;
		xmmsc_result_list_next (res);
	}
	
	for (ii=ii;ii<posPlayListLen/2;ii++)
	{
		free(plt[i%posPlayListLen]);
		plt[i%posPlayListLen]=(char*)malloc(sizeof(char));
		*(plt[i%posPlayListLen])='\0';
		i++;
	}

	//int start=0;
	int scrpos=posPlayListLen/2+posPlayListLen%2;
	

	for (i=start+posPlayListLen;i>start+posPlayListLen/2;i--)
	{
		int ai=i%posPlayListLen;
                move(posPlayList+scrpos,1);
		printDisp(plt[ai],COLS-2);
                free(plt[ai]);
                scrpos--;
	}
 	scrpos=posPlayListLen/2+1+posPlayListLen%2;
	for (i=start+1;i<start+posPlayListLen/2;i++)
	{	
		int ai=i%posPlayListLen;
		move(posPlayList+scrpos,1);
		printDisp(plt[ai],COLS-2);
		free(plt[ai]);
		scrpos++;
		
	}
	move(posPlayListLen%2+posPlayListLen/2+posPlayList,0);
	addch((char)173);
	//printw("");
	/*
        for (ii=0;ii<i%posPlayListLen;ii++)
        {
                move(ii+posPlayList,1);
                printw(plt[ii]);
                free(plt[ii]);
                move(ii+posPlayList,0);
                if (ii==star)
                        printw("*");
                else
                        printw(" ");
        }
	*/	
	free(plt);
	xmmsc_result_unref (res);
}
void printDisp(char* d,int len)
{
	int i;
	for (i=0;i<len&&d[i]!='\0';i++)
		addch(d[i]);
	for (i=i;i<len;i++)
		addch(' ');
}
void menuAdd(xmmsc_connection_t *c)
{
	int ii=0;
	int sp=0;
	char s[256];
	int i;
	int size;
	char ** keys=getKeys(c);
	do
	{
		clear();
		
		move(0,0);
		printw("Search word:");
		move(1,0);
		printw("Attribute to search:");
		cbreak();
		for (i=0;i<255;i++)
			s[i]=' ';
	
		s[255]='\0';
	
		for (i=0;keys[i]!=NULL;i++)
		{
			move(2+i,1);
			printw(keys[i]);
		}
		move(2+i,1);
		printw("Any Attribute");
		size=i;
		move(2,0);
		addch((char)173);
	}
	while(menuAdd1(&ii,&sp,keys,size,s,c));
	destroyKeys(keys);
	
}
int menuAdd1(int* pi , int * psp,char **keys,int size, char * s, xmmsc_connection_t *c)
{
	int i=*pi;
	int sp=*psp;
	int ii;
	while(1)
	{
		move(0,12+sp);
		curs_set(1);
		cbreak();
		int ch=getch();
		switch (ch)
		{
			case KEY_ENTER :
			case 13 :
				s[sp]='\0';
				menuAddSearch(s,keys,i,c);
				s[sp]=' ';
				return 1;
			case KEY_CANCEL:
			case 27 :
				return 0;
			case KEY_UP:
				move(2+i,0);
				addch(' ');
				i--;
				if (i<0)
					i=size;
				move(2+i,0);
				addch((char)173);
				break;
			case KEY_DOWN:
				move(2+i,0);
				addch(' ');
				i++;
				if (i>size)
					i=0;
				move(2+i,0);
				addch((char)173);
				break;
			case KEY_LEFT:
				sp--;
				if (sp<0)
					sp=0;
				break;
			case KEY_RIGHT:
				sp++;
				if (sp>255)
					sp=255;
				break;
			case KEY_BACKSPACE:
			case 8 :
				if (sp>0)
					sp--;
			case KEY_DC :   //del key
			case 127 :
				for(ii=sp;ii<254;ii++)
					s[ii]=s[ii+1];
				s[ii]=' ';
				break;
			default:
				s[sp]=(char)ch;
				sp++;
				if (sp>255)
					sp=255;
				move(0,12);
				printDisp(s,COLS-12);
		}
		refresh();
	}
	*pi=i;
	*psp=sp;
	return 1;
	
}
void menuAddSearch(char * value, char ** keys, int keyi,xmmsc_connection_t *c)
{
	clear();
	char str[256];
	move(1,0);
	printw("Searched Attribute:");
	if (keys[keyi]!=NULL)
	{
		sprintf(str,"select distinct id from media where key=\'%s\' and value like \'%%%s%%\';",keys[keyi],value);
		printw(keys[keyi]);
	}
	else
	{
		sprintf(str,"select distinct id from media where value like '%%%s%%';",value);
		printw("Any Attribute");
	}
	//FILE* debug=fopen("debug.txt","w");
	//fprintf(debug,"%s\n",str);
	//fclose(debug);

	xmmsc_result_t * res = xmmsc_medialib_select (c,str);
	xmmsc_result_wait(res);
	long size=0;
	while (xmmsc_result_list_valid (res))
	{
		xmmsc_result_list_next (res);
		size++;
	}
	move(0,0);
	printw("Searched string:");
	printDisp(value,50); 
	if (size==0)
	{
		move(5,0);
		printw("Nothing has been found. :)");
		halfdelay(50);
		getch();
		cbreak();
		return;
	}
	move(2,0);
	char stri[256];
	sprintf(stri,"Number of hits:%d",size);
	printw(stri);
	uint * id=malloc(sizeof(uint)*size);
	
	xmmsc_result_list_first(res);
	int i;
	for (i=0;i<size;i++)
	{	
		int a=xmmsc_result_get_dict_entry_type (res,"id");
		//move(0,0);
		//sprintf(str,"%d ",a==XMMSC_RESULT_VALUE_TYPE_INT32);
		//printw(str);

		xmmsc_result_get_dict_entry_int32(res,"id",(id+i));
		xmmsc_result_list_next (res);
	}
	xmmsc_result_unref(res);
	
	i=0;
	int pos=0;
	while (1)
	{
		int dir=0;
		int ii;
		for (ii=pos;ii<pos+LINES-4&&ii<size;ii++)
		{
			char stri[3000];
			move(ii-pos+3,1);
			struct song songg=getSongInfo(c,id[ii]);
			sprintf(stri,"|% 4d %s  %s: %s  %s",ii,songg.genre,songg.artist,songg.title,songg.url);
			printDisp(stri,COLS-1);
			deleteSong(songg);
		}
		move(i-pos+3,0);
		addch((char)173);
		refresh();
		cbreak();
		keypad(stdscr, TRUE);
		int ch=getch();
		switch (ch)
		{
			case KEY_UP :
				dir=-1;
				break;
			case KEY_DOWN :
				dir=1;
				break;
			case KEY_ENTER :
			case 13:
				move(LINES-1,0);
				char stri[256];
				sprintf(stri,"select %d as id;" , id[i]);
				res = xmmsc_medialib_add_to_playlist (c,stri);
				xmmsc_result_wait(res);
				xmmsc_result_unref(res);
				
				printw("Song Added");
				refresh();
				halfdelay(50);
				getch();
				cbreak();
				move(LINES-1,0);
				printw("          ");
				break;
			case KEY_CANCEL :
			case 27:
				return;
			case 'a':
			case 'A':
				res = xmmsc_medialib_add_to_playlist (c,str);
				xmmsc_result_wait(res);
				xmmsc_result_unref(res);
				move(LINES-1,0);
				printw("All Songs Added");
				refresh();
				halfdelay(50);
				getch();
				cbreak();
				move(LINES-1,0);
				printw("               ");
				break;
		}
		if (i+dir<size&&i+dir>=0) //check bounds
		{
			move(i-pos+3,0);
			addch(' ');
                	                       	
			if ((i-pos)+dir<0 || (i-pos)+dir>LINES-5)	
				pos=pos+dir;
			
			i=i+dir;
		}
			       
	}
}
/*
int updatePos(int i,int pos, int dir)
{
	if (i-pos< -dir||i-pos<dir-LINES+4)
		return(i+dir);
	else
		return(pos+dir);
	
}
*/
char ** getKeys(xmmsc_connection_t *c)
{
	xmmsc_result_t * res = xmmsc_medialib_select (c,"select distinct key from media;");
	xmmsc_result_wait(res);
	char ** list=(char**)malloc(sizeof(char*)*21);
	int i=0;
	while (xmmsc_result_list_valid (res)&&i<20) 
	{
		char* r;
		xmmsc_result_get_dict_entry_str (res,"key", &r);
		int l=strlen(r)+2;
		list[i]=malloc(sizeof(char)*l);
		strcpy(list[i],r);
		xmmsc_result_list_next (res);
		i++;
	}
	for (i=i;i<21;i++)
		list[i]=NULL;
	
	xmmsc_result_unref(res);
	return list;
}
void destroyKeys(char** cl)
{
	int i;
	for (i=0;cl[i]!=NULL;i++)
		free(cl[i]);
	free(cl);
}
