/*
 * DATlib   Copyright (c) 1995-1996 Marcus Meissner &
 *          Friedrich-Alexander Universitaet Erlangen-Nuernberg (FAU), IMMD4
 *          All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    This product includes software developed by Marcus Meissner
 *    at the Friedrich-Alexander Universitaet Erlangen-Nuernberg (FAU), IMMD4
 *
 * 4. The name of the University or the author may not be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND THE AUTHOR ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE UNIVERSITY OR THE AUTHOR BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#ifdef linux
#define TEXTMODE
#endif

#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <stdio.h>

#include <X11/Xlib.h>
#include <xview/xview.h>
#include <xview/frame.h>
#include <xview/panel.h>
#include <xview/notify.h>
#include <xview/canvas.h>
#include <xview/xv_xrect.h>

#include "dataudiolib.h"
#include "xaudio.h"
#define __IN_MYTHINGS
#include "ply.h"
#include "_7seg.h"

#define SECONDS_BEFORE_SKIP	30

#define FRAME_FRAME 101

extern ply_window1_objects   *Ply_window1;

int	quit();
int	play(Panel_item item);
int	stop(Panel_item item);
int	forward(Panel_item item);
int	backward(Panel_item item);
#ifdef sun
int	open(char *fn,int mode);
#endif
Notify_value write_audio_data(Notify_client client,int xfd);
Notify_value write_null_data(Notify_client client,int xfd);
int	open_dat();

int	prognr;
int	maxx,lastsamplerate=0;
struct	da_time	ptime;
struct	da_time atime;
struct	da_time runtime;
struct	da_date	rdate;

int	valid,wasclosed;

int	NULL_fd = -1;

#define	VALID_PNR	1
#define	VALID_PTIME	2
#define	VALID_ATIME	4
#define	VALID_RTIME	8
#define	VALID_RDATE	16

#define	Y_PNR		5
#define	Y_PTIME		30
#define	Y_ATIME		55
#define	Y_RTIME		80
#define	Y_RDATE		105

Notify_client	nclient=10101;

void
close_audio() {
	if (AUDIO_fd<=0)
		return;
	notify_set_output_func(nclient,NOTIFY_FUNC_NULL,AUDIO_fd);
	if (fileno(stdout)!=AUDIO_fd)
		close(AUDIO_fd);
	AUDIO_fd=0;
}

void
open_null() {
	if (NULL_fd>0) return;
	NULL_fd=open("/dev/null",O_WRONLY);
}

void
open_audio() {
	if (AUDIO_fd>0)
		return;
	AUDIO_fd=audio_open(O_WRONLY);
	if (AUDIO_fd==-1)
		perror("open audio?");
	if (lastsamplerate)
		audio_change_attribs(AUDIO_fd,lastsamplerate,16,1);
	notify_set_output_func(nclient,write_audio_data,AUDIO_fd);
}

void
print_prognr() {
#ifdef TEXTMODE
	char	buffer[10];

	if (valid&VALID_PNR)
		sprintf(buffer,"%3d",prognr);
	else
		strcpy(buffer,"---");
	xv_set(Ply_window1->prognrtxt,PANEL_LABEL_STRING,buffer,NULL);
#else
	struct	ixbm	xb;
	Xv_opaque	image;
	int	x=0,y=0;

	init_ixbm(&xb,48,22);
	if (valid&VALID_PNR) {
		disp7segnr(&xb,&x,&y,prognr,3);
	} else {
		disp7seg(&xb,&x,&y,8);
		x+=2;
		disp7seg(&xb,&x,&y,8);
		x+=2;
		disp7seg(&xb,&x,&y,8);
	}
	image = xv_create(XV_NULL, SERVER_IMAGE, 
		SERVER_IMAGE_DEPTH, 1,
		SERVER_IMAGE_BITS, xb.bits,
		XV_WIDTH, xb.width,
		XV_HEIGHT, xb.height,
		NULL
	);
	xv_set(Ply_window1->prognrtxt,PANEL_LABEL_IMAGE,image,NULL);
#endif
}

void
print_time(struct da_time *xtime,int valmask,Xv_opaque whatentry) {
#ifdef TEXTMODE
	char	buffer[30];

	if (valid & valmask)
		sprintf(buffer,"%02d %02d:%02d:%02d",
			xtime->indexnr,
			xtime->hour,
			xtime->minute,
			xtime->second
		);
	else
		sprintf(buffer,"-- --:--:--");
	xv_set(whatentry,PANEL_LABEL_STRING,buffer,NULL);
#else
	struct	ixbm	xb;
	int	x=0,y=0;
	Xv_opaque	image;

	init_ixbm(&xb,128,22);
        if (!(valid&valmask)) {
                disp7dp(&xb,&x,&y);
                x+=2;
#define PRINT_MINUS(x,y) disp7seg(&xb,x,y,8);(*x)+=2;
                PRINT_MINUS(&x,&y);
                PRINT_MINUS(&x,&y);
                x+=5;
                PRINT_MINUS(&x,&y);
                PRINT_MINUS(&x,&y);
                disp7dp(&xb,&x,&y);
                PRINT_MINUS(&x,&y);
                PRINT_MINUS(&x,&y);
                disp7dp(&xb,&x,&y);
                PRINT_MINUS(&x,&y);
                PRINT_MINUS(&x,&y);
        } else {
                disp7dp(&xb,&x,&y);
                x+=2;
                disp7segnr(&xb,&x,&y,xtime->indexnr,2);
                x+=5;
                disp7segnr(&xb,&x,&y,xtime->hour,2);
                disp7dp(&xb,&x,&y);
                disp7segnr(&xb,&x,&y,xtime->minute,2);
                disp7dp(&xb,&x,&y);
                disp7segnr(&xb,&x,&y,xtime->second,2);
        }
	image = xv_create(XV_NULL, SERVER_IMAGE, 
		SERVER_IMAGE_DEPTH, 1,
		SERVER_IMAGE_BITS, xb.bits,
		XV_WIDTH, xb.width,
		XV_HEIGHT, xb.height,
		NULL
	);
	xv_set(whatentry,PANEL_LABEL_IMAGE,image,NULL);
#endif
}

void
print_programtime() {
	print_time(&ptime,VALID_PTIME,Ply_window1->progtime);
}


void
print_abstime() {
	print_time(&atime,VALID_ATIME,Ply_window1->abstime);
}

void
print_runtime() {
	print_time(&runtime,VALID_RTIME,Ply_window1->runtime);
}

static char	*wday2str[16]={
	"---","Sun","Mon","Tue","Wed","Thu","Fri","Sat",
	"---","---","---","---","---","---","---","---"
};
static char	*month2str[13]={
	"---","Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",
};

void
print_recorddate() {
	char	buffer[200];

	if (!(valid&VALID_RDATE))
		strcpy(buffer,"--- -- --- -- -- -- --");
	else {
		char	monthbuf[4];
		if (rdate.month<=12)
			sprintf(monthbuf,"%s",month2str[rdate.month]);
		else
			strcpy(monthbuf,"---");

		sprintf(buffer,"%s %02d %s %02d %02d:%02d:%02d",
			wday2str[rdate.weekday&0xf],
			rdate.year,
			monthbuf,
			rdate.day,
			rdate.hour,
			rdate.minute,
			rdate.second
		);
	}
	xv_set(Ply_window1->recorddate,PANEL_LABEL_STRING,buffer,NULL);
}

void
set_track_selected(int prognr) {
	Menu		menu;
	Menu_item	mitem;
	int		i,nrofitems,nr;
	char		buf1[20];

/*
	menu=xv_get(Ply_window1->button_toc,PANEL_ITEM_MENU);
	if (menu==NULL)
		return;
	nrofitems=xv_get(menu,MENU_NITEMS);
	sprintf(buf1,"Track %d.",prognr);
	for (i=1;i<nrofitems+1;i++) {
		int	flag;
		char	*str;

		mitem=xv_get(menu,MENU_NTH_ITEM,i);
		if (mitem==NULL)
			break;
		str=xv_get(mitem,MENU_STRING);
		if (str!=NULL && sscanf(str,buf1,&nr)) {
			xv_set(menu,MENU_SELECTED_ITEM,mitem,NULL);
		}
	}
*/
}

