// ----------------------------------------------------------------------------
//
//  Copyright (C) 2003-2013 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 <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sndfile.h>
#include <math.h>
#include "mainthr.h"


Mainthr::Mainthr (Shdata *shdata) :
    A_thread ("main"),
    _state (Shdata::X_INIT),
    _count (0),
    _shdata (shdata)
{
    _shdata->_mode  = Shdata::M_MEAS;
    _shdata->_opsel = 0;
    _shdata->_opdat._stat = Shdata::S_NONE;
    _shdata->_opdat._queue = 0;
    for (int i = 0; i < 8; i++)
    {
	_shdata->_ipdat [i]._stat = Shdata::S_NONE;
	_shdata->_ipdat [i]._type = 0;
	_shdata->_ipdat [i]._chan0 = 0;
	_shdata->_ipdat [i]._queue = 0;
    }
}


Mainthr::~Mainthr (void)
{
}


void Mainthr::stop (void)
{
    _state = Shdata::X_EXIT;
}


void Mainthr::thr_main (void)
{
    int c, r;

    set_time (0);
    inc_time (100000);
    while (1)
    {
	get_event_timed ();
	inc_time (50000);
        c = _shdata->_par2cld.eget ();
        do
	{
	    if (c == Shdata::X_EXIT)
            {
		_shdata->_mode = 0;
		stopcapt (true);
		_shdata->_cld2par.eput (_state = Shdata::X_EXIT);
                return;
	    }
	    else if (c == Shdata::X_TICK) _count = 0;
	    else if (++_count == 40) return;

	    switch (_state)
	    {
	    case Shdata::X_INIT:
		if (c == Shdata::X_STOP)
		{
		    _shdata->_cld2par.eput (Shdata::X_STOP);
		    _shdata->_mode = Shdata::M_MEAS; 
		    _state = Shdata::X_STOP;
		}
		break;

	    case Shdata::X_STOP:
		if (c == Shdata::X_TEST)
		{
		    if (inittest ())
		    {
			_shdata->_mode |= Shdata::M_PLAY; 
			_shdata->_cld2par.eput (_state = Shdata::X_TEST);
		    }
		}
		else if (c == Shdata::X_CAPT)
		{
		    if (initcapt ())
		    {
			_shdata->_mode |= Shdata::M_PLAY | Shdata::M_CAPT;
			_shdata->_cld2par.eput (_state = Shdata::X_CAPT);
		    }
		}
		break;

	    case Shdata::X_TEST:
		if (c == Shdata::X_STOP)
		{
		    stopplay ();
		    _shdata->_mode = Shdata::M_MEAS;
		    _shdata->_cld2par.eput (_state = Shdata::X_STOP);
		}
		break;

	    case Shdata::X_CAPT:
		if (c == Shdata::X_STOP)
		{
		    _shdata->_mode = Shdata::M_MEAS;
		    stopcapt (true);
		    _shdata->_cld2par.eput (_state = Shdata::X_STOP);
		}
		else
		{
		    r = iohandle ();
		    if (r & TRIG) _shdata->_cld2par.eput (Shdata::X_TRIG);
		    if (r & DONE)
		    {
			_shdata->_mode = Shdata::M_MEAS;
			stopcapt (false);
			_shdata->_cld2par.eput (_state = Shdata::X_STOP);
		    }
		}
		break;
	    }

   	    c = _shdata->_par2cld.eget ();
	}
        while (c);
    }
}


int Mainthr::inittest (void)
{
    int    i, n;
    float  *p, w, m, g;

    n = 2 * _shdata->_fsamp;
    n -= n % _shdata->_fsize;
    g = _shdata->_level;
    _shdata->_opdat._queue = new Fltlfq (n, 1);    
    p = _shdata->_opdat._queue->wr_ptr ();
    w = 2 * M_PI * 880.0 / n;
    m = 2 * M_PI *  15.0 / n;
    for (i = 0; i < n; i++)
    {
	*p++ = g * sinf (i * w + 20.0f * sinf (i * m));
    }           
    return 1;
}


int Mainthr::initcapt (void)
{
    int     i, n;
    char    s [1024];
    Ipdata  *D;

    _shdata->_error [0] = 0;
    n = 2 * _shdata->_fsamp;
    n -= n % _shdata->_fsize;

    if (_opfile.open_read (_shdata->_opdat._name)) 
    {
	_shdata->_opdat._stat = Shdata::S_ERROR;
        strcpy (_shdata->_error, "Can't open sweep file");
        return 0;
    }
    if (_opfile.type () != Impdata::TYPE_SWEEP)
    {
        _opfile.close ();
	_shdata->_opdat._stat = Shdata::S_ERROR;
        strcpy (_shdata->_error, "Sweep file has wrong type");
        return 0;
    }
    _shdata->_opdat._stat = Shdata::S_OPEN;
    _shdata->_opdat._queue = new Fltlfq (n, 1);    
    if (_shdata->_oplen > (int)(_opfile.n_fram ())) _shdata->_oplen = _opfile.n_fram ();

    for (i = 0; i < 8; i++)
    {
        D = _shdata->_ipdat + i;
	if (D->_type)
	{
            sprintf (s, "%s/capture/%s", _shdata->_sessdir, D->_name);
	    Impdata::checkext (s);
            _ipfile [i].set_type (D->_type);
            _ipfile [i].set_rate (_shdata->_fsamp, 1);
            _ipfile [i].set_n_sect (_shdata->_niter);
            _ipfile [i].set_n_fram (_shdata->_iplen);   
            if (_ipfile [i].open_write (s))
	    {
	        D->_stat = Shdata::S_ERROR;
                sprintf (_shdata->_error, "Can't open capture file '%s'", s); 
                stopcapt (true);
                return 0;
	    }
            D->_stat = Shdata::S_OPEN;
            D->_queue = new Fltlfq (n, D->_type & 255);
        }
    }    

    _op_it = 0;
    _op_si = 0;
    _ip_it = 0;
    _ip_si = 0;
    rdopdata (n);
    return 1;
}


