// ----------------------------------------------------------------------------
//
//  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 "mainwin.h"
#include "styles.h"


Mainwin::Mainwin (X_window *parent, X_resman *resman, X_callback *callb, Filewin *filewin, ITC_ctrl *itcctrl) :
    X_window (parent, XPOS, YPOS, XDEF, YDEF, XftColors.main_bg->pixel),
    _callb (callb),
    _xp (XPOS),
    _yp (YPOS),
    _xs (XDEF),
    _ys (YDEF),
    _state (INIT),
    _count (0),
    _flock (false),
    _doall (false),
    _itcctrl (itcctrl),
    _filewin (filewin),
    _disp (0),
    _data1 (0),
    _data2 (0),
    _convol (0),
    _awfilt (0),
    _prog0 (0),
    _prog1 (0)
{
    X_hints   H;
    char      s [1024];

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

    H.size (XDEF, YDEF);
    H.minsize (XMIN, YMIN);
    H.maxsize (disp ()->xsize (), disp ()->ysize ());
    H.position (XPOS, YPOS);
    H.rname (resman->rname ());
    H.rclas (resman->rclas ());
    x_apply (&H); 

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

    *_file1 = 0;
    *_file2 = 0;
    _base1 = _file1;

    _splashw = new Splashwin (this, (_xs - 400) / 2, (_ys - 300) / 2, 400, 300);
    _splashw->x_set_win_gravity (CenterGravity);
    _editwin = new Editwin (this, this, LMAR, TMAR, _xs - LMAR, _ys - TMAR);
    _filtwin = new Filtwin (this, this, LMAR, TMAR, _xs - LMAR, _ys - TMAR);
    _exptwin = new Exptwin (parent, resman, this);
    _progwin = new Progwin (this, resman);

    makewin ();
    _modwin = _ifwview;
    _modwin->x_map ();
    x_map ();
}


Mainwin::~Mainwin (void)
{
    delete _data1;
    delete _data2;
    delete _splashw;
    delete _editwin;
    delete _filtwin;
}


void Mainwin::stop (void)
{
    _state = TERM;
}    


void Mainwin::handle_time (void)
{
    if (++_count == 100) _splashw->x_unmap ();
    switch (_state)
    {
    case INIT:
        _splashw->x_mapraised (); 
        set_view ();
	break;

    case TERM:
        _state = EXIT; 
        break;

    default:
	;
    }
}


void Mainwin::handle_event (XEvent *E)
{
    switch (E->type)
    {
    case ConfigureNotify:
        resize ((XConfigureEvent *) E);
        break;
    case ClientMessage:
        xcmesg ((XClientMessageEvent *) E);
        break;
    }
}


void Mainwin::resize (XConfigureEvent *E)
{
    if ((_xs != E->width) || (_ys != E->height))
    {
	_xs = E->width;
	_ys = E->height;
        _editwin->resize (_xs - LMAR, _ys - TMAR);
        _filtwin->resize (_xs - LMAR, _ys - TMAR);
    }
    _xp = E->x;
    _yp = E->y;
}


void Mainwin::xcmesg (XClientMessageEvent *E)
{
    if (E->message_type == _xatom) _callb->handle_callb (CB_MAIN_CLOSE, 0, 0);
}