long	lastlocateto=-1;
int	pressed=0;

_locate_to(int prognr) {
	struct	da_locate	daloc;
	wasclosed = (DAT_fd<=0);
	
	lastlocateto=prognr;
	if (wasclosed)
		open_dat();

	if (AUDIO_fd <= 0) {
		if (NULL_fd<=0) {
			open_null();
			notify_set_output_func(nclient,write_null_data,NULL_fd);
		}
	}
	if (DAT_fd>0) {
		daloc.type	= DA_LOC_PROGNR;
		daloc.nextop	= DA_LOC_NEXTOP_READ;
		daloc.u.prognr	= prognr;
		da_control(DAT_fd,DA_LOCATE,(long)&daloc);
	}
	return XV_OK;
}

int
fastbackward(Panel_item item) {
	return _locate_to(0xBBB);
}

int
backward(Panel_item item) {
	struct	da_time	xtime;

	wasclosed = (DAT_fd<=0);
	if (wasclosed)
		open_dat();
	
	pressed++;
	if (DAT_fd>0) {
		if ((lastlocateto==-1) || (lastlocateto>0x100))
			da_control(DAT_fd,DA_GET_PROGNR,&lastlocateto);

		da_control(DAT_fd,DA_GET_PROGTIME,(long)&xtime);
		if (	(pressed>1) ||
			(	!(xtime.hour)	&&
				!(xtime.minute) &&
				(xtime.second < SECONDS_BEFORE_SKIP)
			)
		) {
			if (lastlocateto>=1)
				_locate_to(lastlocateto-1);
			else
				_locate_to(lastlocateto);
		} else
			_locate_to(lastlocateto);
	}
	return XV_OK;
}

