/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
 *  CDPlay  0.31
 *           Copyright (C) 1994    Sariel Har-Peled
 *
 *  Based on WorkBone CD Rom Player Software
 *           Copyright (C) 1993  Thomas McWilliams
 *  
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
\*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/

#include  <stdlib.h>
#include  <stdio.h>
#include  <stdarg.h>
#include  <sys/types.h>
#include  <sys/time.h>
#include  <signal.h>
#include  <unistd.h>
#include  <termios.h>
#include  <mntent.h>
#include  <getopt.h>
#include  <string.h>
#include  <ncurses.h>

#include  "struct.h"
#include  "cdp.h"
#include  "database.h"
#include  "color.h"
#include  "misc.h"
#include  "display.h"
#include  "getline.h"


/*======================================================================
 * Prototypes
\*======================================================================*/
static void             playtime( char    * str );


/*======================================================================
 * CD Player mode structure
\*======================================================================*/
typedef  struct {
    BOOL                     fSilentMode;
    BOOL                     fNoAutoPlay;
    BOOL                     fCDPlayMode;
    BOOL                     fCmdTable, fNoFastIn, fCmdVersion;
    int                      startTrack;
}  modeInfoType;


/*======================================================================
 * Variables
\*======================================================================*/
char                   *cur_trackname;  /* Take a guess */
int                     cur_index = 0;  /* Current index mark */
int                     cur_frame;      /* Current frame number */
struct play            *playlist = NULL;
struct cdinfo           thiscd, oldcd, *cd = &thiscd;
int                     cur_track = -1; /* Current track number, starting at 1 */
char                   *cur_artist;     /* Name of current CD's artist */
char                    cur_avoid;      /* Avoid flag */
char                    cur_contd;      /* Continued flag */
char                   *cur_cdname;     /* Album name */
int                     cur_nsections;  /* Number of sections currently defined */
int                     exit_on_eject = 0;

int                     cur_balance = 10,
                        info_modified;
int                     cur_track,
                        cur_pos_abs,
                        cur_pos_rel,
                        cur_tracklen,
                        cur_cdlen,
                        cur_cdmode,
                        cur_ntracks,
                        cur_lasttrack,
                        cur_firsttrack,
                        cur_listno;
static displayInfoType  dispInfo;


/*======================================================================
 * Start Of Code
\*======================================================================*/


static  void     dispScreen( void )
{
    dispInfo.cur_track = cur_track;
    if  ( memcmp( &oldcd, &thiscd, sizeof( oldcd ) ) != 0 ) {
        load(); 
	memcpy( &oldcd, &thiscd, sizeof( oldcd ) );
    }
    displayControlPanel( &dispInfo );
}


static  void    printHelpMessage( void )
{
    printf( "\nCDPlay version 0.31     Copyright 1994 (c) Sariel Har-Peled\n" );
    printf(   "      Based on WorkBone Copyright 1993 (c) Thomas McWilliams\n" );
    printf(   "Free Software under GNU General Public License.\n\n" );
    printf(   "Useage:  cdp [ -h | -l | -s | -n ] [play <track-num>]\n" );
    printf(   "         cdplay [ -h | -l | -n ] [play <track-num>] [stop] [table]\n"
	          );
    printf(   "           -h : help\n" );
    printf(   "           -n : do NOT auto play\n" );
    printf(   "           -l : slow start (wait for init)\n" );
    printf(   "           -s : Silent Mode\n\n" );
    printf(   "Type 'cdp' to start program.\n\n" );
    printf(   "Engage the NUM LOCK on your keypad. From inside CD Play\n" );
    printf(   "pressing DEL on numeric keypad will display a help menu.\n\n" );
    printf(   "'cdplay' is the non-interactive version of the program.\n\n" );
    myExit( 0 );
}