void Mainwin::handle_callb (int k, X_window *W, XEvent *E)
{
    int c, e;

    c = X_callback::cb_class (k);
    e = X_callback::cb_event (k);
    switch (c)
    {
    case X_callback::TEXTIP:
    {
	if (_flock || _prog0) break;
	switch (e)
	{
        case X_textip::BUT:
	    XSetInputFocus (dpy (), W->win (), RevertToPointerRoot, CurrentTime);
	    break;

        case X_textip::MODIF:
            modif (W);
	    break;
	}
    }
    case X_callback::BUTTON:
    {
	X_button     *B = (X_button *) W;
	XButtonEvent *X = (XButtonEvent *) E;

        if (_flock || _prog0) break;
        k = B->cbid ();
        if (e == X_button::RELSE)
	{
	    switch (k)
	    {
            case B_CHAN:
            case B_CHAN + 1:
            case B_CHAN + 2:
            case B_CHAN + 3:
                settrace (k - B_CHAN, ((XButtonEvent *) E)->button);
                break; 

            case B_FRMI:
		next_sect (-1);
		break;

            case B_FRPL:
		next_sect (+1);
		break;

            case B_SESS:
		_flock = 1;
		_filewin->open (this, CB_FILE_SESS, ".", "", 0);
		break;

            case B_LOAD:
		_flock = 1;
		_filewin->open (this, CB_FILE_LOAD, ".", ".ald", 0);
		break;

            case B_SAVE:
		break;

            case B_SONE:
            case B_SALL:
		_flock = true;
		_doall = k == B_SALL;
                switch (_state)
		{
		case CONV:
                    _filewin->open (this, CB_FILE_SAVE, "impresp", ".ald", _doall ? _base1 : 0);
                    break;
		case EDIT:
                    _filewin->open (this, CB_FILE_SAVE, "edited",  ".ald", _doall ? _base1 : 0);
                    break;
                default:
                    _filewin->open (this, CB_FILE_SAVE, ".", ".ald", 0);
		}
		break;

            case B_IMPT:
		break;

            case B_EXPT:
		if (_data1 == 0) break;
		_flock = 1;
                _exptwin->show (); 
		break;

            case B_SGEN:
                _callb->handle_callb (CB_OPEN_SGEN, this, 0); 
		break;

            case B_CAPT:
                _callb->handle_callb (CB_OPEN_CAPT, this, 0); 
		break;

            case B_CONV:
                set_conv ();
		break;

            case B_EDIT:
                set_edit ();
		break;

            case B_SINT:
//                set_sint ();
 		break;

            case B_FILT:
//                set_filt ();
 		break;

            case B_CANC:
                cancel ();
		break;

            case B_APPL:
                apply ();
		break;

            case B_FILE:
                _tcfil = _tcfil1;
                _filewin->open (this, CB_FILE_LOAD, "sweep", ".ald", 0);
		break;

            case B_TREF:
                edit_tref (X->button == Button3);
		break; 

            case B_GAIN:
                edit_gain (X->button == Button3, X->state & ShiftMask);
		break; 

            case B_TRM0:
                edit_trim0 (X->button == Button3);
		break; 

            case B_TRM1:
                edit_trim1 (X->button == Button3);
 	        break; 

            case B_FADE:
                edit_fade (X->button == Button3);
 	        break; 
	    }
	}
	break;
    }
/*
    case X_callback::MENU:
    {
        X_menuwin *M = (X_menuwin *) W;
        switch (e)
        {
	case X_menuwin::OPEN:
	    M->show ();
	    break;

	case X_menuwin::SEL:
	    // M->selection () 
            M->hide ();
	    break;

	case X_menuwin::CAN:
            M->hide ();
	    break;
	}
	break;
    }
*/
    default:
	switch (k)
	{
	case CB_FILE_CANC:
            _flock = false;
              break;

	case CB_FILE_SESS:
            _flock = false;
            _callb->handle_callb (CB_FILE_SESS, 0, 0);
            break;

	case CB_FILE_LOAD:
	    _flock = false;
	    file_load ();
	    break;

	case CB_FILE_SAVE: 
	    _flock = false;
	    if (_doall) save_all ();
	    else        save_one ();
	    break;

	case CB_FILE_IMPT:
	    _flock = false;
	    break;

	case CB_FILE_EXPT:
	    _flock = false;
	    file_expt ();
             break;
	}
	break;
    }
}


void Mainwin::handle_mesg (ITC_mesg *M)
{
    switch (M->type ())
    {
    case MT_CONV_ONE:
	conv_done_one ((M_calc_conv *) M);
	break;
	
    case MT_CONV_ALL:
	conv_done_all ((M_calc_conv *) M);
	break;

    case MT_EDIT_ONE:
	edit_done_one ((M_calc_edit *) M);
	break;
	
    case MT_EDIT_ALL:
	edit_done_all ((M_calc_edit *) M);
	break;

    case MT_EXPORT:
	M->recover ();
        _editwin->update ();                   
	break;
    }
}


void Mainwin::handle_prog (void)
{
    if (++_prog1 == _prog0)
    {
	_progwin->hide ();
        _prog1 = _prog0 = 0;
    }
    else _progwin->prog ((float) _prog1 / (float) _prog0);
}


void Mainwin::set_view (void)
{
    _modwin->x_unmap ();
    _modwin = _ifwview;
    _modwin->x_map ();
    _tfile->x_mapraised ();
    _editwin->x_map ();
    _filtwin->x_unmap ();
    _state = VIEW;
    set_disp (_data1, 0, 0);
    fileinfo ();
}


void Mainwin::set_conv (void)
{
    if (_data1 == 0) return;
    _modwin->x_unmap ();
    _modwin = _ifwconv;
    _modwin->x_map ();
    _tfile->x_mapraised ();
    _state = CONV;
    conv_init ();
}


void Mainwin::set_edit (void)
{
    if (_data1 == 0) return;
    _modwin->x_unmap ();
    _modwin = _ifwedit;
    _modwin->x_map ();
    _tfile->x_mapraised ();
    _state = EDIT;
    edit_init ();
}


void Mainwin::set_sint (void)
{
    if (_data1 == 0) return;
    _modwin->x_unmap ();
    _modwin = _ifwsint;
    _modwin->x_map ();
    _tfile->x_mapraised ();
    _state = SINT;
    sint_init ();
}


void Mainwin::set_filt (void)
{
    if (_data1 == 0) return;
    _modwin->x_unmap ();
    _modwin = _ifwfilt;
    _modwin->x_map ();
    _tfile->x_mapraised ();
    _editwin->x_unmap ();
    _filtwin->x_map ();
    _state = FILT;
}


void Mainwin::file_load (void)
{
    char name [1024];
    
    sprintf (name, "%s/%s", _filewin->wdir (), _filewin->file ());
    switch (_state)
    {
    case VIEW:
        strcpy (_file1, name); 
	Impdata::checkext (_file1);
        delete _data1;
        _data1 = new Impdata;
        if (_data1->open_read (_file1))
	{
            delete _data1;
            _data1 = 0; 
            return;
	}
        _data1->alloc ();
        _data1->read_sect (0);
        set_disp (_data1, 0, 0);
	fileinfo ();
	break;
  
    case CONV:
        _tcfil->set_text (name);
        modif (_tcfil);
	break;
    }
}


