/*  Tucnak - VHF contest log
    Copyright (C) 2002-2018  Ladislav Vaiz <ok1zia@nagano.cz>

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    version 2 as published by the Free Software Foundation.

*/

    
#include "header.h"


#include "alsa.h"
#include "dsp.h"
#include "fifo.h"
#include "language2.h"
#include "oss.h"
#include "pa.h"
#include "rtlsdr.h"
#include "rc.h"
#include "sndf.h"
#include "sndpipe.h"
#include "sdrc.h"
#include "tsdl.h"


struct dsp *init_dsp(enum dsp_type type, enum dsp_cfg_group dcg){
    struct dsp *dsp;

	progress(VTEXT(T_INIT_SOUNDCARD));		

    dsp = g_new0(struct dsp, 1);   
    dsp->type = type;
#if defined(HAVE_OSS) || defined(HAVE_SWRADIO)
    dsp->fd = -1;
#endif
#if (!defined(HAVE_OSS)) && (!defined(HAVE_ALSA) && !defined(HAVE_SNDFILE))
	dsp->type = DSPT_PORTAUDIO;
#endif
#if defined(HAVE_ALSA) && (!defined(HAVE_PORTAUDIO))
    if (dsp->type == DSPT_PORTAUDIO) dsp->type = DSPT_ALSA;
#endif

#ifdef HAVE_SNDFILE
    dsp->set_format = dummy_dsp_set_format;
#endif
    dsp->open   = dummy_dsp_open;
    dsp->close2 = dummy_dsp_close;
    dsp->write  = dummy_dsp_write;
    dsp->read   = dummy_dsp_read;
    dsp->reset  = dummy_dsp_reset;
    dsp->sync   = dummy_dsp_sync;
    dsp->set_source     = dummy_dsp_set_source;
    dsp->set_plevel     = dummy_dsp_set_plevel;
	dsp->set_sdr_format = dummy_dsp_set_sdr_format;

