// ----------------------------------------------------------------------------
//
//  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 <math.h>
#include "styles.h"
#include "captwin.h"
#include "global.h"


#define MT X_menuwin_item::TITLE
#define MS X_menuwin_item::SPACE
#define ML X_menuwin_item::LAST


X_menuwin_item  Captwin::_mtype_def [] = 
{
    { "Ch / Type  ",   MT,     0 },
    { "1",             0,      0 },
    { "2",             0,      0 },
    { "2 / MS",        0,      0 },
    { "3",             0,      0 },
    { "3 / Amb B",     0,      0 },
    { "4",             0,      0 },
    { "4 / Amb B",     0,      0 },
    { "4 / Amb A",     0,      0 },
    { "Remove",   MS | ML | 1, 0 }
};


U32 Captwin::_ftype [] = 
{
    0,
    Impdata::TYPE_RAW + 1,
    Impdata::TYPE_RAW + 2,
    Impdata::TYPE_MS,
    Impdata::TYPE_RAW + 3,
    Impdata::TYPE_AMB_B3,
    Impdata::TYPE_RAW + 4,
    Impdata::TYPE_AMB_B4,
    Impdata::TYPE_AMB_A,
    ~0u
};


Captwin::Captwin (X_window *parent, X_resman *resman, X_callback *callb, Filewin *filewin) :
    X_window (parent, XPOS, YPOS, XDEF, YDEF, XftColors.main_bg->pixel),
    _callb (callb),
    _filewin (filewin),
    _xs (XDEF),
    _ys (YDEF),
    _state (INIT),
    _count (0),
    _extproc (0),
    _shdata (0)
{
    char     s [1024];
    X_hints  H;

    _xatom = XInternAtom (dpy (), "WM_DELETE_WINDOW", True);
    XSetWMProtocols (dpy (), win (), &_xatom, 1);
    _xatom = XInternAtom (dpy (), "WM_PROTOCOLS", True);

    H.rname (resman->rname ());
    H.rclas (resman->rclas ());
    x_apply (&H); 

    sprintf (s, "%s-%s  [%s] Capture", PROGNAME, VERSION, resman->rname ());
    x_set_title (s);

    if (resman->getb (".jack", 0) || resman->getb (".alsa", 0))
    {
        _extproc = new Extproc ("aliki", SHM_SIZE);
        if (_extproc->shmsize () == 0)
	{
    	    fprintf (stderr, "Can't create shared memory !\n");
            _state = EXIT; 
            return;
	}

	_shdata = (Shdata *)(_extproc->shmdata ());
	_shdata->_nplay = 8; 
	_shdata->_ncapt = 8; 
	_shdata->_state = 0; 
	if (resman->getb (".jack", 0))
	{
	    strcpy (_shdata->_ident, resman->rname ());
	    _shdata->_state = Shdata::A_JACK; 
	}
	else
	{
	    const char *p;
	    p = resman->get (".fsamp", 0);
	    if (! p || sscanf (p, "%d", &_shdata->_fsamp) != 1) _shdata->_fsamp = 48000;
	    p = resman->get (".period", 0);
	    if (! p || sscanf (p, "%d", &_shdata->_fsize) != 1) _shdata->_fsize = 1024;
	    p = resman->get (".nfrags", 0);
	    if (! p || sscanf (p, "%d", &_shdata->_nfrag) != 1) _shdata->_nfrag = 2;
	    p = resman->get (".device", 0);
	    strcpy (_shdata->_ident, (p) ? p : "hw:0,0");
	    _shdata->_class = SCHED_FIFO;
	    _shdata->_prior = -30;
	    _shdata->_state = Shdata::A_ALSA; 
	}

	if (_extproc->start (resman->get (".rtexec", 0), "aliki-rt"))
	{
	    fprintf (stderr, "Can't start audio process !\n");
	    _state = EXIT; 
	    return;
	}

    }
    else _state = EXIT;
}