void Mainwin::file_save (void)
{
}


void Mainwin::file_impt (void)
{
}


void Mainwin::file_expt (void)
{
    int          c, s;
    U32          m, mask;
    const char   *name;
    M_export     *M;

    name = _exptwin->file ();
    if (*name)
    {
        if (_exptwin->chan () == EXP_CHAN_ALL) mask = ((1u << _data1->n_chan ()) - 1);
        else mask = _editwin->get_mask ();

	switch (_exptwin->type ())
	{
	case EXP_TYPE_RAW:
	case EXP_TYPE_WAV:
	    for (c = 0, m = mask; m; m >>= 1) c += m & 1;
	    break;
	default:
	    c = 1;
	}

	switch (_exptwin->sect ())
	{
	case EXP_SECT_COMB:
	case EXP_SECT_ITER:
            s = _data1->n_sect ();
	    break;
	default:
	    s = 1;
	}

        M = new M_export ();	
        M->_impd = _data1;
	M->_name = name;
	M->_type = _exptwin->type ();
	M->_form = _exptwin->form ();
	M->_sect = _exptwin->sect ();
	M->_mask = mask;
	M->_rval = 0;
        _itcctrl->send_event (TO_CALC, M);
        _progwin->show (_xs / 2, _ys / 2, "Exporting...");
        _prog0 += c * s;
    }
}


void Mainwin::set_disp (Impdata *data, int nchan1, const char **labels)
{
    int     i, f = 0;
    float   t0, t1;
    char    s [16];

    _nchan1 = 0;

    _editwin->clear_marks ();

    _disp = data;         
    if (data)
    {
	if (nchan1)
	{
            _nchan1 = nchan1;
            _cmask2 = ((1 << (data->n_chan () - _nchan1)) - 1) << _nchan1;
	}
        else
	{
            _nchan1 = data->n_chan ();
            _cmask2 = 0;
	}
        for (i = 0; i < 4; i++) _bchan [i]->x_unmap ();
        for (i = 0; i < _nchan1; i++)
	{
            if (_cmask2) _bchan [i]->set_text (labels [i], 0);
            else         _bchan [i]->set_text (Impdata::channame (data->type (), i), 0);
            _bchan [i]->set_stat (1);
        } 
        if (_cmask2)
        {
            _bchan [i]->set_text (labels [i], 0);
            _bchan [i]->set_stat (0);
	    i++;
            f |= Editwin::OVERLAY;
	}
        if (i > 1) while (i) _bchan [--i]->x_map ();
        if (data->n_sect () > 1)
	{
	    _bfrmi->x_map ();
            _bfrpl->x_map ();
            sprintf (s, "%d / %d", data->i_sect () + 1, data->n_sect ());
	    _tfrag->set_text (s);                 
	}
        else
	{
	    _bfrmi->x_unmap ();
            _bfrpl->x_unmap ();
            _tfrag->set_text ("1 / 1");
	}        
        if (_state == SINT) f |= Editwin::POWMODE;
        t0 = (0               - data->dtref ()) / data->drate () - 0.1;
        t1 = (data->n_fram () - data->dtref ()) / data->drate () + 0.1; 
        _editwin->set_data (data, (1 << _nchan1) - 1, f, t0, t1); 
    }
    else
    {
        for (i = 0; i < 4; i++) _bchan [i]->x_unmap ();
        _bfrmi->x_unmap ();
        _bfrpl->x_unmap ();
        _tfrag->set_text ("- / -");
        _editwin->set_data (0, 0, 0, 0, 0);
    }
}


void Mainwin::settrace (int k, int b)
{
    int  i, m;

    if (k < _nchan1) m = 1 << k;
    else             m = _cmask2;
    if (b == Button3) m ^= _editwin->get_mask ();
    if (m)
    {
	_editwin->set_mask (m);
        for (i = 0; i < _nchan1; i++)
	{
            _bchan [i]->set_stat (m & 1);
            m >>= 1;
	}
        if (_cmask2) _bchan [_nchan1]->set_stat (m ? 1 : 0);
    }
}


void Mainwin::modif (X_window *W)
{
    switch (_state)
    {
    case CONV:
        _bappl->set_stat (2);
        if (W == _tcfil1)
	{
            delete _convol;
            _convol = 0;
	}
	_data2->clear ();
	_editwin->update ();
	break;
    }
}


void Mainwin::cancel ()
{
    switch (_state)
    {
    case CONV:
        delete _data2;
        delete _convol;
        _data2  = 0;
        _convol = 0; 
	break;

    case EDIT:
        delete _data2;
	_data2 = 0;
	break;

    case FILT:
	break;

    case SINT:
        delete _data2;
        delete _awfilt;
        _data2  = 0;
        _awfilt = 0;
	break;
    }
    set_view ();
}


void Mainwin::apply ()
{
    switch (_state)
    {
    case CONV:
        _tctim1->callb_modified ();
        _tctim2->callb_modified ();
        _tcfil1->callb_modified ();
        conv_calc_one (0);
	break;
    }
}


