// ----------------------------------------------------------------------------
//
//  Copyright (C) 2024 Fons Adriaensen <fons@linuxaudio.org>
//    
//  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 3 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.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------


#include <math.h>
#include <stdio.h>
#include <string.h>
#include "jfwcapt.h"


Jfwcapt::Jfwcapt (const char *client_name,
		  const char *server_name,
		  int nchan) :
    _nchan (nchan),
    _fchan (0),
    _fbuff (0)
{
    if (nchan > MAXCHAN) nchan = MAXCHAN;
    else if (nchan < 0) nchan = 0;
    if (open_jack (client_name, server_name, nchan + 1, 0) || init ())
    {
        _state = FAILED;
        return;
    }
}


Jfwcapt::~Jfwcapt (void)
{
    fini ();
}


int Jfwcapt::init (void)
{
    int  c;
    char s [16];

    for (c = 0; c < _nchan; c++)
    {
	sprintf (s, "in_%d", c);
        if (create_inp_port (c, s)) return 1;
    }	
    if (create_inp_port (_nchan, "_sync")) return 1;
    _state = SILENCE;
    return 0;
}


void Jfwcapt::fini (void)
{
    _state = INITIAL;
    close_file ();
    close_jack ();
}


int Jfwcapt::close_file (void)
{    
    if (_state > SILENCE) return 1;
    _afile.close ();
    delete _fbuff;
    _fbuff = 0;
    _fchan = 0;
    return 0;
}


int Jfwcapt::create_file (const char *fname, int fchan, const char *ftype)
{
    int   type, form, dith, v;
    char  opts [64];
    char  *p, *q;

    if (_state != SILENCE) return 1;
    close_file ();
    if (! fname) return 0;

    if ((fchan < 1) || (fchan > 1024)) return 1;

    // Decode string versions of form, type and dith.
    // This should really go into the Audiofile class.
    type = Audiofile::TYPE_WAV;
    form = Audiofile::FORM_24BIT;
    dith = 0;
    if (ftype)
    {
	strncpy (opts, ftype, 64);
	opts [63] = 0;
	q = 0;
        p = strtok_r (opts, ",", &q);
	while (p)
	{
	    if      ((v = _afile.enc_type (p)) >= 0) type = v;
	    else if ((v = _afile.enc_form (p)) >= 0) form = v;
            else if ((v = _afile.enc_dith (p)) >= 0) dith = v;
	    else return 1;
	    p = strtok_r (0, ",", &q);
	}
    }

    // Open file for writing.
    if (_afile.open_write (fname, type, form, jack_rate (), fchan)) return 1;
    _afile.set_dither (dith);
    _fchan = fchan;
    _fbuff = new float [_fchan * jack_size ()];

    return 0;
}


void Jfwcapt::jack_freewheel (int run)
{
}


int Jfwcapt::jack_process (int nframes)
{
    int              c, i, n;
    unsigned char   *sync;
    float           *inpp, *p;

    if (_state < SILENCE) return 0;

    // Check message on _sync input.
    sync = (unsigned char *) jack_port_get_buffer (_inp_ports [_nchan], nframes);
    n = 0;
    if (   sync [0] == 'F' && sync [1] == 'W'
        && sync [2] == 0 && sync [3] == 1
        && sync [6] == 0 && sync [7] == 1)
    {
	// Number of samples to record.
	n = sync [4] + (sync [5] << 8);
    }

    // Check start of recording.
    if (_state == SILENCE)
    {
        if (n && _fbuff) _state = PROCESS;
    }

    // If not recording return.
    if (_state != PROCESS) return 0;

    // Check for end of recording.
    if (n == 0)
    {
        _state = SILENCE;
        close_file ();
    }

    // Write samples to file.
    for (c = 0; c < _fchan; c++)
    {
	if (c < _nchan)
	{
            // Interleave samples.
            inpp = (float *) jack_port_get_buffer (_inp_ports [c], nframes);
            for (i = 0, p = _fbuff + c; i < n; i++, p += _fchan) *p = inpp [i];
	}
	else
	{
	    // More file channels than jack ports, clear.
            for (i = 0, p = _fbuff + c; i < n; i++, p += _fchan) *p = 0;
	}
    }
    _afile.write (_fbuff, (int64_t) n);
    
    return 0;
}