Captwin::~Captwin (void)
{
    delete _extproc;
}


void Captwin::show (void)
{
    if (_state == EXIT) return;
    x_mapraised ();
}


void Captwin::hide (void)
{
    x_unmap ();
}


void Captwin::stop (void)
{
    if (_state != EXIT) _state = TERM;
}


void Captwin::handle_time (void)
{
    if (_state != EXIT)
    {
        _shdata->_par2cld.eput (Shdata::X_TICK); 

        switch (_shdata->_cld2par.eget ())
	{
        case Shdata::X_EXIT:
            _state = TERM; 
            _callb->handle_callb (CB_CAPT_NOTAV, 0, 0);
	    break;

	case Shdata::X_INIT:
            makewin ();
            _shdata->_par2cld.eput (Shdata::X_STOP);
            _callb->handle_callb (CB_CAPT_AVAIL, 0, 0);
	    break;

	case Shdata::X_STOP:
            setstate (STOP); 
            break;

	case Shdata::X_TEST:
            setstate (TEST); 
            break;

	case Shdata::X_CAPT:
            setstate (CAPT); 
            break;

	case Shdata::X_TRIG:
            _btrig->set_stat (2);
            _count = 8;
  	    system (_titcom->text ());
            break;
	}
    }

    if (_count)
    {
        if (--_count == 0) _btrig->set_stat (0);
    }

    switch (_state)
    {
    case INIT:
        break;

    case STOP:
    case TEST:
    case CAPT:
        for (int i = 0; i < (int)(_shdata->_ncapt); i++) _meter [i]->set_val (_shdata->_meter [i]);
	break;

    case TERM:
        _shdata->_par2cld.eput (Shdata::X_EXIT); 
        _state = EXIT;
        break;

    default:
	;
    }
}


void Captwin::handle_event (XEvent *E)
{
    switch (E->type)
    {
    case Expose:
        expose ((XExposeEvent *) E);
        break;  

    case ClientMessage:
        xcmesg ((XClientMessageEvent *) E);
        break;
    }
}


void Captwin::expose (XExposeEvent *E)
{
    if (E->count == 0) redraw ();
}


void Captwin::xcmesg (XClientMessageEvent *E)
{
    if (E->message_type == _xatom) hide ();
}


void Captwin::handle_callb (int k, X_window *W, _XEvent *E )
{
    int b;

    switch (X_callback::cb_class (k))
    {
    case X_callback::BUTTON:
    {
        X_button *B = (X_button *) W;
        if (X_callback::cb_event (k) == X_button::PRESS)
	{
            b = B->cbid ();

            if (b >= B_PLCH)
	    {
		b -= B_PLCH;
                if (B->stat ())
		{
		    B->set_stat (0);
                    _shdata->_opsel &= ~(1 << b);
		}
                else
		{
                    B->set_stat (2);
                    _shdata->_opsel |= (1 << b); 
		}
	    }
            else if (b >= B_CPCH)
	    {
		_chan0 = b - B_CPCH;
                _mtype->show ();
	    }
	    else switch (b)
	    {
	    case B_SSEL:
                _filewin->open (this, CB_FILE_LOAD, "sweep", ".ald", 0);
		break;

	    case B_STOP:
		_shdata->_par2cld.eput (Shdata::X_STOP);
		break;

	    case B_CAPT:
		if (_state == STOP) startcapt ();
		break;

	    case B_TEST:
		if (_state == STOP) starttest ();
                if (_state == TEST) _shdata->_par2cld.eput (Shdata::X_STOP);
		break;

	    case B_TRIG:
		system (_titcom->text ());
		break;
	    }
	}
	break;
    }
    case X_callback::TEXTIP:
    {
        X_textip *T = (X_textip *) W;
        if (X_callback::cb_event (k) == X_textip::BUT)
	{
	    if (_state == STOP && T != _ttplay) T->enable (); 
	}
	break;
    }  
    case X_callback::MENU:
    {
        X_menuwin *M = (X_menuwin *) W;
        switch (X_callback::cb_event (k))
        {
	case X_menuwin::SEL:
            addgroup (_ftype [M->selection ()]); 
	    break;
	}
        M->hide ();
	break;
    }  
    default:
	switch (k)
	{
	case CB_FILE_LOAD:
	    file_load ();
	    break;
	}
    }
}


