/*
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 *
 */

#include "MP3Broadcaster.h"
#include "MP3MetaInfoUpdater.h"
#include "StringTranslator.h"

#include "defaultPaths.h"

#include <stdio.h>
#include <stdlib.h>
#include "SafeStdLib.h"
#include <signal.h>
#include <sys/stat.h>
#ifndef __Win32__
    #include <unistd.h>
#endif


#ifndef __Win32__
        #include <netdb.h>
    #if defined (__solaris__) || defined (__osf__) || defined (__hpux__)
        #include "daemon.h"
    #else
        #ifndef __FreeBSD__
            #include <sys/sysctl.h>
        #endif
    #endif
#endif


#include <errno.h>

#include "OSHeaders.h"
#include "OS.h"
#include "OSMemory.h"
#include "Task.h"
#include "TimeoutTask.h"
#include "OSArrayObjectDeleter.h"
#include "ConfParser.h"
#include "MP3FileBroadcaster.h"
// must now inlcude this from the project level using the -i switch in the compiler
#ifndef __MacOSX__
    #include "revision.h"
#endif


MP3Broadcaster* MP3Broadcaster::sBroadcaster = NULL;

//#include "MyAssert.h"

MP3Broadcaster::MP3Broadcaster(char* ipaddr, int port, char* config, char* playList, char* workingDir, Bool16 useICY) :
    mValid(true),
    mPort(8000),
    mBitRate(0),
    mFrequency(0),
    mUpcomingSongsListSize(7),
    mRecentSongsListSize(0),
    mLogging(0),
    mShowCurrent(true),
    mShowUpcoming(true),
    mNumErrors(0),
    mNumWarnings(0),
    mPreflight(false),
    mCleanupDone(false),
    mTempPicker(NULL),
    mSocket(NULL, 0),
    mLog(NULL),
    mUseICY(useICY)
{
    sBroadcaster = this;
    
    strcpy(mIPAddr, "128.0.0.1");
    strcpy(mPlayListPath, DEFAULTPATHS_ETC_DIR "mp3playlist.ply");
    strcpy(mWorkingDirPath, DEFAULTPATHS_ETC_DIR);
    strcpy(mPlayMode, "sequential");
    strcpy(mMountPoint, "/");
    strcpy(mGenre, "Pop");
    strcpy(mURL, "");
    strcpy(mPIDFile, "");

    //see if there is a defaults File.
    //if there is load it and over-ride the other defaults
    int len = ::strlen(config) + 10;
    char *defaultFileName = new char[len];
    qtss_snprintf(defaultFileName, len, "%s%s", config, ".def");
    (void) ::ParseConfigFile( false, defaultFileName, ConfigSetter, this ); //ignore if no defaults file
    delete [] defaultFileName;
 
    int err = ::ParseConfigFile( false, config, ConfigSetter, this );
    if (err)
    {
        mValid = false;
        return;
    }
    
    if (ipaddr != NULL)
    {
        // override config file
        // size limit for any IP addr is 255
        strncpy(mIPAddr, ipaddr, 255);
    }
    
    if (port != 0)
    {
        // override config file
        mPort = port;
    }
    
    if (playList)
    {
        // override config file
        // size limit for any playlist string is PATH_MAX - 1
        strncpy(mPlayListPath, playList, PATH_MAX-1);
    }
    
    if (workingDir)
    {
        // override config file
        // size limit for any working path is PATH_MAX - extension - 1
        strncpy(mWorkingDirPath, playList, PATH_MAX-12);
    }
    
    CreateWorkingFilePath(".current", mCurrentFile);
    CreateWorkingFilePath(".upcoming", mUpcomingFile);
    CreateWorkingFilePath(".replacelist", mReplaceFile);
    CreateWorkingFilePath(".stoplist", mStopFile);
    CreateWorkingFilePath(".insertlist", mInsertFile);
    CreateWorkingFilePath("mp3_broadcast.log", mLogFile);
}