void Mainwin::next_sect (int d)
{
    int   i, j, n;;
    char  s [16]; 

    i = _data1->i_sect ();
    j = i + d;
    n = _data1->n_sect () - 1;
    if      (j > n) j = n;
    else if (j < 0) j = 0;
    if (j != i)
    {
        sprintf (s, "%d / %d", j + 1, n + 1);    
        _tfrag->set_text (s);
        switch (_state)
	{
	case VIEW:
            _data1->read_sect (j);
            _editwin->update ();                   
            break;

	case CONV:
            _data1->read_sect (j);
	    conv_calc_one (0);
            break;

	case EDIT:
            _data1->read_sect (j);
            edit_calc_one (0);
            break;

	case SINT:
            _data1->read_sect (j);
            sint_exec ();
            _editwin->update ();                   
            break;
	}
    }
}


void Mainwin::save_one (void)
{
    char name [1024];

    sprintf (name, "%s/%s", _filewin->wdir (), _filewin->file ());
    switch (_state) 
    {
    case CONV:
	conv_calc_one (name);
	break;
    case EDIT:
	edit_calc_one (name);
	break;
    }
}


void Mainwin::save_all (void)
{
    char name [1024];

    sprintf (name, "%s/%s", _filewin->wdir (), _filewin->file ());
    switch (_state) 
    {
    case CONV:
	conv_calc_all (name);
	break;
    case EDIT:
	edit_calc_all (name);
	break;
    }
}


void Mainwin::conv_init ()
{
    _bappl->set_stat (2);
    _data2 = new Impdata ();
    _data2->set_type (_data1->type ());
    _data2->set_rate (_data1->rate_n (), _data1->rate_d ());
    _data2->set_n_fram (1000);
    _data2->set_n_sect (1);
    _data2->alloc ();
    _editwin->set_data (_data2, 0, 0, 0, 0);
}


int Mainwin::conv_prep (M_calc_conv *M)
{
    char      s1 [1024];
    char      s2 [1024];
    float     t0, t1;
    float     b [2 * 4096];
    int       c, i, n;
    Impdata   T;
    Convdata  *D; 

    if (! _convol)
    {
        *s1 = *s2 = 0;
        if (   (sscanf (_tcfil1->text (), "%s", s1) != 1)
            || T.open_read (s1)
            || ((T.type () != Impdata::TYPE_SWEEP) && (T.n_chan () != 1))
            || (T.rate_n () / T.rate_d () != _data1->rate_n () / _data1->rate_d ()))
        {
	    askdata (_tcfil1);
            return 1;
	}
        c = _data1->n_chan ();
        _convlen = T.n_fram ();
        D = new Convdata (4096, _convlen, 0);
        for (i = 0; i < (int)(D->npar ()); i++)
	{
            n = T.read_ext (4096, b);            
            if (n < 4096) memset (b + 2 * n, 0, 2 * (4096 - n) * sizeof (float));
	    D->prepare_part (i, 1.0f, b + (T.n_chan () - 1), T.n_chan ());  
	}        
        D->prepare_done ();
        T.close ();
        _convol = new Convolver (4096, _convlen, 0, c, c);                
        for (i = 0; i < c; i++) _convol->set_conv (i, i, D);
    }     

    if (   (sscanf (_tctim1->text (), "%f", &t0) != 1)
        || (t0 > 0.0f))
    {
        askdata (_tctim1);
        return 1;
    }
    if (   (sscanf (_tctim2->text (), "%f", &t1) != 1)
        || (t1 < t0 + 0.1f)
        || (t1 > 40.0f))
    {
	askdata (_tctim2);
        return 1;
    }
    M->_conv = _convol;
    M->_data1 = _data1;
    M->_data2 = _data2;
    M->_k0 = _convlen - 1 + (int)(floor (_data1->drate () * t0 + 0.5f)); 
    M->_k1 = _convlen - 1 + (int)(floor (_data1->drate () * t1 + 0.5f)); 
    if (M->_k0 < 0) M->_k0 = 0;
    if (_data2->n_fram () != (U32)(M->_k1 - M->_k0))
    {
        _data2->set_n_fram ((U32)(M->_k1 - M->_k0));
        _data2->alloc ();
	_editwin->set_data (_data2, 0, 0, 0, 0);
    }
    _data2->set_tref (_convlen - 1 - M->_k0);
    return 0;
}


int Mainwin::conv_save (const char *name, bool all)
{
    _data2->set_n_sect (all ? _data1->n_sect () : 1);
    strcpy (_file2, name);
    Impdata::checkext (_file2);
    if (! strcmp (_file1, _file2))
    {
	fprintf (stderr, "Can't save to current input '%s'.\n", _file1);
        return 1;
    }
    if (_data2->open_write (_file2))
    {
	fprintf (stderr, "Can't create output file '%s'.\n", _file2);
	return 1;
    }
    return 0;
}


void Mainwin::conv_calc_one (const char *name)
{
    M_calc_conv *M; 

    M = new M_calc_conv (MT_CONV_ONE);	
    if (conv_prep (M) || (name && conv_save (name, false)))
    {
	M->recover ();
        return;
    }
    M->_sect = name ? 0 : -1;
    _progwin->show (_xs / 2, _ys / 2, "Performing convolution...");
    _prog0 += (M->_k1 + _convol->part () - 1) / _convol->part (); 
    _itcctrl->send_event (TO_CALC, M);
}