void Captwin::redraw (void)
{
    int      dx, dy;
    X_draw   D (dpy (), _wlevel->win (), dgc (), 0);

    dx = Slevel.length () + 11;
    dy = 20 * _shdata->_ncapt + 31;
    D.setfunc (GXcopy);     
    D.setcolor (Colors.meter_ds);
    D.move (0, dy - 1);
    D.draw (0, 0);
    D.draw (dx - 1, 0);
    D.setcolor (Colors.meter_ls);
    D.move (dx, 1);
    D.draw (dx, dy);
    D.draw (1, dy);
}


void Captwin::makewin (void)
{
    int      i, n, x, y;
    char     s [8];
    X_hints  H;

    Bst0.size.x = 17;
    Bst0.size.y = 17; 
    x = 10;
    y = 5;
    add_text (x + 350, y, 170, 17, "Capture files", 0);
    _mtype = new X_menuwin (this, this, &Mst1, x + 310, y + 10, _mtype_def);

    n = _shdata->_ncapt;
    _wlevel = new X_subwin (this, x, y, Slevel.length () + 12, n * 20 + 32, Mlevel.bg);
    _wlevel->x_map ();
    _wlevel->x_add_events (ExposureMask);
    (new X_hscale (_wlevel, &Slevel, 6, 2, 15, 0))->x_map ();
    y += 18;
    for (i = 0; i < n; i++)
    {
	_meter [i] = new X_hmeter (_wlevel, &Mlevel, &Slevel, 6, 16 + i * 20, 20);
	_meter [i]->set_ref (0.5f);
	_meter [i]->x_map ();
	_chbut [i] = new X_ibutton (this, this, &Bst0, x + 280, y, disp ()->image1515 (X_display::IMG_DN));
        _chbut [i]->x_map ();
	_chbut [i]->set_cbid (B_CPCH + i);
	_chlab [i] = new X_textip (this, 0,    &Tst0, x + 305, y,  30, 17,  7);
	_grlab [i] = new X_textip (this, this, &Tst1, x + 340, y, 200, 19, 31);
	y += 20;
    }
    (new X_hscale (_wlevel, &Slevel, 6, 14 + n * 20, 15, 0))->x_map ();

    x += 10;
    y += 20;
    add_text (x + 120, y, 140, 17, "Sweep file", 1);
    (_bsweep = new X_ibutton (this, this, &Bst0, x + 270, y, disp ()->image1515 (X_display::IMG_DN), B_SSEL))->x_map ();
    (_tsweep = new X_textip (this, 0, &Tst1, x + 295, y, 235, 17, 1023))->x_map (); 
    _tsweep->set_align (1);

    y += 24;
    add_text (x + 120, y, 140, 17, "Equal. file", 1);
    (_bequal = new X_ibutton (this, this, &Bst0, x + 270, y, disp ()->image1515 (X_display::IMG_DN), B_ESEL))->x_map ();
    (_tequal = new X_textip (this, 0, &Tst1, x + 295, y, 235, 17, 1023))->x_map (); 
    _tequal->set_align (1);
 
    y += 35;
    add_text (x, y, 110, 17, "Ouput level", 1);
    (_tlevel = new X_textip (this, this, &Tst1, x + 120, y, 50, 17, 15))->x_map (); 
    _tlevel->set_align (1);
    _tlevel->set_text ("-20");

    y += 25;
    Bst0.size.x = 28;
    Bst0.size.y = 18;
    add_text (x, y, 110, 17, "Output channel", 1);
    for (i = 0; i < (int)(_shdata->_nplay); i++)
    {
	sprintf (s, "%d", i + 1);
        _bplay [i] =  new X_tbutton (this, this, &Bst0, x + 120 + 30 * i, y, s, 0, 0); 
        _bplay [i]->x_map ();
        _bplay [i]->set_cbid (B_PLCH + i);
    }

    x -= 10;
    y += 36;      
    add_text (x, y,      120, 19, "Sweep time", 1);
    add_text (x, y + 24, 120, 19, "Capture time", 1);
    add_text (x, y + 48, 120, 19, "Iterations", 1);
    add_text (x, y + 72, 120, 19, "Idle time", 1);
    add_text (x, y + 96, 120, 19, "Trigger command", 1);
    (_ttplay = new X_textip (this, this, &Tst1, x + 130, y,      100, 17, 16))->x_map (); 
    (_ttcapt = new X_textip (this, this, &Tst1, x + 130, y + 24, 100, 17, 16, _ttplay))->x_map (); 
    (_tniter = new X_textip (this, this, &Tst1, x + 130, y + 48, 100, 17, 16, _ttcapt))->x_map (); 
    (_ttskip = new X_textip (this, this, &Tst1, x + 130, y + 72, 100, 17, 16, _tniter))->x_map (); 
    (_titcom = new X_textip (this, this, &Tst1, x + 130, y + 96, 200, 17, 64, _ttskip))->x_map (); 
    _ttplay->set_text ("0.0");
    _ttcapt->set_text ("15.0");
    _ttskip->set_text ("1.0");
    _tniter->set_text ("1");

    Bst0.size.x = 60;
    Bst0.size.y = 25; 
    add_text (x + 250, y + 10, 140, 17, "Capture", 1);
    (_bstop = new X_tbutton (this, this, &Bst0, x + 410, y +  6, "Stop",  0, B_STOP))->x_map ();
    (_bcapt = new X_tbutton (this, this, &Bst0, x + 475, y +  6, "Start", 0, B_CAPT))->x_map ();
    (_btest = new X_tbutton (this, this, &Bst0, x + 410, y + 36, "Test",  0, B_TEST))->x_map ();
    (_btrig = new X_tbutton (this, this, &Bst0, x + 410, y + 90, "Trig",  0, B_TRIG))->x_map ();

    _ys = y + 140;
    H.size (_xs, _ys);
    H.minsize (_xs, _ys);
    H.maxsize (_ys, _ys);
    H.position (XPOS, YPOS);
    x_apply (&H); 
    x_resize (_xs, _ys);
}