int
fastforward(Panel_item item) {
	return _locate_to(0xAAA);
}

int
forward(Panel_item item) {
	wasclosed=(DAT_fd<=0);
	pressed++;

	if (wasclosed)
		open_dat();
	if (DAT_fd) {
		if ((lastlocateto==-1) || (lastlocateto>0x100))
			da_control(DAT_fd,DA_GET_PROGNR,&lastlocateto);
		_locate_to(lastlocateto+1);
	}
	return XV_OK;
}

void
cb_toc_changed(int fd,enum da_cb_type type,union da_callback_data *data) {
	Menu	menu;
	Menu_item	mitem;
	int	nrofitems,i;
	char	*s;

/* 
	menu=xv_get(Ply_window1->button_toc,PANEL_ITEM_MENU);
	if (!menu) {
		fprintf(stderr,"toc_changed, menu=%x!\n");
		return;
	}
	if (data==NULL) {
		xv_set(menu,MENU_INACTIVE,TRUE,NULL);
		return;
	}
	xv_set(menu,MENU_INACTIVE,FALSE,NULL);
	nrofitems=xv_get(menu,MENU_NITEMS);
	for (i=nrofitems+1;i>1;i--)
		xv_set(menu,MENU_REMOVE_ITEM,xv_get(menu,MENU_NTH_ITEM,1),NULL);
	if (data->toc.tocentries)
		xv_set(Ply_window1->button_toc,PANEL_INACTIVE,FALSE,NULL);
	else
		xv_set(Ply_window1->button_toc,PANEL_INACTIVE,TRUE,NULL);
	for (i=0;i<data->toc.tocentries;i++) {
		char	buf[200];
		Menu_item do_select_toc_ent(Menu_item);
		if (data->toc.toc[i].value>=100)
			continue;

		s=data->toc.toc[i].name;
		if (s==NULL)
			s="";
		sprintf(buf,"Track %x.%x%s",data->toc.toc[i].prognr,data->toc.toc[i].value,s);
		xv_set(menu,MENU_ITEM,
			XV_KEY_DATA,INSTANCE,Ply_window1,
			MENU_STRING,strdup(buf),
			MENU_GEN_PROC,do_select_toc_ent,
			NULL,
		NULL
		);
	}
	set_track_selected(prognr);
	xv_set(menu,MENU_GEN_PIN_WINDOW,Ply_window1->window1,"TOC-Menu",NULL);
*/
}

void
cb_samplerate(int fd,enum da_cb_type type,union da_callback_data *data) {
	audio_change_attribs(AUDIO_fd,data->samplerate,16,1);
	lastsamplerate=data->samplerate;
}