void Mainwin::conv_done_one (M_calc_conv *M)
{
    M->recover ();
    _data2->close ();
    _bappl->set_stat (0);
    _editwin->update ();
}


void Mainwin::conv_calc_all (const char *name)
{
    M_calc_conv *M; 

    M = new M_calc_conv (MT_CONV_ALL);	
    if (conv_prep (M) || (name && conv_save (name, true)))
    {
	M->recover ();
        return;
    }
    _progwin->show (_xs / 2, _ys / 2, "Performing convolution on all sections...");
    _prog0 += _data1->n_sect () * ((M->_k1 + _convol->part () - 1) / _convol->part ()); 
    _itcctrl->send_event (TO_CALC, M);
}


void Mainwin::conv_done_all (M_calc_conv *M)
{
    M->recover ();
    _data2->close ();
    delete _data1;
    _data1 = _data2;
    _data2 = 0;
    strcpy (_file1, _file2); 
    _data1->open_read (_file1);
    _data1->alloc ();
    _data1->read_sect (0);
    delete _convol;
    _convol = 0;
    set_view ();
}



void Mainwin::edit_init ()
{
    _offs = 0;
    _gain = 1.0f;
    _slope = 0.0f;
    _ind0  = 0;
    _ind1  = _data1->n_fram () - 1;
    _ttref->set_text ("0.0");
    _tgain->set_text ("1.0");
    _ttrm00->set_text ("--");
    _ttrm01->set_text ("--");
    _ttrm10->set_text ("--");
    _ttrm11->set_text ("--");
    _ttfade->set_text ("--");
    _tslope->set_text ("0.0");
    _btref->set_stat (0);
    _bgain->set_stat (0);
    _btrim0->set_stat (0);
    _btrim1->set_stat (0);
    _data2 = new Impdata ();
    _data2->set_type (_data1->type ());
    _data2->set_rate (_data1->rate_n (), _data1->rate_d ());
}


int Mainwin::edit_prep (M_calc_edit *M)
{
    M->_data1 = _data1;
    M->_data2 = _data2;
    M->_offs = _offs;
    M->_gain = _gain;
    M->_ind0 = _ind0;
    M->_ind1 = _ind1;
    M->_slope = 0;
    if (_btrim0->stat ())
    {
	M->_trm00 = _trm00;
	M->_trm01 = _trm01;
    }
    else M->_trm00 = M->_trm01 = 0;
    if (_btrim1->stat ())
    {
	M->_trm10 = _trm10;
	M->_trm11 = _trm11;
    }
    else M->_trm10 = M->_trm11 = 0;
    if (_bfade->stat ())
    {
	M->_tfade = _tfade;
	M->_slope = _slope;
    }
    return 0;
}


int Mainwin::edit_save (const char *name, bool all)
{
    _data2->set_n_sect (all ? _data1->n_sect () : 1);
    _data2->set_n_fram (_ind1 - _ind0 + 1);
    _data2->alloc ();
    _data2->set_tref (_data1->tref_i () - _ind0 + _offs); 
    strcpy (_file2, name);
    Impdata::checkext (_file2);
    if (! strcmp (_file1, _file2))
    {
	fprintf (stderr, "Can't save to current input '%s'.\n", _file1);
        return 1;
    }
    if (_data2->open_write (_file2))
    {
	fprintf (stderr, "Can't create output file '%s'.\n", _file2);
	return 1;
    }
    return 0;
}


void Mainwin::edit_calc_one (const char *name)
{
    M_calc_edit *M; 

    M = new M_calc_edit (MT_EDIT_ONE);	
    if (edit_prep (M) || (name && edit_save (name, false)))
    {
	M->recover ();
        return;
    }
    if (name)
    {
        M->_sect = 0;
        _progwin->show (_xs / 2, _ys / 2, "Performing edits...");
        _prog0 += 1;
    }
    else M->_sect = -1;
    _data1->read_sect (_data1->i_sect ());
    _itcctrl->send_event (TO_CALC, M);
}


void Mainwin::edit_done_one (M_calc_edit *M)
{
    M->recover ();
    _data2->close ();
    _editwin->update ();
}


void Mainwin::edit_calc_all (const char *name)
{
    M_calc_edit *M; 

    M = new M_calc_edit (MT_EDIT_ALL);	
    if (edit_prep (M) || (name && edit_save (name, true)))
    {
	M->recover ();
        return;
    }
    _progwin->show (_xs / 2, _ys / 2, "Performing edits on all sections...");
    _prog0 += _data1->n_sect ();
    _itcctrl->send_event (TO_CALC, M);
}


void Mainwin::edit_done_all (M_calc_edit *M)
{
    M->recover ();
    _data2->close ();
    delete _data1;
    _data1 = _data2;
    _data2 = 0;
    strcpy (_file1, _file2); 
    _data1->open_read (_file1);
    _data1->alloc ();
    _data1->read_sect (0);
    set_view ();
}