static void          printTOC( void )
{
    int                  ind;
    struct trackinfo   * trk;
    char                 buffer[ 200 ], ch;
 
    load();
    printf( "\nArtist : %s\n", thiscd.artist );
    printf( "CD Name: %s\n", thiscd.cdname );
    printf( "Tracks Number: %3d   Total Play Time: %2d:%02d:%02d\n",
	      thiscd.ntracks, thiscd.length / 3600, (thiscd.length / 60) % 60,
	      thiscd.length % 60 );

    printf( "Track Num  Start Time  Length  Name \n" );
    for  ( ind = 0; ind < thiscd.ntracks; ind++ ) {
	trk = &thiscd.trk[ ind ];
	if  ( trk->songname != NULL ) {
	    sprintf( buffer, "%s", trk->songname );
	} else
	    buffer[ 0 ] = 0;

	ch = ( (ind + 1 ) == cur_track )? '*' : ' ';
	printf( 
           " %c %2d     %2d:%02d:%02d    %2d:%02d    %s\n",
	   ch,
	   ind + 1, trk->start / (60 * 75 * 60), trk->start / (60 *75) % 60,
	   ( trk->start / 75 ) % 60,
	   trk->length / 60, trk->length % 60,
	   &buffer[0] );
    }
}


static  void        dispStatus( int      * pSaveTrack )
{
    char                    szTotalTime[ 100 ];

    switch (cur_cdmode) {
        case  0 :             /* CDNULL */
	    cur_track = *pSaveTrack = 1;
	    displayStatusPrintf( &dispInfo,  "stopped " );
	    break;

	case  1 :             /* CDPLAY */
	    playtime( szTotalTime );
	    displayStatusPrintf( &dispInfo,  "playing #~%d~%s ", cur_track,
				szTotalTime );
	    break;

	case  3 :             /* CDPAUZ */
	    displayStatusPrintf( &dispInfo,  "pause   #%d ", cur_track);
	    break;

	case  4 :             /* CDSTOP */
	    displayStatusPrintf( &dispInfo,  "stopped #%d ", *pSaveTrack );
	    break;

	case  5 :             /* CDEJECT */
	    return;

        default :
	    displayStatusPrintf( &dispInfo, "cur_cdmode %d ", cur_cdmode);
	    break;
    } /* switch */
}


static  void     doPlayLoop( modeInfoType      * pMode )
{
    struct timeval          mydelay;
    fd_set                  rset;
    int                     sel_stat, cdStat;
    int                     key = 0, oldTrack = -1, oldCdMode = -1;
    int                     save_track = 1, tmppos = 0;

    mydelay.tv_sec = 0;
    mydelay.tv_usec = 200000;

    do {
        cdStat = cd_status ();
	dispStatus( &save_track );
	if  ( cur_cdmode != oldCdMode  ||  cur_track != oldTrack ) {
	    oldCdMode = cur_cdmode;
	    oldTrack = cur_track;
	    dispScreen();
	} 
	    

        /*
         * use select() to update status every 200 millisecs or
         * sooner if keypad has data
         */
        FD_ZERO (&rset);
        FD_SET (STDIN_FILENO, &rset);
        sel_stat = select( 4, &rset, NULL, NULL, 
			   pMode->fSilentMode? NULL : &mydelay);
        if  ( cdStat == 0  ||  cdStat == 4  ||  cur_cdmode == CDEJECT) 
            return;
        if (cur_cdmode < 1)
            save_track = 1;

        /* if key was pressed, parse it and do function */
        if (FD_ISSET (STDIN_FILENO, &rset)) {
	    key = getch();
	    switch   ( key ) {
                case  '.' :
                case  '?' :
		    break;
		    
	        case   ENTER :
		    dispInfo.cur_track = cur_track;
		    displayEditCurrSongName( &dispInfo );
		    break;

	        case  'a' :
	        case  'A' :		    
		    dispInfo.cur_track = cur_track;
		    displayEditArtistName( &dispInfo );
                    break;

	        case  'c' :
	        case  'C' :		    
		    dispInfo.cur_track = cur_track;
		    displayEditCDName( &dispInfo );
                    break;

    	        case  'r' :
	        case  'R' :
		    displayClearScreen( &dispInfo );
		    dispScreen();
		    break;

                case  '1' :
                    if (cur_cdmode == CDPLAY) {
                        tmppos = cur_pos_rel - 15;
                        play_cd( cur_track, tmppos > 0 ? 
                                   tmppos : 0, cur_ntracks + 1);
                    }
                    break;

                case  '3' :
                    if (cur_cdmode == CDPLAY) {
                        tmppos = cur_pos_rel + 15;
                        if (tmppos < thiscd.trk[cur_track - 1].length)
                            play_cd (cur_track, tmppos, cur_ntracks + 1);
                    }
                    break;

                case  '2' :
	        case  KEY_DOWN :
                    stop_cd ();
                    eject_cd ();
                    break;

                case  '4' :
	        case  KEY_LEFT :
                    cur_track--;
                    if (cur_track < 1)
                        cur_track = cur_ntracks;
                    play_cd (cur_track, 0, cur_ntracks + 1);
                    break;

                case '5':
                    if (cur_cdmode == CDPLAY)
                        play_cd (cur_track, 0, cur_ntracks + 1);
                    break;

                case  '6':
                case  KEY_RIGHT :
                    if (cur_track == cur_ntracks)
                        cur_track = 0;
                    play_cd (cur_track + 1, 0, cur_ntracks + 1);
                    break;

                case '7':
                    displayStatusPrintf( &dispInfo,  "stop ");
                    save_track = cur_track;
                    stop_cd ();
                    break;

                case  '8' :
                case  KEY_UP :
                    if (cur_cdmode == CDPLAY  ||  cur_cdmode == CDPAUZ) {
                        pause_cd ();
                    }
                    break;

                case '9':
                    if (cur_cdmode == CDSTOP  ||  cur_cdmode == CDNULL) {
                        play_cd (save_track, 0, cur_ntracks + 1);
                    }
                    break;

                case 0x1b:
		    getch();
		    getch();
                    displayStatusPrintf( &dispInfo,  "Turn ON Num Lock! " );
                    sleep (1);
                    break;

                case 's' :
                case 'S' :
                    pMode->fSilentMode = ! pMode->fSilentMode;
                    break;

	        case 't' :
	        case 'T' :
		    printTOC();
		    break;

	        case 'q' :
	        case 'Q' :
		    myExit( 0 );
		    break;

                default:
                    break;
            }
        }
    }  while  ( key != 'q' );
}