void
cb_prognr(int fd,enum da_cb_type type,union da_callback_data *data) {
	if (!data)
		valid&=~VALID_PNR;
	else {
		valid|=VALID_PNR;
		prognr=data->prognr;
	}
	print_prognr(maxx,Y_PNR);
	set_track_selected(data->prognr);
}

void
cb_tname_changed(int fd,enum da_cb_type type,union da_callback_data *data) {
	if (data->trackname==NULL) {
		xv_set(Ply_window1->window1,XV_LABEL,"Untitled",NULL);
		xv_set(xv_get(Ply_window1->window1,FRAME_ICON),XV_LABEL,"Untitled",NULL);
	} else {
		xv_set(Ply_window1->window1,XV_LABEL,data->trackname,NULL);
		xv_set(xv_get(Ply_window1->window1,FRAME_ICON),XV_LABEL,data->trackname,NULL);
	}
}

void
cb_progtime(int fd,enum da_cb_type type,union da_callback_data *data) {
	if (data==NULL)
		valid&=~VALID_PTIME;
	else {
		memcpy(&ptime,&(data->time),sizeof(ptime));
		valid|=VALID_PTIME;
	}
	print_programtime(maxx,Y_PTIME);
}

void
cb_abstime(int fd,enum da_cb_type type,union da_callback_data *data) {
	if (data==NULL)
		valid&=~VALID_ATIME;
	else {
		memcpy(&atime,&(data->time),sizeof(atime));
		valid|=VALID_ATIME;
	}
	print_abstime(maxx,Y_ATIME);
}


char    *wds[16]={
	"Mon","Tue","Wed","Thu","Fri","Sat","Sun",
	"---","---","---","---","---","---","---","---","---",
};
char    *mons[19]={
	"---",/*0*/
	"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep",/* 1-9*/
	"Oct","Nov","Dec",/* 10 - 12 */
};

void
cb_recorddate(int fd,enum da_cb_type type,union da_callback_data *data) {
	if (data==NULL) {
		valid&=~VALID_RDATE;
	} else {
		memcpy(&rdate,data,sizeof(rdate));
		valid|=VALID_RDATE;
	}
	print_recorddate(maxx,Y_RDATE);
}

void
cb_runtime(int fd,enum da_cb_type type,union da_callback_data *data) {
	if (data==NULL)
		valid&=~VALID_RTIME;
	else {
		memcpy(&runtime,&(data->time),sizeof(runtime));
		valid|=VALID_RTIME;
	}
	print_runtime(maxx,Y_RTIME);
}

Notify_value
write_null_data(Notify_client client,int xfd) {
	char	buf[4];

	if (da_read(DAT_fd,(unsigned char*)buf,4)>0) {
		xv_set(Ply_window1->mainpanel,PANEL_VALUE,4/*stop*/,NULL);
		if (wasclosed) {
			da_close(DAT_fd);
			DAT_fd = -1;
		}
		notify_set_output_func(nclient,NOTIFY_FUNC_NULL,xfd);
		close(NULL_fd);
		NULL_fd=-1;
	}
	return NOTIFY_DONE;
}

Notify_value
write_audio_data(Notify_client client,int xfd) {
	unsigned short	buf[2000];
	int		curleft;
	int		res;

	if (!DAT_fd) {
		xv_set(Ply_window1->mainpanel,PANEL_VALUE,4/*stop*/,NULL);
		close_audio();
		return NOTIFY_DONE;
	}
	res=da_read(DAT_fd,(unsigned char*)buf,2000);
	if (!res)
		return NOTIFY_DONE;
	lastlocateto=-1;pressed=0;
	if (res==-1) {
		perror("da_read");
		if (-1==da_control(DAT_fd,DA_REWIND,0))
			perror("da_control REWIND");
		da_close(DAT_fd);
		DAT_fd=0;
		xv_set(Ply_window1->mainpanel,PANEL_VALUE,4/*stop*/,NULL);
		close_audio();
		return NOTIFY_DONE;
	}
	if (xv_get(Ply_window1->mainpanel,PANEL_VALUE)!=1)
		xv_set(Ply_window1->mainpanel,PANEL_VALUE,1/*play*/,NULL);
	curleft=0;
	while (curleft!=2000) {
		res=write(xfd,buf+curleft,2000-curleft);
		if (res==-1)
			break;
		curleft+=res;
	}
	return NOTIFY_DONE;
}