void Mainwin::edit_tref (bool undo)
{
    int i;

    if (undo)
    {
	if (_btref->stat ())
	{
	    _btref->set_stat (0);
	    _ttref->set_text ("0.0");
	    _editwin->set_offs (_offs = 0);
	}
        else return;
    }
    else
    {
	i = _editwin->get_currmark ();
        if (i < 0) return;
        _btref->set_stat (2);
        _offs = _editwin->get_markpos (i) - _data1->tref_i ();
        ptime (_data1->tref_i (), _ttref);
	_editwin->set_offs (_offs);
    }
    if (_btrim0->stat ())
    {
	ptime (_trm00, _ttrm00);
	ptime (_trm01, _ttrm01);
    }
    if (_btrim1->stat ())
    {
	ptime (_trm10, _ttrm10);
	ptime (_trm11, _ttrm11);
    }
}


void Mainwin::edit_gain (bool undo, bool norm)
{
    int    c, i, j, i0, i1, k, m, n, r, s;
    float  u, v, *p;
    char   t [16];

    if (undo)
    {
	if (_bgain->stat ())
	{
	    _bgain->set_stat (0);
	    _tgain->set_text ("1.0");
	    _editwin->set_gain (_gain = 1.0f);
	}
    }
    else if (norm)
    {
	i = _editwin->get_currmark ();
        if (i < 0) return;
        k = _editwin->get_markpos (i);
        r = _data1->rate_n () / (_data1->rate_d () * 100); 
        s = _data1->n_fram () - 1; 
        c = _data1->n_chan ();
        i0 = k - r;
        i1 = k + r;
        if (i0 < 0) i0 = 0;
        if (i1 > s) i1 = s;
        m = _editwin->get_mask ();
        n = 0;
        u = 0.0f;
        for (j = 0; m; j++, m >>= 1)
	{
	    if (m & 1)
	    {
                n++;
                p = _data1->data (j);
                for (i = i0; i <= i1; i++)
	        {
		    v = p [c * i];
		    u += v * v * 0.5f * (1.0f + cosf (M_PI * (i - k) / r));
		}
	    }
	}  
        _gain = sqrtf (n / u);
        sprintf (t, "%5.3lf", _gain);  
        _tgain->set_text (t);
        _bgain->set_stat (2);
        _editwin->set_gain (_gain);
    }
    else
    {
	if (sscanf (_tgain->text (), "%f", &v) != 1) return;
	_gain = v;
        sprintf (t, "%5.3lf", _gain);  
        _tgain->set_text (t);
        _bgain->set_stat (2);
        _editwin->set_gain (_gain);
    }
}


void Mainwin::edit_trim0 (bool undo)
{
    int m0, m1;

    if (undo)
    {
	if (_btrim0->stat ())
	{
            _ind0 = 0;
	    _btrim0->set_stat (0);
	    _ttrm00->set_text ("--");
	    _ttrm01->set_text ("--");
	}
        else return;
    }
    else
    {
	m0 = _editwin->get_markpos (0); 
	m1 = _editwin->get_markpos (1); 
        if ((m0 == INT_MIN) || (m1 == INT_MIN)) return;
        _btrim0->set_stat (2);
        if (m0 < m1) { _trm00 = m0; _trm01 = m1; }
        else         { _trm00 = m1; _trm01 = m0; }
        _ind0 = (_trm00 < 0) ? 0 : _trm00;
	ptime (_trm00, _ttrm00);
	ptime (_trm01, _ttrm01);
    }
    edit_calc_one (0);
}


void Mainwin::edit_trim1 (bool undo)
{
    int m0, m1, n;

    if (undo)
    {
	if (_btrim1->stat ())
	{
            _ind1 = _data1->n_fram () - 1;
	    _btrim1->set_stat (0);
	    _ttrm10->set_text ("--");
	    _ttrm11->set_text ("--");
	}
        else return;
    }
    else
    {
	m0 = _editwin->get_markpos (0); 
	m1 = _editwin->get_markpos (1); 
        if ((m0 == INT_MIN) || (m1 == INT_MIN)) return;
        _btrim1->set_stat (2);
        if (m0 < m1) { _trm10 = m0; _trm11 = m1; }
        else         { _trm10 = m1; _trm11 = m0; }
        n = _data1->n_fram () - 1;   
        _ind1 = (_trm11 > n) ? n : _trm11;
	ptime (_trm10, _ttrm10);
	ptime (_trm11, _ttrm11);
    }
    edit_calc_one (0);
}


void Mainwin::edit_fade (bool undo)
{
    int   i, k;
    float v;

    if (undo)
    {
	if (_bfade->stat ())
	{
	    _bfade->set_stat (0);
	    _ttfade->set_text ("--");
	}
        else return;
    }
    else
    {
	i = _editwin->get_currmark ();
        if (i < 0) return;
        k = _editwin->get_markpos (i);
	if (k < _ind0) return;
	if (k > _ind1) return;
	if (sscanf (_tslope->text (), "%f", &v) != 1) return;
	_slope = v;
	_tfade = k;
        _bfade->set_stat (2);
	ptime (_tfade, _ttfade);
    }
    edit_calc_one (0);
}


void Mainwin::sint_init ()
{
    _subs = (U32)(_data1->drate() / 1000);
    _data2 = new Impdata;
    _data2->set_type (_data1->type ());
    _data2->set_rate (_data1->rate_n (),_data1->rate_d ());
    _data2->set_n_sect (_data1->n_sect ());
    _data2->set_n_fram (_ind1 - _ind0 + 1);
    _data2->set_tref ((U32)(_data1->dtref () / _subs));
    _data2->alloc ();
    _awfilt = new ACweight ();
    _awfilt->init ((int)(_data1->drate ()));
    set_disp (_data2, 0, 0);
    sint_exec ();
    _editwin->update ();
}