Bool16 MP3Broadcaster::ConfigSetter( const char* paramName, const char* paramValue[], void* userData )
{
    // return true if set fails
    MP3Broadcaster* thisPtr = (MP3Broadcaster*)userData;

    if (!::strcmp( "destination_ip_address", paramName) )
    {   
        if (strlen(paramValue[0]) >= sizeof(thisPtr->mIPAddr))
            return true;
        
        strcpy(thisPtr->mIPAddr, paramValue[0]);
        return false;
    }
    else if (!::strcmp( "destination_base_port", paramName) )
    {
        thisPtr->mPort = atoi(paramValue[0]);

        return false;
    }
    else if (!::strcmp( "max_upcoming_list_size", paramName) )
    {
        if ( ::atoi( paramValue[0] )  < 0 )
            return true;
            
        thisPtr->mUpcomingSongsListSize = ::atoi( paramValue[0] );
        return false;
    }
    else if (!::strcmp( "play_mode", paramName) )
    {
        if (strlen(paramValue[0]) >= sizeof(thisPtr->mPlayMode))
            return true;
        
        strcpy(thisPtr->mPlayMode, paramValue[0]);
        return false;
    }   
    else if (!::strcmp( "recent_songs_list_size", paramName) )
    {
        if ( ::atoi( paramValue[0] )  < 0 )
            return true;
            
        thisPtr->mRecentSongsListSize = ::atoi( paramValue[0] );
        return false;
    }
    else if (!::strcmp( "playlist_file", paramName) )
    {
        if (strlen(paramValue[0]) >= sizeof(thisPtr->mPlayListPath))
            return true;
        
        strcpy(thisPtr->mPlayListPath, paramValue[0]);
        return false;
    }
    else if (!::strcmp( "working_dir", paramName) )
    {
        if (strlen(paramValue[0]) >= sizeof(thisPtr->mWorkingDirPath))
            return true;
        
        strcpy(thisPtr->mWorkingDirPath, paramValue[0]);
        return false;
    }
    else if (!::strcmp( "logging", paramName) )
    {
        return SetEnabled(paramValue[0], &thisPtr->mLogging);
    }
    else if (!::strcmp( "show_current", paramName) )
    {
        return SetEnabled(paramValue[0], &thisPtr->mShowCurrent);
    }
    else if (!::strcmp( "show_upcoming", paramName) )
    {
        return SetEnabled(paramValue[0], &thisPtr->mShowUpcoming);
    }
    else if (!::strcmp( "use_icy", paramName) )
    {
        return SetEnabled(paramValue[0], &thisPtr->mUseICY);
    }
    else if (!::strcmp( "broadcast_name", paramName) )
    {
        if (strlen(paramValue[0]) >= sizeof(thisPtr->mName))
            return true;
        
        strcpy(thisPtr->mName, paramValue[0]);
        return false;
    }
    else if (!::strcmp( "broadcast_password", paramName) )
    {
        if (strlen(paramValue[0]) >= sizeof(thisPtr->mPassword))
            return true;
        
        strcpy(thisPtr->mPassword, paramValue[0]);
        return false;
    }
    else if (!::strcmp( "broadcast_genre", paramName) )
    {
        if (strlen(paramValue[0]) >= sizeof(thisPtr->mGenre))
            return true;
        
        strcpy(thisPtr->mGenre, paramValue[0]);
        return false;
    }
    else if (!::strcmp( "broadcast_url", paramName) )
    {
        if (strlen(paramValue[0]) >= sizeof(thisPtr->mURL))
            return true;
        
        strcpy(thisPtr->mURL, paramValue[0]);
        return false;
    }
    else if (!::strcmp( "broadcast_bitrate", paramName) )
    {
        if ( ::atoi( paramValue[0] )  < 0 )
            return true;
            
        thisPtr->mBitRate = ::atoi( paramValue[0] );
        return false;
    }
    else if (!::strcmp( "broadcast_sample_rate", paramName) )
    {
        if ( ::atoi( paramValue[0] )  < -1 )
            return true;
            
        thisPtr->mFrequency = ::atoi( paramValue[0] );
        return false;
    }
    else if (!::strcmp( "broadcast_mount_point", paramName) )
    {
        if (strlen(paramValue[0]) >= sizeof(thisPtr->mMountPoint))
            return true;
        // Make sure the mountpoint always begins with a '/' character.
        // If its missing prepend it to the mountpoint name for them.
        thisPtr->mMountPoint[0] = '\0';
        if (*paramValue[0] != '/')
            strcpy(thisPtr->mMountPoint, "/");
        strcat(thisPtr->mMountPoint, paramValue[0]);
        return false;
    }
    else if (!::strcmp( "pid_file", paramName) )
    {
        if (strlen(paramValue[0]) >= sizeof(thisPtr->mPIDFile))
            return true;
        
        strcpy(thisPtr->mPIDFile, paramValue[0]);
        return false;
    }
    else if (!::strcmp( "max_err_file_k_size", paramName) )
    {
        if ( !paramValue[0] || !::strlen(paramValue[0]) )
             return true;
			
        unsigned long setvalue = kSInt32_Max;
        int maxValue = ::atoi( paramValue[0] );
        if (maxValue >= 0)
            setvalue = (unsigned long) maxValue;
		
        qtss_setmaxprintfcharsinK( (unsigned long) setvalue);
        return false;
    }
    
    return true;
}

