// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: i_cdmus.c,v 1.2 2003/08/18 18:26:06 hurdler Exp $
//
// Copyright (C) 1998-2000 by DooM Legacy Team.
//
// 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
// of the License, 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.
//
//
// $Log: i_cdmus.c,v $
// Revision 1.2  2003/08/18 18:26:06  hurdler
// stable 1.41
//
// Revision 1.2  2000/02/27 00:42:11  hurdler
// fix CR+LF problem
//
// Revision 1.1.1.1  2000/02/22 20:32:33  hurdler
// Initial import into CVS (v1.29 pr3)
//
//
// DESCRIPTION:
//      cd music interface (uses bcd library)
//      
//-----------------------------------------------------------------------------

#include "bcd.h"        // CD-Audio library by Brennan Underwood

#include "../doomdef.h"
#include "../i_sound.h"
#include "../command.h"
#include "../i_system.h"
#include "../s_sound.h"

// ------
// protos
// ------
void Command_Cd_f(void);

//======================================================================
//                   CD AUDIO MUSIC SUBSYSTEM
//======================================================================

byte cdaudio_started = 0;       // for system startup/shutdown

#define MAX_CD_TRACKS 256
static boolean cdPlaying = false;
static int cdPlayTrack;         // when cdPlaying is true
static boolean cdLooping = false;
static byte cdRemap[MAX_CD_TRACKS];
static boolean cdEnabled = true;        // cd info available
static boolean cdValid;         // true when last cd audio info was ok
static boolean wasPlaying;
static int cdVolume = 0;        // current cd volume (0-31)

// 0-31 like Music & Sfx, though CD hardware volume is 0-255.
consvar_t cd_volume = { "cd_volume", "31", CV_SAVE, soundvolume_cons_t };

// allow Update for next/loop track
// some crap cd drivers take up to
// a second for a simple 'busy' check..
// (on those Update can be disabled)
consvar_t cdUpdate = { "cd_update", "1", CV_SAVE, CV_OnOff };

// hour,minutes,seconds
static char *hms(int hsg)
{
    int hours, minutes, seconds;
    static char s[9];

    seconds = hsg / 75;
    minutes = seconds / 60;
    seconds %= 60;
    hours = minutes / 60;
    minutes %= 60;
    if (hours > 0)
        sprintf(s, "%d:%02d:%02d", hours, minutes, seconds);
    else
        sprintf(s, "%2d:%02d", minutes, seconds);
    return s;
}

void Command_Cd_f(void)
{
    char *s;
    int i, j;

    if (!cdaudio_started)
        return;

    if (COM_Argc() < 2)
    {
        CONS_Printf("cd [on] [off] [remap] [reset] [open]\n" "   [info] [play <track>] [loop <track>]\n" "   [stop] [resume]\n");
        return;
    }

    s = COM_Argv(1);

    // activate cd music
    if (!strncmp(s, "on", 2))
    {
        cdEnabled = true;
        return;
    }

    // stop/deactivate cd music
    if (!strncmp(s, "off", 3))
    {
        if (cdPlaying)
            I_StopCD();
        cdEnabled = false;
        return;
    }

    // remap tracks
    if (!strncmp(s, "remap", 5))
    {
        i = COM_Argc() - 2;
        if (i <= 0)
        {
            CONS_Printf("CD tracks remapped in that order :\n");
            for (j = 1; j < MAX_CD_TRACKS; j++)
                if (cdRemap[j] != j)
                    CONS_Printf(" %2d -> %2d\n", j, cdRemap[j]);
            return;
        }
        for (j = 1; j <= i; j++)
            cdRemap[j] = atoi(COM_Argv(j + 1));
        return;
    }

    // reset the CD driver, useful on some odd cd's
    if (!strncmp(s, "reset", 5))
    {
        cdEnabled = true;
        if (cdPlaying)
            I_StopCD();
        for (i = 0; i < MAX_CD_TRACKS; i++)
            cdRemap[i] = i;
        bcd_reset();
        cdValid = bcd_get_audio_info();
        return;
    }

    // any other command is not allowed until we could retrieve cd information
    if (!cdValid)
    {
        CONS_Printf("CD is not ready.\n");
        return;
    }

    if (!strncmp(s, "open", 4))
    {
        if (cdPlaying)
            I_StopCD();
        bcd_open_door();
        cdValid = false;
        return;
    }

    if (!strncmp(s, "info", 4))
    {
        int totaltime = 0;

        if (!bcd_get_audio_info())
        {
            CONS_Printf("Error getting audio info: %s\n", bcd_error());
            cdValid = false;
            return;
        }

        cdValid = true;

        if (lowest_track == 0)
            CONS_Printf("No audio tracks\n");
        else
        {
            // display list of tracks
            // highlight current playing track
            for (i = lowest_track; i <= highest_track; i++)
            {
                CONS_Printf("%s%2d. %s  %s\n", cdPlaying && (cdPlayTrack == i) ? "\2 " : " ", i, tracks[i].is_audio ? "audio" : "data ", hms(tracks[i].len));
                if (tracks[i].is_audio)
                    totaltime += tracks[i].len;
            }

            if (totaltime)
                CONS_Printf("\2Total time : %s\n", hms(totaltime));
        }
        if (cdPlaying)
        {
            CONS_Printf("%s track : %d\n", cdLooping ? "looping" : "playing", cdPlayTrack);
        }
        return;
    }

    if (!strncmp(s, "play", 4))
    {
        I_PlayCD(atoi(COM_Argv(2)), false);
        return;
    }

    if (!strncmp(s, "stop", 4))
    {
        I_StopCD();
        return;
    }

    if (!strncmp(s, "loop", 4))
    {
        I_PlayCD(atoi(COM_Argv(2)), true);
        return;
    }

    if (!strncmp(s, "resume", 4))
    {
        I_ResumeCD();
        return;
    }

    CONS_Printf("cd command '%s' unknown\n", s);
}