void Captwin::setstate (int s)
{
    switch (s)
    {
    case STOP:
    case TERM:
    case EXIT:
	_bstop->set_stat (1);
	_btest->set_stat (0);
	_bcapt->set_stat (0);
	break;

    case TEST:
	_bstop->set_stat (0);
	_btest->set_stat (2);
	_bcapt->set_stat (0);
	break;

    case CAPT:
	_bstop->set_stat (0);
	_btest->set_stat (0);
	_bcapt->set_stat (2);
	break;
    }
    _state = s;
}


void Captwin::add_text (int xp, int yp, int xs, int ys, const char *text, int align)
{
    (new X_textln (this, &Tst0, xp, yp, xs, ys, text, align))->x_map ();
}


void Captwin::addgroup (U32 type)
{
    int c, i, n;

    remgroup ();
    if (type == (U32)(~0)) return;
    c = _chan0;
    n = type & 255;
    for (i = 1; i < n; i++)
    {
	if ((i + c == 8) || _shdata->_ipdat [i + c]._type) return;
    }
    _grlab [c]->set_text ("");
    _grlab [c]->x_map ();
    _grlab [c]->enable ();
    _chlab [c]->set_text (Impdata::channame (type, 0));    
    _chlab [c]->x_map ();
    _chbut [c]->set_stat (2);
    for (i = 1; i < n; i++)
    {
      _chlab [c + i]->set_text (Impdata::channame (type, i));    
      _chlab [c + i]->x_map ();
      _chbut [c + i]->x_unmap ();
    }
    _shdata->_ipdat [c]._type = type; 
    _shdata->_ipdat [c]._chan0 = c; 
}