int
play(Panel_item item) {
	open_dat();
	if (-1==DAT_fd)
		xv_set(Ply_window1->mainpanel,PANEL_VALUE,4/*stop*/,NULL);
	else
		da_control(DAT_fd,DA_ABORT,(long)0);
	open_audio();
	return XV_OK;
}

int 
stop(Panel_item item) {
	ply_window1_objects *ip = (ply_window1_objects *) xv_get(item, XV_KEY_DATA, INSTANCE);
	close_audio();
	if (DAT_fd)
		da_control(DAT_fd,DA_ABORT,0);
	return XV_OK;
}

int
eject(Panel_item item) {
	close_audio();
	open_dat();
	if (DAT_fd>0) {
		if (-1==da_control(DAT_fd,DA_EJECT,0))
			perror("da_control DA_EJECT");
		da_close(DAT_fd);
		DAT_fd=0;
	}
	xv_set(Ply_window1->mainpanel,PANEL_VALUE,4/*stop*/,NULL);
	return XV_OK;
}

int
programnumber(Panel_item item,int nr) {
	struct 	da_locate	loc;
	
	if (DAT_fd) {
		loc.type=DA_LOC_PROGNR;
		loc.nextop=DA_LOC_NEXTOP_READ;
		loc.u.prognr=nr;
		if (-1==da_control(DAT_fd,DA_LOCATE,(long)&loc)) {
			perror("da_control LOCATE");
		}
	}
	return XV_OK;
}

/*
Menu_item
do_select_toc_ent(Menu_item item, Menu_generate op) {
	char	*buf;
	int	prognr,indexnr;
	struct 	da_locate	loc;

	switch (op) {
	case MENU_DISPLAY:
		break;

	case MENU_DISPLAY_DONE:
		break;

	case MENU_NOTIFY:
		buf=xv_get(item,MENU_STRING);
		if (2!=sscanf(buf,"Track %d.%d",&prognr,&indexnr)) {
			fprintf(stderr,"do_select_toc_ent:menuitem %s???\n",buf);
			return;
		}
		loc.type=DA_LOC_PROGNR;
		loc.nextop=DA_LOC_NEXTOP_READ;
		loc.u.prognr=prognr;
		if (DAT_fd) {
			if (-1==da_control(DAT_fd,DA_LOCATE,(long)&loc)) {
				perror("da_control LOCATE");
			}
		}
		break;
	case MENU_NOTIFY_DONE:
		break;
	}
	return item;
}
*/

quit() {
	xv_destroy_safe(Ply_window1->window1);
	if (DAT_fd>0)
		da_close(DAT_fd);
	return XV_OK;
}

xpause() {
	close_audio();
	if (DAT_fd>0)
		da_close(DAT_fd);
	DAT_fd=0;
}

int
open_dat() {
	char	*tape;
	if (DAT_fd>0)
		return DAT_fd;

	tape=getenv("TAPE");
	if (!tape)
		tape=DEFAULTAUDIOTAPE;
	DAT_fd=da_open(tape,O_RDONLY|O_NDELAY);
	if (DAT_fd==-1) {
		fprintf(stderr,"ply_mythings.c:open_dat:could not open ");
		perror(tape);
	}
	da_add_callback(DAT_fd,DA_CB_PROGNR,0,cb_prognr);
	da_add_callback(DAT_fd,DA_CB_SAMPLERATE,0,cb_samplerate);
	da_add_callback(DAT_fd,DA_CB_PROGTIME,DA_CB_SECONDS,cb_progtime);
	da_add_callback(DAT_fd,DA_CB_ABSTIME,DA_CB_SECONDS,cb_abstime);
	da_add_callback(DAT_fd,DA_CB_RUNTIME,DA_CB_SECONDS,cb_runtime);
	da_add_callback(DAT_fd,DA_CB_RECORDDATE,DA_CB_SECONDS,cb_recorddate);
	da_add_callback(DAT_fd,DA_CB_TOC,0,cb_toc_changed);
	da_add_callback(DAT_fd,DA_CB_TRACKNAME,0,cb_tname_changed);
	return DAT_fd;
}