// pause cd music
void I_StopCD(void)
{
    if (!cdaudio_started || !cdEnabled)
        return;

    bcd_stop();

    wasPlaying = cdPlaying;
    cdPlaying = false;
}

// continue after a pause
void I_ResumeCD(void)
{
    if (!cdaudio_started || !cdEnabled)
        return;

    if (!cdValid)
        return;

    if (!wasPlaying)
        return;

    bcd_resume();
    cdPlaying = true;
}

void I_ShutdownCD(void)
{
    int rc;

    if (!cdaudio_started)
        return;

    I_StopCD();

    rc = bcd_close();
    if (!rc)
        CONS_Printf("Error shuting down cd\n");
}

void I_InitCD(void)
{
    int rc;
    int i;

    rc = bcd_open();

    if (rc >= 0x200)
    {
        CONS_Printf("MSCDEX version %d.%d\n", rc >> 8, rc & 255);

        I_AddExitFunc(I_ShutdownCD);
        cdaudio_started = true;
    }
    else
    {
        if (!rc)
            CONS_Printf("%s\n", bcd_error());

        cdaudio_started = false;
        return;
    }

    // last saved in config.cfg
    i = cd_volume.value;
    I_SetVolumeCD(0);   // initialize to 0 for some odd cd drivers
    I_SetVolumeCD(i);   // now set the last saved volume

    for (i = 0; i < MAX_CD_TRACKS; i++)
        cdRemap[i] = i;

    if (!bcd_get_audio_info())
    {
        CONS_Printf("\2CD Init: No CD in player.\n");
        cdEnabled = false;
        cdValid = false;
    }
    else
    {
        cdEnabled = true;
        cdValid = true;
    }

    COM_AddCommand("cd", Command_Cd_f);
}

// loop/go to next track when track is finished (if cd_update var is true)
// update the volume when it has changed (from console/menu)
// TODO: check for cd change and restart music ?
//
void I_UpdateCD(void)
{
    int newVolume;
    int now;
    static int last;            //game tics (35th of second)

    if (!cdaudio_started)
        return;

    now = I_GetTime();
    if ((now - last) < 10)      // about 1/4 second
        return;
    last = now;

    // update cd volume changed at console/menu
    newVolume = cd_volume.value & 31;

    if (cdVolume != newVolume)
        I_SetVolumeCD(newVolume);

    // slow drivers exit here
    if (!cdUpdate.value)
        return;

    if (cdPlaying)
    {
        if (!bcd_audio_busy())
        {
            cdPlaying = false;
            if (cdLooping)
                I_PlayCD(cdPlayTrack, true);
            else
            {
                // play the next cd track, or restart the first
                cdPlayTrack++;
                if (cdPlayTrack > highest_track)
                    cdPlayTrack = lowest_track;
                while (!tracks[cdPlayTrack].is_audio && cdPlayTrack < highest_track)
                    cdPlayTrack++;
                I_PlayCD(cdPlayTrack, true);
            }
        }
    }

}

//
void I_PlayCD(int track, boolean looping)
{
    if (!cdaudio_started || !cdEnabled)
        return;

    if (!cdValid)
        return;

    track = cdRemap[track];

    if (cdPlaying)
    {
        if (cdPlayTrack == track)
            return;
        I_StopCD();
    }

    if (track < lowest_track || track > highest_track)
    {
        //CONS_Printf ("\2CD Audio: wrong track number %d\n", track);
        // suppose there are not enough tracks for game levels..
        // then do a modulo so we always get something to hear
        track = (track % (highest_track - lowest_track + 1)) + 1;
        //return;
    }

    cdPlayTrack = track;

    if (!tracks[track].is_audio)
    {
        CONS_Printf("\2CD Play: not an audio track\n");
        return;
    }

    cdLooping = looping;

    if (!bcd_play_track(track))
    {
        CONS_Printf("CD Play: could not play track %d\n", track);
        cdValid = false;
        cdPlaying = false;
        return;
    }

    cdPlaying = true;

}

// volume : logical cd audio volume 0-31 (hardware is 0-255)
int I_SetVolumeCD(int volume)
{
    int hardvol;

    if (!cdaudio_started || !cdEnabled)
        return 0;

    // translate to hardware volume
    volume &= 31;

    hardvol = ((volume + 1) << 3) - 1;  //highest volume is 255
    if (hardvol <= 8)
        hardvol = 0;    //lowest volume is ZERO

    cdVolume = volume;

    if (bcd_set_volume(hardvol))
    {
        CV_SetValue(&cd_volume, volume);

        return 1;
    }
    else
        return 0;
}