void Mainwin::sint_exec ()
{
    int    c, h, i, j, m;
    float  *p, *q, v1, v2, v3, s1, s2;

    m = _editwin->get_mask ();
    c = _data1->n_chan ();
    memset (_data2->data (0), 0, 2 * _data2->n_fram () * sizeof (float));
    for (j = 0; m; j++, m >>= 1)
    {
        if (m & 1)
	{
            p = _data1->data (j);  
            q = _data2->data (0);
            for (i = 0; i < (int)(_data2->n_fram ()); i++)
            {
                s1 = s2 = 0.0f;
                for (h = 0; h < _subs; h++)
		{
		    v1 = *p;
                    p += c;
                    _awfilt->process (1, &v1, &v2, &v3);
                    s1 += v1 * v1;
                    s2 += v2 * v2;
		}
                *q++ += s1;
                *q++ += s2; 
	    }
	}
    }

    q = _data2->data (0) + 2 * _data2->n_fram ();
    s1 = s2 = 0.0f;
    for (i = 0; i < (int)(_data2->n_fram ()); i++)
    {
	s2 += *--q;
        *q = s2;
	s1 += *--q;
        *q = s1;
    }
    q = _data2->data (0);
    s1 = 1.0f / (s1 + 1e-30);
    s2 = 1.0f / (s2 + 1e-30);
    for (i = 0; i < (int)(_data2->n_fram ()); i++)
    {
        *q++ *= s1;
        *q++ *= s2;
    }
}


void Mainwin::ptime (int v, X_textip *T)
{
    char  s [16];

    sprintf (s, "%8.6lf", (v - _offs - _data1->dtref ()) / _data1->drate ());
    T->set_text (s);
}