void Captwin::remgroup ()
{
    U32 f;
    int c, i, n;

    c = _chan0;
    f = _shdata->_ipdat [c]._type; 
    n = f & 255;
    if (n)
    {
        _grlab [c]->x_unmap ();
	_chlab [c]->x_unmap (); 
        _chbut [c]->set_stat (0);
        for (i = 1; i < n; i++)
	{
	    _chlab [c + i]->x_unmap ();
            _chbut [c + i]->x_map ();
	}
    }
    _shdata->_ipdat [c]._type = 0; 
}


void Captwin::askdata (X_textip *T)
{
    T->set_text ("???");
    XSetInputFocus (dpy (), T->win (), RevertToPointerRoot, CurrentTime);
}


void Captwin::starttest (void)
{
    float    gg;
    if ((sscanf (_tlevel->text (), "%f", &gg) != 1) || (gg < -120.0f) || (gg > -10.0f)) 
    {
	askdata (_tlevel);
        return;
    }
    _shdata->_level = powf (10.0f, 0.05f * gg);
    _shdata->_par2cld.eput (Shdata::X_TEST);
}


void Captwin::startcapt (void)
{
    int      i, ni;
    float    tp, tc, ti, gg;
    char     s [64];

    if ((sscanf (_tlevel->text (), "%f", &gg) != 1) || (gg < -120.0f) || (gg > 0.0f)) 
    {
	askdata (_tlevel);
        return;
    }
    tp = (float)(_shdata->_oplen) / _shdata->_fsamp;  
    if ((sscanf (_ttcapt->text (), "%f", &tc) != 1) || (tc < tp) || (tc > 45.0f)) 
    {
	askdata (_ttcapt);
        return;
    }
    if ((sscanf (_ttskip->text (), "%f", &ti) != 1) || (ti < 1.0f) || (ti > 30.0f)) 
    {
	askdata (_ttskip);
        return;
    }
    if ((sscanf (_tniter->text (), "%d", &ni) != 1) || (ni < 1) || (ni > 1000.0f)) 
    {
	askdata (_tniter);
        return;
    }

    for (i = 0; i < (int)(_shdata->_ncapt); i++)
    {
        if (_shdata->_ipdat [i]._type)
        {
	    if (sscanf (_grlab [i]->text (), "%s", s) != 1)
	    {
		askdata (_grlab [i]);
                return;
	    }
            strcpy (_shdata->_ipdat [i]._name, s);
	}
    }

    ti += tc;
    _shdata->_iplen = (int)(tc * _shdata->_fsamp + 0.5f);    
    _shdata->_itlen = (int)(ti * _shdata->_fsamp + 0.5f);    
    _shdata->_niter = ni;
    _shdata->_level = powf (10.0f, 0.05f * gg);

    disp ()->nofocus ();
    _shdata->_par2cld.eput (Shdata::X_CAPT);
}


void Captwin::file_load (void)
{
    Impdata F;
    char    s [1024];

    snprintf (s, 1024, "%s/%s", _filewin->wdir (), _filewin->file ());
    if (F.open_read (s)) return;
    _shdata->_oplen = F.n_fram ();
    F.close ();
    strcpy (_shdata->_opdat._name, s);
    _tsweep->set_text (s);
    sprintf (s, "%5.3lf\n", (float)(_shdata->_oplen) / _shdata->_fsamp);
    _ttplay->set_text (s);
}


void Captwin::set_sess (const char *name)
{
    if (_state == EXIT) return;
    if (strlen (name) > 1023) return;
    strcpy (_shdata->_sessdir, name);
}