Bool16 MP3Broadcaster::SetEnabled( const char* value, Bool16* field)
{
    if ( ::strcmp( "enabled", value) && ::strcmp( "disabled", value) )
        return true;
        
    *field = !strcmp( "enabled", value );
        
    return false;
}

void MP3Broadcaster::CreateWorkingFilePath(char* extension, char* result)
{
    if (strlen(mWorkingDirPath) + strlen(extension) >= PATH_MAX)
        result[0] = 0;
    else
    {
        strcpy(result, mWorkingDirPath);
        strcat(result, extension);
    }
}

void MP3Broadcaster::CreateCurrentAndUpcomingFiles()
{
    if (mShowCurrent) 
    {   
        if(FileCreateAndCheckAccess(mCurrentFile))
        {   /* error */
            mLog->LogInfo( "MP3Broadcaster Error: Failed to create current broadcast file" );
        }
    }


    if (mShowUpcoming) 
    {       
        if(FileCreateAndCheckAccess(mUpcomingFile))
        {   /* error */
            mLog->LogInfo( "MP3Broadcaster Error: Failed to create upcoming broadcast file" );
        }
    }
}

void MP3Broadcaster::UpdatePlaylistFiles(PlaylistPicker *picker,PlaylistPicker *insertPicker)
{
    if ( (NULL == picker) || (NULL == insertPicker) ) 
        return;
        
    /* if .stoplist file exists - prepare to stop broadcast */
    if(!access(mStopFile, R_OK))
    {
        picker->CleanList();
        PopulatePickerFromFile(picker, mStopFile, "", NULL);

        mTempPicker->CleanList();

        remove(mStopFile);
        picker->mStopFlag = true;
    }

    /* if .replacelist file exists - replace current playlist */
    if(!access(mReplaceFile, R_OK))
    {
        picker->CleanList();     
        PopulatePickerFromFile(picker, mReplaceFile, "", NULL);
        
        mTempPicker->CleanList();
        
        remove(mReplaceFile);
        picker->mStopFlag = false;
    }

    /* if .insertlist file exists - insert into current playlist */
    if(!access(mInsertFile, R_OK))
    {
        insertPicker->CleanList();
        mTempPicker->CleanList();

        PopulatePickerFromFile(insertPicker, mInsertFile, "", NULL);
        remove(mInsertFile);
        picker->mStopFlag = false;
    }


                // write upcoming playlist to .upcoming file 
    if (mShowUpcoming) 
    {
        FILE *upcomingFile = fopen(mUpcomingFile, "w");
        if(upcomingFile)
        {
            mElementCount = 0;
        
            if (!::strcmp(mPlayMode, "weighted_random")) 
                qtss_fprintf(upcomingFile,"#random play - upcoming list not supported\n");
            else
            {   qtss_fprintf(upcomingFile,"*PLAY-LIST*\n");
                ShowPlaylistElements(insertPicker,upcomingFile);
                ShowPlaylistElements(picker,upcomingFile);
                if (    picker->GetNumMovies() == 0 
                        && !picker->mStopFlag 
                        && 0 != ::strcmp(mPlayMode, "sequential") 
                    )
                {   picker->CleanList();
                    PopulatePickerFromFile(picker,mPlayListPath,"",NULL);
                    ShowPlaylistElements(picker,upcomingFile);
                    mTempPicker->CleanList();
                    PopulatePickerFromFile(mTempPicker,mPlayListPath,"",NULL);
                }
                
                if  (   mElementCount <= mUpcomingSongsListSize 
                        && 0 == ::strcmp(mPlayMode, "sequential_looped")
                    )
                {   if (mTempPicker->GetNumMovies() == 0)
                    {   mTempPicker->CleanList();
                        PopulatePickerFromFile(mTempPicker,mPlayListPath,"",NULL);
                    }
                    //sElementCount can be zero if the playlist contains no paths to valid files
                    while ( (mElementCount != 0) && mElementCount <= mUpcomingSongsListSize )
                        ShowPlaylistElements(mTempPicker,upcomingFile);
                }
            }
            fclose(upcomingFile);
        }   
    }
    else
    {   
        if (    picker->GetNumMovies() == 0 
                && !picker->mStopFlag 
                && ::strcmp(mPlayMode, "sequential") 
            )
        {   picker->CleanList();
            PopulatePickerFromFile(picker,mPlayListPath,"",NULL);
        }       
    }
}