static void             playtime( char    * str )
{
    int                     mymin, emin;
    int                     mysec, esec;

    mysec = cur_pos_rel % 60;
    mymin = cur_pos_rel / 60;
    esec = cur_pos_abs % 60;
    emin = cur_pos_abs / 60;
    if  ( emin >= 60 ) 
        sprintf( str, "%s ~%2d~:~%02d~  ~%d~:~%02d~:~%02d~",
		 cur_track > 9 ? " " : "  ",
                 mymin, mysec, emin / 60, emin % 60, esec );
    else
        sprintf( str, "%s ~%2d~:~%02d~  ~%2d~:~%02d~", cur_track > 9 ? " " : "  ",
             mymin, mysec, emin, esec );
}
 

static  void        checkCDMounted( void )
{
    FILE                   * fp;
    struct mntent          * mnt;
    
    /* check if drive is mounted (from Mark Buckaway's cdplayer code) */
    if  ( ( fp = setmntent (MOUNTED, "r")) == NULL) {
        fprintf(stderr, "Couldn't open %s: %s\n", MOUNTED, strerror (errno));
        myExit (1);
    }

    while ((mnt = getmntent (fp)) != NULL) {
        if (strcmp (mnt->mnt_type, "iso9660") == 0) {
            fputs ("CDROM already mounted. Operation aborted.\n", stderr);
            endmntent (fp);
            myExit (1);
        }
    }
    endmntent (fp);
}


static BOOL         isNumeric( char      * str ) 
{
    if  ( str == NULL )
        return  FALSE;
    while  ( *str ) {
        if  ( *str < '0'  ||  *str > '9' )
	    return   FALSE;
	str++;
    }

    return   TRUE;
}