int Mainthr::iohandle (void)
{
    int      n, r;
    Fltlfq  *Q = _shdata->_opdat._queue;

    n = Q->wr_frames ();
    r = 0;
    if (n > Q->size () / 4) return rdopdata (n) | wripdata (n);
    return 0;
}


int Mainthr::rdopdata (int nfrm)
{
    int     i, k, n, r;
    float   g;
    float   *p;    
    Fltlfq  *Q = _shdata->_opdat._queue;
    
    g = _shdata->_level;
    r = 0;
    while (nfrm)
    {
        n = Q->wr_nowrap (nfrm);
        if (n > 4096) n = 4096;
        p = Q->wr_ptr ();
        nfrm -= n;
        while (n)
	{
	    if (_op_it < _shdata->_niter)
	    {
		k = _shdata->_oplen - _op_si;
		if (k > 0)
		{
		    if ( k > n) k = n;
		    if (_op_si == 0) _opfile.locate (0);
		    _opfile.read_ext (k, _buff);
                    for (i = 0; i < k; i++) _buff [i] = g * _buff [2 * i];          
                    memcpy (p, _buff, k * sizeof (float));
		}                                         
		else
		{
		    k = _shdata->_itlen - _op_si;
		    if (k > n) k = n;
		    memset (p, 0, k * sizeof (float));
		}       
		_op_si += k;
		if (_op_si == _shdata->_itlen)
		{
		    _op_si = 0;
		    _op_it++;
		}                
	    }
	    else 
	    {
		k = n;
		memset (p, 0, k * sizeof (float));
	    }
            Q->wr_commit (k);
            p += k;
	    n -= k;
	}
    }
    return r;
}


int Mainthr::wripdata (int nfrm)
{
    int     i, k, n, r;
    Ipdata  *D;
    Fltlfq  *Q;

    r = 0;
    while (nfrm)
    {
        k = _shdata->_iplen - _ip_si;
	if (k > 0)
	{
            if (k > nfrm) k = nfrm;
            for (i = 0, D = _shdata->_ipdat; i < 8; i++, D++)
            {
                Q = D->_queue;
                if (Q)
		{
                    if (_ip_si == 0) _ipfile [i].locate (_ip_it);
                    n = Q->rd_nowrap (k);
                    _ipfile [i].write_ext (n, Q->rd_ptr ());
                    Q->rd_commit (n);
                    if (n < k)
		    {
                        _ipfile [i].write_ext (k - n, Q->rd_ptr ());
                        Q->rd_commit (k - n);
		    }
		}
	    }
            nfrm -= k;
            _ip_si += k;
            if (_ip_si == _shdata->_iplen)
	    {  
                r |= TRIG;
                if (_ip_it == _shdata->_niter - 1) return  r | DONE;
	    }
	}
        else
	{
            k = _shdata->_itlen - _ip_si;
            if (k > nfrm) k = nfrm;
            for (i = 0, D = _shdata->_ipdat; i < 8; i++, D++)
            {
                Q = D->_queue;
                if ( Q) Q->rd_commit (k);
            }
            nfrm -= k;
            _ip_si += k;
            if (_ip_si == _shdata->_itlen)
	    {
                _ip_si = 0;
                _ip_it++;
	    }
	}            
    }
    return r;
}


void Mainthr::stopplay (void)
{
    set_time ();
    inc_time (100000);
    get_event_timed ();
    _opfile.close ();
    delete _shdata->_opdat._queue;
    _shdata->_opdat._queue = 0;
}


void Mainthr::stopcapt (bool rm)
{
    int     i;
    char    s [1024];
    Ipdata  *D;

    stopplay ();
    for (i = 0; i < 8; i++)
    {
        D = _shdata->_ipdat + i;
	if (D->_stat == Shdata::S_OPEN)
	{
            _ipfile [i].close (); 
            D->_stat = Shdata::S_NONE;
            delete D->_queue;
            D->_queue = 0;
            if (rm)
	    {
               sprintf (s, "%s/capture/%s", _shdata->_sessdir, D->_name);
               unlink (s);
	    }
	}   
    }
}