void MP3Broadcaster::UpdateCurrentFile(char *thePick)
{
    if (NULL == thePick)
        return;
        
    // save currently playing song to .current file 
    if (mShowCurrent) 
    {   FILE *currentFile = fopen(mCurrentFile, "w");
        if(currentFile)
        {
            qtss_fprintf(currentFile,"u=%s\n",thePick);

            fclose(currentFile);
        }   
    }
}


void MP3Broadcaster::PrintPlaylistElement(PLDoubleLinkedListNode<SimplePlayListElement> *node,void *file)
{   
    sBroadcaster->mElementCount ++;
    if (sBroadcaster->mElementCount <= sBroadcaster->mUpcomingSongsListSize)    
    {   
        char* thePick = node->fElement->mElementName;
        qtss_fprintf((FILE*)file,"%s\n", thePick);
    }
}

void MP3Broadcaster::ShowPlaylistElements(PlaylistPicker *picker,FILE *file)
{
    if (mElementCount > mUpcomingSongsListSize)     
        return;
        
    UInt32  x;
    for (x= 0;x<picker->GetNumBuckets();x++)
    {   
        picker->GetBucket(x)->ForEach(PrintPlaylistElement,file);
    }
}

void MP3Broadcaster::PreFlightOrBroadcast( bool preflight, bool daemonize, bool showMovieList, bool currentMovie, bool checkMP3s, const char* errorlog)
{
    PlaylistPicker*     picker = NULL;
    PlaylistPicker*     insertPicker = NULL;
    MP3FileBroadcaster  fileBroadcaster(&mSocket, mBitRate, mFrequency);
    MP3MetaInfoUpdater* metaInfoUpdater = NULL;
    
    long                moviePlayCount;
    char*               thePick = NULL;
    long                numMovieErrors;
   	bool				didAtLeastOneMoviePlay = false;
    int err;
        
        mPreflight = preflight;
        
    if ( preflight )
        ShowSetupParams();

    if (preflight)
        picker = new PlaylistPicker(false);             // make sequential picker, no looping
    else
    {   
        picker = MakePickerFromConfig(); // make  picker according to parms
        mTempPicker =  new PlaylistPicker(false);
        insertPicker = new PlaylistPicker(false);
        insertPicker->mRemoveFlag = true;
    }
    
    // initial call uses empty string for path, NULL for loop detection list
    (void)PopulatePickerFromFile( picker, mPlayListPath, "", NULL );
    
    if ( preflight )
    {
        if ( picker->mNumToPickFrom == 1 )
            qtss_printf( "\nThere is one movie in the Playlist.\n\n" );
        else
            qtss_printf( "\nThere are (%li) movies in the Playlist.\n\n", (long) picker->mNumToPickFrom );
    }   
    
    if ( picker->mNumToPickFrom == 0 )
    {   
        qtss_printf( "- MP3Broadcaster setup failed: There are no movies to play.\n" );
        mNumErrors++;
        goto bail;
    }
    

    // check that we have enough movies to cover the recent movies list.
    if ( preflight )
    {
        if (  !strcmp( mPlayMode, "weighted_random" ) ) // this implies "random" play
        {   
            if ( mRecentSongsListSize >=  picker->mNumToPickFrom )
            {
                mRecentSongsListSize = picker->mNumToPickFrom - 1;
                qtss_printf("- PlaylistBroadcaster Warning:\n  The recent_movies_list_size setting is greater than \n");
                qtss_printf("  or equal to the number of movies in the playlist.\n" );
                                mNumWarnings++;
            }
        }
    }
    
    // create the log file
    mLog = new MP3BroadcasterLog( mWorkingDirPath,  mLogFile, mLogging );
    
//  if ( !PreflightTrackerFileAccess( R_OK | W_OK ) )
//      goto bail;

    if (!preflight)
    {
        err = ConnectToServer();
        if (err)
        {       
                        if (err == EACCES)
                            qtss_printf("- MP3Broadcaster: Disconnected from Server. Bad password or mount point\n  Exiting.\n" );
                        else
                            qtss_printf("- MP3Broadcaster: Couldn't connect to server\n  Exiting.\n" );
            mNumErrors++;
            goto bail;
        }
    }
    
    //Unless the Debug command line option is set, daemonize the process at this point
    if (daemonize)
    {   
#ifndef __Win32__
        qtss_printf("- MP3Broadcaster: Started in background.\n");
        
        // keep the same working directory..
#ifdef __sgi__
		if (::_daemonize(_DF_NOCHDIR, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO) != 0)
#else
        if (::daemon( 1, 0 ) != 0)
#endif
        {
            qtss_printf("- MP3Broadcaster:  System error (%i).\n", errno);
            mNumErrors++;
            goto bail;
        }

#endif      
    }
    
        if (daemonize && (errorlog != NULL))
    {   
                freopen(errorlog, "a", stdout);
        ::setvbuf(stdout, (char *)NULL, _IONBF, 0);
    }
        
        if (!preflight)
        {
                metaInfoUpdater = new MP3MetaInfoUpdater(mPassword, mMountPoint, mSocket.GetRemoteAddr(), mPort);
                metaInfoUpdater->Start();
                fileBroadcaster.SetInfoUpdater(metaInfoUpdater);
    }
        
        // ^ daemon must be called before we Open the log and tracker since we'll
    // get a new pid, our files close,  ( does SIGTERM get sent? )
    
    if (( mLog ) && ( mLogging ))
        mLog->EnableLog( false ); // don't append ".log" to name for PLB
    
    if ( mLogging && !mLog->IsLogEnabled() )
    {
        if (  mLog->LogDirName() && *mLog->LogDirName() )
            qtss_printf("- MP3Broadcaster: The log file failed to open.\n  ( path: %s/%s )\n  Exiting.\n", mLog->LogDirName(), mLog->LogFileName() );
        else
            qtss_printf("- MP3Broadcaster: The log file failed to open.\n  ( path: %s )\n  Exiting.\n", mLog->LogFileName() );
        
        mNumErrors++;
        goto bail;
    }
    
    
    if (mPIDFile[0] != 0)
    {
        if(!FileCreateAndCheckAccess(mPIDFile))
        {
            FILE *pidFile = fopen(mPIDFile, "w");
            if(pidFile)
            {
                qtss_fprintf(pidFile,"%d\n",getpid());
                fclose(pidFile);
            }   
        }
    }
    
//  AddOurPIDToTracker( bcastSetupFilePath ); // <-- exits on failure

    if ( !preflight )
        mLog->LogInfo( "MP3Broadcaster started." );
    else
        mLog->LogInfo( "MP3Broadcaster preflight started." );

    if(!preflight)
    {   
        CreateCurrentAndUpcomingFiles();
        SendXAudioCastHeaders();
    }
        
    if (!preflight)
    {
        // check the frequency of the first song
        fileBroadcaster.PlaySong( picker->GetFirstFile(), NULL, true, true );
    }
    
    moviePlayCount = 0;
    numMovieErrors = 0;
 	didAtLeastOneMoviePlay = false;
           
    while (true)
    {   
    
        if (!showMovieList && !preflight)
            UpdatePlaylistFiles(picker, insertPicker);
             
        if (NULL != insertPicker)
            thePick = insertPicker->PickOne(); 
            
        if (NULL == thePick && (NULL != picker))
            thePick = picker->PickOne();
                
        if ( (thePick != NULL) && (!preflight && showMovieList) )
        {
            // display the picks in debug mode, but not preflight
            qtss_printf( "[%li] %s picked\n", moviePlayCount, thePick );
        }
        
        
        if ( !showMovieList )
        {
            int playError;
                
            if(!preflight)
            {   UpdateCurrentFile(thePick);
                /* if playlist is about to run out repopulate it */
                if  (   !::strcmp(mPlayMode, "sequential_looped") )
                {   
                    if(NULL == thePick &&!picker->mStopFlag)
                    {   
                        if (picker->GetNumMovies() == 0) 
                            break;
                        else
                            continue;
                    }
                        
                }
            }

            if (thePick == NULL)
                break;
            
            ++moviePlayCount;
            
            if(!preflight)
            {
            
                SInt64 startTime = OS::Milliseconds();
                SInt64 endTime = 0;
                playError = fileBroadcaster.PlaySong( thePick, mCurrentFile );
                endTime = OS::Milliseconds();
                
				//were we able to actually play the movie?
				didAtLeastOneMoviePlay = didAtLeastOneMoviePlay || (playError == 0);
				
				//ok, we've reached the end of the current playlist
				if (picker->GetNumMovies() == 0)
				{
					//If we determine that every one of the movies resulted in an error, then bail
					if (!didAtLeastOneMoviePlay)
					{
						qtss_printf("Quitting:  Playlist contains no valid files.\n");
						mLog->LogInfo( "Quitting:  Playlist contains no valid files.\n" );
						goto bail;
					}
					else
					{
						didAtLeastOneMoviePlay = false;
					}
				}
                
                mLog->LogMediaData(thePick, fileBroadcaster.GetTitle(), 
                                            fileBroadcaster.GetArtist(), 
                                            fileBroadcaster.GetAlbum(),
                                            (UInt32) ((endTime - startTime)/1000L),
                                            playError);
            }
            else
            {
                playError = fileBroadcaster.PlaySong( thePick, NULL, preflight, !checkMP3s );
            }
            
            if (playError == MP3FileBroadcaster::kConnectionError)
            {
                // do something
                mNumErrors++;
                goto bail;
            }
            
            if ( !preflight && (playError != 0))
            {    
                 qtss_printf("File %s : ", thePick);
                 mLog->LogMediaError( thePick, MapErrorToString(playError), NULL );
            }
            else if (playError != 0)
            {
                qtss_printf("File %s : ", thePick);
                MapErrorToString(playError);
                numMovieErrors++;
                mNumWarnings++;
            }
        }
        

        delete [] thePick;
        thePick = NULL;
    } //while (true)
    
    remove(mCurrentFile);
    remove(mUpcomingFile);  

    if ( preflight )
    {
        char    str[256];   
        qtss_printf( " - "  );
        if (numMovieErrors == 1)
            strcpy(str, "MP3Broadcaster found one problem MP3 file.");
        else
            qtss_sprintf( str, "MP3Broadcaster found %li problem MP3 files." , numMovieErrors );
        qtss_printf( "%s\n", str );
        if (mLog) mLog->LogInfo( str );
        
        if (numMovieErrors == moviePlayCount)
        {
            qtss_printf("There are no valid MP3s to play\n");
            mNumErrors++;
        }
    }
    
bail:

    delete picker;

    if (metaInfoUpdater)
        delete metaInfoUpdater;
        
        Cleanup();

#ifndef __Win32__
/*
    if ( sgTrackingSucceeded )
    {
        // remove ourselves from the tracker
        // this is the "normal" remove, also signal handlers
        // may remove us.
        
        BCasterTracker  tracker( sgTrackerFilePath );
        
        tracker.RemoveByProcessID( getpid() );
        tracker.Save();
    }
*/
#endif
}