    switch(dsp->type){
        case DSPT_OSS:
#ifdef HAVE_OSS
#ifdef HAVE_SNDFILE
            dsp->set_format = oss_set_format;
#endif
            dsp->open   = oss_open;
            dsp->close2  = oss_close;
            dsp->write  = oss_write;
            dsp->read   = oss_read;
            dsp->reset  = oss_reset;
            dsp->sync   = oss_sync;
            dsp->set_source = oss_set_source;
            dsp->set_plevel = oss_set_plevel;

			           
			switch (dcg){
				case DCG_SSBD:
				    dsp->oss_filename = g_strdup(cfg->ssbd_dsp);
				    dsp->oss_mixer = g_strdup(cfg->ssbd_mixer);

					if (cfg->ssbd_oss_src){
						dsp->source = g_strdup(cfg->ssbd_oss_src);
					}else{
						dsp->source = g_strdup(oss_recsrc2source(cfg->ssbd_recsrc));
					}
					break;
				case DCG_SDR:
					zinternal("OSS not supported for SDR");
					break;
			}
#else
            log_addf(VTEXT(T_NO_OSS));
#endif
            break;
        case DSPT_ALSA:
#ifdef HAVE_ALSA  
#ifdef HAVE_SNDFILE
            dsp->set_format = alsa_set_format;
#endif
            dsp->open   = alsa_open;
            dsp->close2  = alsa_close;
            dsp->write  = alsa_write;
            dsp->read   = alsa_read;
            dsp->reset  = alsa_reset;
            dsp->sync   = alsa_sync;
            dsp->set_sdr_format = alsa_set_sdr_format;
            dsp->set_source = alsa_set_source;
            dsp->set_plevel = alsa_set_plevel;
            dsp->pcm_opened = 0;
            snd_lib_error_set_handler(z_alsa_error_handler);
			switch (dcg){
				case DCG_SSBD:
                    dsp->pcm_play = g_strdup(cfg->ssbd_pcm_play);
                    dsp->pcm_rec = g_strdup(cfg->ssbd_pcm_rec);
					dsp->alsa_mixer = g_strdup(cfg->ssbd_alsa_mixer);
					dsp->alsa_src = g_strdup(cfg->ssbd_alsa_src);
					break;
				case DCG_SDR:
                    dsp->pcm_play = g_strdup(cfg->sdr_pcm_play);
					dsp->pcm_rec = g_strdup(cfg->sdr_pcm_rec);
					break;
			}
#else
            log_addf(VTEXT(T_NO_ALSA));
#endif            
            break;
        /*case DSPT_SSBD:*/
        case DSPT_PORTAUDIO:
#ifdef HAVE_PORTAUDIO
#ifdef HAVE_SNDFILE
            dsp->set_format = pa_set_format;
#endif
            dsp->open   = pa_open2;
            dsp->close2 = pa_close2;
            dsp->write  = pa_write2;
            dsp->read   = pa_read2;
            dsp->reset  = pa_reset2;
            dsp->sync   = pa_sync2;
#ifdef HAVE_SNDFILE
			dsp->set_sdr_format = pa_set_sdr_format;
#endif
            if (!pa_initialised){
				Pa_Initialize();
				pa_initialised = 1;
			}
			switch (dcg){
				case DCG_SSBD:
					dsp->pa_play = cfg->ssbd_pa_play;
					dsp->pa_rec = cfg->ssbd_pa_rec;
                    dsp->pa_play_hi = cfg->ssbd_pa_play_hi;
                    dsp->pa_rec_hi = cfg->ssbd_pa_rec_hi;
                    dsp->pa_play_s = g_strdup(cfg->ssbd_pa_play_s);
                    dsp->pa_rec_s = g_strdup(cfg->ssbd_pa_rec_s);
					break;
				case DCG_SDR:
					dsp->pa_play = cfg->sdr_pa_play;
					dsp->pa_rec = cfg->sdr_pa_rec;
					break;
			}
#else
            log_addf(VTEXT(T_NO_PORTAUDIO));
#endif
            break;
		case DSPT_SNDFILE:
#ifdef HAVE_SNDFILE
            dsp->open   = sndfile_open;
            dsp->close2 = sndfile_close;
            dsp->write  = sndfile_write;
            dsp->read   = sndfile_read;
            dsp->reset  = sndfile_reset;
            dsp->sync   = sndfile_sync;
            dsp->set_format		= sndfile_set_format;
			dsp->set_sdr_format = sndfile_set_sdr_format;
			switch (dcg){
				case DCG_SSBD:
					break;
				case DCG_SDR:
					dsp->rec_filename = g_strdup(cfg->sdr_sndfilename);
					dsp->play_filename = g_strdup(cfg->sdr_af_filename);
					break;
			}
#else
            log_addf(VTEXT(T_NO_SNDFILE));
#endif
            break;
		case DSPT_SNDPIPE:
#ifdef USE_SDR
            dsp->open   = sndpipe_open;
            dsp->close2 = sndpipe_close;
            dsp->write  = sndpipe_write;
            dsp->read   = sndpipe_read;
            dsp->reset  = sndpipe_reset;
            dsp->sync   = sndpipe_sync;
#ifdef HAVE_SNDFILE
            dsp->set_format		= sndpipe_set_format;
#endif
			dsp->set_sdr_format = sndpipe_set_sdr_format;
			switch (dcg){
				case DCG_SSBD:
					break;
				case DCG_SDR:
					break;
			}
			dsp->pipe[0] = -1;
			dsp->pipe[1] = -1;
#else
            log_addf(VTEXT(T_NO_SNDPIPE));
#endif
            break;
		case DSPT_SDRC:
            dsp->open   = sdrc_open;
            dsp->close2 = sdrc_close;
            dsp->write  = sdrc_write;
            dsp->read   = sdrc_read;
            dsp->reset  = sdrc_reset;
            dsp->sync   = sdrc_sync;
#ifdef HAVE_SNDFILE
            dsp->set_format		= sdrc_set_format;
#endif
			dsp->set_sdr_format = sdrc_set_sdr_format;
			break;
        case DSPT_SWRADIO:
#ifdef HAVE_SWRADIO
            dsp->open   = swradio_open;
            dsp->close2 = swradio_close;
            dsp->read   = swradio_read;
#else
            log_addf("No support for /dev/swradio");
#endif
            break;
        case DSPT_RTLSDR:
#ifdef HAVE_LIBRTLSDR
            dsp->open   = rtlsdr_open2;
            dsp->close2 = rtlsdr_close2;
            dsp->read   = rtlsdr_read;
			dsp->set_sdr_format = rtlsdr_set_sdr_format;
			dsp->rtlsdr_device = g_strdup("0");
#else
            log_addf("No support for librtlsdr");
#endif
            break;
		case DSPT_HAMLIB:
#ifdef HAVE_HAMLIB
			// keep dummy
#else			
			log_addf("No support for hamlib");
#endif
			break;
        default:
            zinternal("bad dsp->type");
            break;
            
    }
    