static void         readCommandLine( int    argc, char     * argv[ ], 
			      modeInfoType      * pInfoMode )
{
    char                   * str, *strNext, * pos;
    int                      ind;

    memset( pInfoMode, 0, sizeof( modeInfoType ) );

    str = argv[ 0 ];
    pos = strrchr( argv[ 0 ], '/' );
    if ( pos != NULL )
       str = pos + 1;    

    if  ( strcmp( str, "cdplay" ) == 0 ) {
	pInfoMode->fCmdVersion = TRUE;
        pInfoMode->fCDPlayMode = TRUE;
    }

    /* get options */
    for (ind = 1; ind < argc; ind++ ) {
	str = argv[ ind ];
	if  ( ind < argc - 1 ) 
	    strNext = argv[ ind + 1 ];
	else
            strNext = NULL;
	if  ( str[ 0 ] == '-'  &&  str[ 1 ]  &&  ! str[ 2 ] ) {
            switch  ( str[ 1 ] ) {
                case 'l':
                    pInfoMode->fNoFastIn = TRUE;
                    break;
 
                case 's':
                    pInfoMode->fSilentMode = TRUE;
                    break;

                case 'n':
		    pInfoMode->fNoAutoPlay = TRUE;
		    break;

                default:
                    printHelpMessage();
		    break;
	    } /* switch */
	} else {
	    if  ( strcmp( str, "table" ) == 0 ) {
	        pInfoMode->fCmdVersion = TRUE;
		pInfoMode->fCmdTable = TRUE;
		continue;
	    } 
	    if  ( strcmp( str, "stop" ) == 0 ) {
	        pInfoMode->fCmdVersion = TRUE; 
	        pInfoMode->fNoAutoPlay = TRUE;
		continue;
	    } 
	    if  ( strcmp( str, "play" ) == 0 ) {
	        if  ( isNumeric( strNext ) ) {
		    pInfoMode->startTrack = atoi( strNext );
		    ind++;
		    continue;
		} 
	    } 

	    printHelpMessage();
	    break;
	} /* if */
    }

}


static void      readStatusFast( void ) 
{
    int                sss, dly = 6;
	
    do { 
        sss = cd_status() ;
	if (sss == 0 || sss == 4) 
	    sleep( 1 );
	dly--;
    }  while  ( sss == 0  ||  sss == 4 );
}


static  void    doCmdCommands( modeInfoType      * pMode )
{
    int             sss;

    /* delay while CD drive initializes itself */
    if ( pMode->fNoFastIn ) { 
        sleep( 6 );
    } else 
        readStatusFast();

    sss = cd_status() ;
    if  ( sss == 0  ||  sss == 4 ) {
        fprintf( stderr, "Unable to play cd\n" );
	myExit( -1 );
    }

    cur_track = 1;        
    if  ( pMode->fCmdTable ) 
        printTOC();

    if  ( ! pMode->fNoAutoPlay ) {
        if   ( pMode->startTrack > 0   &&  pMode->startTrack <= cur_ntracks + 1 )
            cur_track = pMode->startTrack;
	play_cd( cur_track, 0, cur_ntracks + 1 );
	if  ( pMode->fCDPlayMode ) 
	    myExit( 0 );
    }

    myExit( 0 );
}
  

static  void      readStatusAndDisplay( modeInfoType    * pMode )
{
    int                     dly;
    
    if ( pMode->fNoFastIn ) {
        for (dly = 6; dly > -1; dly--) {
            displayStatusPrintf( &dispInfo,  "wait ... initializing %d ", dly);
	    sleep (1);
        }
	displayStatusPrintf( &dispInfo, "" );
    } else {
        displayStatusPrintf( &dispInfo, "Wait... Reading CD Status..." );
	readStatusFast(); 
    }
}


int          main( int argc, char *argv[] )
{
    int                     sss;
    modeInfoType            modeInfo;
    
    readCommandLine( argc, argv, &modeInfo );
    checkCDMounted();
    if  ( modeInfo.fCmdVersion ) 
        doCmdCommands( &modeInfo );

    init_ncurses();
    init_colors();

    memset( &oldcd, 0, sizeof( oldcd ) );
    memset( &thiscd, 0, sizeof( thiscd ) );
    displayInitInfo( &dispInfo, &thiscd );

    /*  display control panel template */
    dispScreen();

    /* delay while CD drive initializes itself */
    readStatusAndDisplay( &modeInfo );

    sss = cd_status ();
    if (sss == 0 || sss == 4)
        goto done;
    cur_track = 1;

    if  ( ! modeInfo.fNoAutoPlay ) {
        if   ( modeInfo.startTrack > 0   &&  
	       modeInfo.startTrack <= cur_ntracks + 1 )
            cur_track = modeInfo.startTrack;
 	play_cd( cur_track, 0, cur_ntracks + 1 );
      }

    /* do cd play loop */
    doPlayLoop( &modeInfo );

done:
    if (thiscd.trk != NULL)
        free (thiscd.trk);

    printf("\n");

    return( 0 );
}


/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
 *
 * cdp.c - end of file
\*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