PlaylistPicker* MP3Broadcaster::MakePickerFromConfig()
{
    // construct a PlaylistPicker object using options set
    
    PlaylistPicker *picker = NULL;
    
    if ( !::strcmp( mPlayMode, "weighted_random" ) )
    {
        picker = new PlaylistPicker( 10, mRecentSongsListSize );
        
    }
    else if ( !::strcmp( mPlayMode, "sequential_looped" ) )
    {           
        picker = new PlaylistPicker(true);
        picker->mRemoveFlag = true;
    }
    else if ( !::strcmp( mPlayMode, "sequential" ) )
    {           
        picker = new PlaylistPicker(false);
        picker->mRemoveFlag = true;
    }
    
    return picker;
}

bool MP3Broadcaster::FileCreateAndCheckAccess(char *theFileName)
{
    FILE *theFile;


#ifndef __Win32__
    if(access(theFileName, F_OK)){
        /* file does not exist  - create and set rights */
        theFile = ::fopen(theFileName, "w+");
        if(theFile){
            /* make sure everybody has r/w access to file */
            (void)::chmod(theFileName, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
            (void)::fclose(theFile);
        }else return 1;
    }else{
        /* file exists - check rights */
        if(access(theFileName, R_OK|W_OK))return 2;
    }

#else

    theFile = ::fopen(theFileName,"a");

    if (theFile) ::fclose(theFile);

#endif


    return 0;
}

int MP3Broadcaster::ConnectToServer()
{
    UInt32 addr;
    addr = SocketUtils::ConvertStringToAddr(mIPAddr);
    if (addr == INADDR_NONE)
    {
        struct hostent* theHostent = ::gethostbyname(mIPAddr);      
        if (theHostent != NULL)
            addr = ntohl(*(UInt32*)(theHostent->h_addr_list[0]));
        else
            qtss_printf("Couldn't resolve address %s\n", mIPAddr);
    }
        
    OS_Error err = mSocket.Open();
    err = mSocket.Connect(addr, mPort);
        
    if (err == 0)
    {
        char buffer1[512];
        char buffer2[512];
        UInt32 len;

        StringTranslator::EncodeURL(mMountPoint, strlen(mMountPoint) + 1, buffer1, sizeof(buffer1));
        if (strlen(buffer1) + strlen(mPassword) + 12 <= 512)
        {
            if (mUseICY)
            {
                // in the ICY protocol there is no mountpoint
                // the reflector assumes a default mountpoint of "/"
                qtss_sprintf(buffer2, "%s\n", mPassword);
            }
            else
            {
                // if the mountpoint does not start with a "/" we prepend one for the
                // reflector before we send the broadcast request.
                if (buffer1[0] == '/')
                    qtss_sprintf(buffer2, "SOURCE %s %s\n\n", mPassword, buffer1);
                else
                    qtss_sprintf(buffer2, "SOURCE %s /%s\n\n", mPassword, buffer1);
            }
            mSocket.Send(buffer2, strlen(buffer2), &len);
        }
        else return -1;
                
                char buffer3[3];
                len = 0;
                mSocket.Read(buffer3, 2, &len);
                buffer3[2] = '\0';
                if (::strcmp(buffer3, "OK") != 0)
                   err = EACCES; 
        }
        
    return err;
}

int MP3Broadcaster::SendXAudioCastHeaders()
{
    char buffer[1024];
    char temp[256];
    UInt32 len;
    
    if (mUseICY)
    {
        qtss_sprintf(buffer, "icy-name:%s\n", mName);
        qtss_sprintf(temp, "icy-genre:%s\n", mGenre);
        strcat(buffer, temp);
        qtss_sprintf(temp, "icy-pub:%s\n", "0");
        strcat(buffer, temp);
        qtss_sprintf(temp, "icy-url:%s\n", mURL);
        strcat(buffer, temp);
    }
    else
    {
        qtss_sprintf(buffer, "x-audiocast-name:%s\n", mName);
        qtss_sprintf(temp, "x-audiocast-genre:%s\n", mGenre);
        strcat(buffer, temp);
        qtss_sprintf(temp, "x-audiocast-public:%s\n", "0");
        strcat(buffer, temp);
        qtss_sprintf(temp, "x-audiocast-description:%s\n", "");
        strcat(buffer, temp);
    }    
    mSocket.Send(buffer, strlen(buffer), &len);
    
    return 0;
}

void MP3Broadcaster::ShowSetupParams()
{
    qtss_printf( "\n" );
    qtss_printf( "Configuration Settings\n" );
    qtss_printf( "----------------------------\n" );
    qtss_printf( "Destination address %s:%d\n", mIPAddr, mPort );
    qtss_printf( "MP3 bitrate %d\n", mBitRate );
    qtss_printf( "play_mode  %s\n", mPlayMode );
    qtss_printf( "recent_movies_list_size  %d\n", mRecentSongsListSize );
    qtss_printf( "playlist_file  %s\n", mPlayListPath );
    qtss_printf( "working_dir  %s\n", mWorkingDirPath );
    qtss_printf( "logging  %d\n", mLogging );
    qtss_printf( "log_file  %s\n", mLogFile );
    qtss_printf( "max_upcoming_list_size  %d\n", mUpcomingSongsListSize );
    qtss_printf( "show_current  %d\n", mShowCurrent );
    qtss_printf( "show_upcoming  %d\n", mShowUpcoming );
    qtss_printf( "use_icy  %d\n", mUseICY );
    qtss_printf( "broadcast_name \"%s\"\n", mName);
    qtss_printf( "broadcast_genre \"%s\"\n", mGenre);
    qtss_printf( "broadcast_mount_point \"%s\"\n", mMountPoint);
    qtss_printf( "broadcast_password \"XXXXX\"\n");
    qtss_printf( "max_err_file_k_size %lu\n", qtss_getmaxprintfcharsinK());
    qtss_printf( "\n" );
}

void MP3Broadcaster::RemoveFiles()
{
    if (mPIDFile[0] != 0)
    {
        remove(mPIDFile);
    }

    remove(mStopFile);
    remove(mReplaceFile);
    remove(mInsertFile);
    remove(mCurrentFile);
    remove(mUpcomingFile);  
}

char* MP3Broadcaster::MapErrorToString(int error)
{
    char* result = NULL;
        
    if (error == MP3FileBroadcaster::kBadFileFormat)
        result = "Bad file format.";
    else if (error == MP3FileBroadcaster::kWrongFrequency)
        result = "Encoded at wrong frequency.";
    else if (error == MP3FileBroadcaster::kWrongBitRate)
        result = "Doesn't use desired bit rate.";
    else if (error == MP3FileBroadcaster::kCouldntOpenFile)
        result = "Couldn't open file.";
        
    if (result != NULL)
        qtss_printf("%s\n", result);
    
    return result;
}

void MP3Broadcaster::Cleanup(bool signalHandler)
{
        if (mCleanupDone)
            return;
            
        mCleanupDone = true;
        
        if (signalHandler)
        {
            mNumErrors++;
            qtss_printf("- MP3Broadcaster: Disconnected from Server while playing. Exiting.\n");
        }
        
        if (mPreflight)
    {
        qtss_printf("Warnings: %ld\n", mNumWarnings);
        qtss_printf("Errors: %ld\n", mNumErrors);
    }
        else
        {
            qtss_printf("Broadcast Warnings: %ld\n", mNumWarnings);
            qtss_printf("Broadcast Errors: %ld\n", mNumErrors);
        }
    
    RemoveFiles();
        
        if (mLog)
        {
            if ( mPreflight )
                mLog->LogInfo( "MP3Broadcaster preflight finished." );
            else
                mLog->LogInfo( "MP3Broadcaster finished." );
    }
        
        //  mLog = NULL; // protect the interrupt handler and just let it die don't delete because it is a task thread
}