void Mainwin::makewin (void)
{
    int       i, y;
    X_window    *W;

    _ifwview = W = new X_window (this, 0, 0, 1200, TMAR, XftColors.main_bg->pixel);
    Bst0.size.x = 70;
    Bst0.size.y = 18;
    _bsess = new X_tbutton (W, this, &Bst0,   0,  0, "Session", 0, B_SESS);
    _bsess->x_map ();
    _bload = new X_tbutton (W, this, &Bst0,  70,  0, "Load",    0, B_LOAD);
    _bload->x_map ();
    _bsave = new X_tbutton (W, this, &Bst0, 140,  0, "Save",    0, B_SAVE);
    _bsave->x_map ();
    _bimpt = new X_tbutton (W, this, &Bst0, 210,  0, "Import",  0, B_IMPT);
    _bimpt->x_map ();
    _bexpt = new X_tbutton (W, this, &Bst0, 280,  0, "Export",  0, B_EXPT);
    _bexpt->x_map ();
    _bsave->set_stat (3);
    _bimpt->set_stat (3);
    
    _tfile = new X_textip (this, 0, &Tst0, 350, 0, XMIN - 350, 18, 127);
    _tfile->x_mapraised ();

    _bsgen = new X_tbutton (W, this, &Bst0,   0, 18, "Sweep",   0, B_SGEN);
    _bsgen->x_map ();
    _bcapt = new X_tbutton (W, this, &Bst0,  70, 18, "Capture", 0, B_CAPT);
    _bcapt->x_map ();
    _bconv = new X_tbutton (W, this, &Bst0, 140, 18, "Convol",  0, B_CONV);
    _bconv->x_map ();
    _bedit = new X_tbutton (W, this, &Bst0, 210, 18, "Edit",    0, B_EDIT);
    _bedit->x_map ();
    _bsint = new X_tbutton (W, this, &Bst0, 280, 18, "Integr",  0, B_SINT);
    _bsint->x_map ();
    _bfilt = new X_tbutton (W, this, &Bst0,   0, 36, "Filter",  0, B_FILT);
    _bfilt->x_map ();
    _bsint->set_stat (3);
    _bfilt->set_stat (3);

    _ifwconv = W = new X_window (this, 0, 0, 1200, TMAR, XftColors.main_bg->pixel);
    Bst0.size.x = 100;
    Bst0.size.y = 18;
    (new X_tbutton (W, this, &Bst0,   0, 0, "Cancel",       0, B_CANC))->x_map ();
    (new X_tbutton (W, this, &Bst0, 100, 0, "Save section", 0, B_SONE))->x_map ();
    (new X_tbutton (W, this, &Bst0, 200, 0, "Apply to all", 0, B_SALL))->x_map ();
    addtext (W, 0, 21, 70, 18, "Start time", 1);
    addtext (W, 0, 40, 70, 18, "End time",   1);
    _tctim1 = new X_textip (W, this, &Tst1, 75, 20, 75, 18, 1023);
    _tctim1->set_text ("-1");
    _tctim1->x_map (); 
    _tctim2 = new X_textip (W, this, &Tst1, 75, 39, 75, 18, 1023, _tctim1); 
    _tctim2->set_text ("1");
    _tctim2->x_map (); 
    addtext (W, 160, 20, 60, 18, "Sweep",  1);
    Bst0.size.x = 17;
    Bst0.size.y = 17;
    (new X_ibutton (W, this, &Bst0, 225, 20, disp ()->image1515 (X_display::IMG_DN), B_FILE))->x_map ();
    _tcfil1 = new X_textip (W, this, &Tst1, 250, 20, 250, 18, 1023, _tctim2);
    _tcfil1->set_align (1);
    _tcfil1->x_map (); 
    Bst0.size.x = 70;
    Bst0.size.y = 18;
    _bappl = new X_tbutton (W, this, &Bst0, 430, 39, "Apply",  0, B_APPL);
    _bappl->x_map ();

    _ifwedit = W = new X_window (this, 0, 0, 1200, TMAR, XftColors.main_bg->pixel); 
    Bst0.size.x = 100;
    Bst0.size.y = 18;
    (new X_tbutton (W, this, &Bst0,   0, 0, "Cancel",       0, B_CANC))->x_map ();
    (new X_tbutton (W, this, &Bst0, 100, 0, "Save section", 0, B_SONE))->x_map ();
    (new X_tbutton (W, this, &Bst0, 200, 0, "Apply to all", 0, B_SALL))->x_map ();
    Bst0.size.x = 80;
    Bst0.size.y = 18;
    _ttref = new X_textip (W, 0,      &Tst1,   5, 20, 90, 18, 1023, 0);
    _ttref->x_map ();
    _btref = new X_tbutton (W, this,  &Bst0,  97, 20, "Time ref",   0, B_TREF);
    _btref->x_map ();
    _tgain = new X_textip (W, this,   &Tst1,   5, 39, 90, 18, 1023, 0);
    _tgain->x_map ();
    _bgain = new X_tbutton (W, this,  &Bst0,  97, 39, "Gain / Norm",  0, B_GAIN);
    _bgain->x_map ();
    _ttrm00 = new X_textip (W, 0,     &Tst1, 200, 20, 80, 18, 1023, 0);
    _ttrm00->x_map ();
    _ttrm01 = new X_textip (W, 0,     &Tst1, 281, 20, 80, 18, 1023, 0);
    _ttrm01->x_map ();
    _btrim0 = new X_tbutton (W, this, &Bst0, 363, 20, "Trim start", 0, B_TRM0);
    _btrim0->x_map ();
    _ttrm10 = new X_textip (W, 0,     &Tst1, 200, 39, 80, 18, 1023, 0);
    _ttrm10->x_map ();
    _ttrm11 = new X_textip (W, 0,     &Tst1, 281, 39, 80, 18, 1023, 0);
    _ttrm11->x_map ();
    _btrim1 = new X_tbutton (W, this, &Bst0, 363, 39, "Trim end",   0, B_TRM1);
    _btrim1->x_map ();
    _ttfade = new X_textip (W, 0,     &Tst1, 460, 20, 80, 18, 1023, 0);
    _ttfade->x_map ();
    _tslope = new X_textip (W, this,  &Tst1, 541, 20, 80, 18, 1023, 0);
    _tslope->x_map ();
    _bfade = new X_tbutton (W, this, &Bst0,  623, 20, "Fade out", 0, B_FADE);
    _bfade->x_map ();

    _ifwsint = W = new X_window (this, 0, 0, 1200, TMAR, XftColors.main_bg->pixel);
    (new X_tbutton (W, this, &Bst0,  0, 0, "Back", 0, B_CANC))->x_map ();

    _ifwfilt = W = new X_window (this, 0, 0, 1200, TMAR, XftColors.main_bg->pixel);
    (new X_tbutton (W, this, &Bst0,  0, 0, "Back", 0, B_CANC))->x_map ();

    addtext (_editwin->bbwin (), 10, 3, 55, 18, "Section:", -1); 
    _tfrag = new X_textip (_editwin->bbwin (),     0, &Tst0, 70, 3, 70, 17, 15);
    _tfrag->x_map ();
    _tfrag->set_text ("- / -");
    Bst0.size.x = 24;
    Bst0.size.y = 17;
    _bfrmi = new X_ibutton (_editwin->bbwin (), this, &Bst0, 145, 3, disp ()->image1515 (X_display::IMG_LT), B_FRMI);
    _bfrpl = new X_ibutton (_editwin->bbwin (), this, &Bst0, 170, 3, disp ()->image1515 (X_display::IMG_RT), B_FRPL);

    Bst0.size.x = 35;
    Bst0.size.y = 18;
    y = 40;
    for (i = 0; i < 4; i++)
    {
        _bchan [i] = new X_tbutton (_editwin->rbwin (), this, &Bst0, 1, y, "-", 0, B_CHAN + i);
        _bchan [i]->x_set_win_gravity (NorthGravity);
        y += 18;
    }
}


void Mainwin::addtext (X_window *W, int xp, int yp, int xs, int ys, const char *text, int align)
{
    (new X_textln (W, &Tst0, xp, yp, xs, ys, text, align))->x_map ();
}


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


void Mainwin::fileinfo (void)
{
    char s [256];
    char *p;

    if (!_data1) return;
    p = strrchr (_file1, '/');
    if (p) p++;
    else p = _file1;
    snprintf (s, 256, "%s   %3.1lf   %d * %d", p, _data1->drate (), _data1->n_sect (), _data1->n_fram ());
    _tfile->set_text (s);
    _base1 = p;
}