	switch (dcg){
		case DCG_SSBD:
			dsp->period_time = cfg->ssbd_period_time;
			dsp->plev = cfg->ssbd_plev;
			dsp->rlev = cfg->ssbd_rlev;
			break;
		case DCG_SDR:
			dsp->plev = -1;
			dsp->rlev = -1;
			break;
	}
    return dsp;
}

void free_dsp(struct dsp *dsp){
    if (!dsp) return;
	dbg("free_dsp(%p) type=%d\n", dsp, dsp->type);

	progress(VTEXT(T_TERMINATING_SOUNDCARD));		
    dsp->close2(dsp);
#ifdef HAVE_OSS
    zg_free0(dsp->oss_filename);
    zg_free0(dsp->oss_mixer);
#endif
#ifdef HAVE_ALSA
	zg_free0(dsp->alsa_mixer);
	zg_free0(dsp->alsa_src);
	zg_free0(dsp->pcm_play);
	zg_free0(dsp->pcm_rec);
#endif    
#ifdef HAVE_PORTAUDIO
    if (dsp->type == DSPT_PORTAUDIO) {
		//Pa_Terminate();
		//pa_initialised = 0;
	}
    zg_free0(dsp->pa_play_s);
    zg_free0(dsp->pa_rec_s);
#endif
#ifdef HAVE_SNDFILE
	if (dsp->sndfile) sf_close(dsp->sndfile);
	zg_free0(dsp->sfinfo);
	zg_free0(dsp->play_filename);
	zg_free0(dsp->rec_filename);
#endif
#ifdef HAVE_LIBRTLSDR
	zg_free0(dsp->rtlsdr_device);
	zg_free0(dsp->rtlsdr_buf);
#endif
    zg_free0(dsp->name);
    zg_free0(dsp->source);
	g_free(dsp);
}


#ifdef HAVE_SNDFILE
int dummy_dsp_set_format(struct dsp *dsp, SF_INFO *sfinfo, int rec){
    return 0;
}
#endif

int dummy_dsp_open(struct dsp *dsp, int rec){
    return 0;
}

int dummy_dsp_close(struct dsp *dsp){
    return 0;
}

int dummy_dsp_get_bufsize(struct dsp *dsp){
	return -1;
}

int dummy_dsp_write(struct dsp *dsp, void *data, int len){
    return 0;
}

int dummy_dsp_read(struct dsp *dsp, void *data, int len){
    return 0;
}

int dummy_dsp_reset(struct dsp *dsp){
    return 0;
}

int dummy_dsp_sync(struct dsp *dsp){
    return 0;
}

int dummy_dsp_set_source(struct dsp *dsp){
    return 0;
}

int dummy_dsp_set_plevel(struct dsp *dsp){
    return 0;
}

int dummy_dsp_set_sdr_format(struct dsp *dsp, int blocksize, int speed, int rec){
	return 0;
}


int dsp_write_empty(struct dsp *dsp){
    int ret;
    void *buf = g_new0(char, dsp->bytes);
    ret = dsp->write(dsp, buf, dsp->frames);
    g_free(buf); 
    return ret;
}
