// ----------------------------------------------------------------------------
//
//  Copyright (C) 2006-2011 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 2 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, write to the Free Software
//  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// ----------------------------------------------------------------------------


#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "styles.h"
#include "global.h"
#include "mainwin.h"


Mainwin::Mainwin (X_rootwin *parent, X_resman *xres, int xp, int yp, Jclient *jclient) :
    A_thread ("Main"),
    X_window (parent, xp, yp, 100, 100, XftColors [C_MAIN_BG]->pixel),
    _stop (false),
    _xres (xres),
    _jclient (jclient)
{
    X_hints     H;
    char        s [1024];
    const char *p;
    float       v;

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

    sprintf (s, "%s - %s  [%s]", PROGNAME, VERSION, jclient->jname ());
    x_set_title (s);
    H.position (xp, yp);
    H.rname (xres->rname ());
    H.rclas (xres->rclas ());
    x_apply (&H); 

    x_add_events (ExposureMask); 
    x_map (); 

    _confwin = new Confwin (parent, this, xres, 200, 200);
    _conf.default_label ();
    _conf._state = AD_conf::ST_ALL;

    makedisp ();

    p = xres->get (".outputs", "0");
    if (p || *p)
    {
	_nout = atoi (p);
	if (_nout < 0) _nout = 0;
	if (_nout > MAXOP) _nout = MAXOP;
    }
    if (_nout)
    {
	_fixop = true;
	_nact = _nout;
	jclient->create_opports (_nout);
    }    
    else
    {
	_fixop = false;
	_nact = _nout = _conf._dec.nspkr;
    }
    
    p = xres->get (".volume", "-80");
    if (p || *p)
    {
        v = atof (p);
        if (v >   10) v =   10;
        if (v < -100) v = -100;
        _fvol->set_val (powf (10.0f, 0.05f * v));
    }

    jclient->set_gain (_fvol->get_val ());    
    set_time (0);
    inc_time (100000);
}

 
Mainwin::~Mainwin (void)
{
}

 
int Mainwin::process (void)
{
    int e;

    if (_stop) handle_stop ();

    e = get_event_timed ();
    switch (e)
    {
    case EV_TIME:
        handle_time ();
	break;
    }
    return e;
}


void Mainwin::handle_event (XEvent *E)
{
    switch (E->type)
    {
    case Expose:
	expose ((XExposeEvent *) E);
	break;  
 
    case ClientMessage:
        clmesg ((XClientMessageEvent *) E);
        break;
    }
}


void Mainwin::expose (XExposeEvent *E)
{
    if (E->count) return;
    if (E->window == _disp->win ()) drawdisp ();
}


void Mainwin::clmesg (XClientMessageEvent *E)
{
    if (E->message_type == _atom) _stop = true;
}


void Mainwin::handle_time (void)
{
    int          i;
    const float  *level;

    level = _jclient->get_level ();
    for (i = 0; i < _nact; i++) _dlev [i]->update (level [i]);
    inc_time (50000);
    XFlush (dpy ());
}


void Mainwin::handle_stop (void)
{
    put_event (EV_EXIT, 1);
}


void Mainwin::handle_callb (int type, X_window *W, XEvent *E)
{
    int      k;
    X_button *B;

    switch (type)
    {
    case X_callback::BUTTON | X_button::PRESS:
	B = (X_button *) W;
	k = B->cbid ();
	if (k < MAXOP) chanbutt (k, (XButtonEvent *) E);
        else switch (k)
	     {
	     case B_CONF:
		 editconf ();
		 break;
	     case B_INTT:
	     case B_EXTT:
		 testbutt (k);
		 break;
	     case B_UP:
		 x_resize (_xm, _ym - 41);
		 _bup->x_unmap ();
		 _bdn->x_map ();
		 break;
	     case B_DN:
		 x_resize (_xm, _ym);
		 _bdn->x_unmap ();
		 _bup->x_map ();
		 break;
	     }
	break;

    case X_callback::SLIDER | X_slider::MOVE:
    case X_callback::SLIDER | X_slider::STOP:
	_jclient->set_gain (_fvol->get_val ());
	break;

    case CB_CONF_CANC:
        editconf ();
	break;

    case CB_CONF_APPL:
        _conf = *(_confwin->conf ());
	applconf ();
	break;
    }
}


void Mainwin::loadconf (void)
{
    const char *p;

    p = _xres->get (".config", 0);
    if (p && *p)
    {
	if (_conf.load (p))
	{
            fprintf (stderr, "Can't load '%s'\n", p);
	    _conf.reset ();
	}
	else
	{
	    _conf._state = AD_conf::ST_ALL;
	    applconf ();
	}
    }
    confdisp ();
}


void Mainwin::editconf (void)
{
    AD_conf *C = new AD_conf;
    *C = _conf;
    _confwin->open (C);
}


void Mainwin::applconf (void)
{
    syncdec (EV_GO_SILENCE);
    if (_conf._state & AD_conf::ST_OUTS)
    {
	_nact = _conf._dec.nspkr;
	if (_fixop)
	{
	    if (_nact > _nout) _nact = _nout;
	}
	else
	{
	    _nout = _nact;
	}
        confdisp ();
	if (!_fixop)
	{
            _jclient->disconn_opports ();
            syncdec (EV_GO_BYPASS);
            _jclient->create_opports (&_conf);
            syncdec (EV_GO_SILENCE);
            _jclient->connect_opports (&_conf);
	}
    }
    if (_conf._state & AD_conf::ST_INPS) _jclient->set_ipports (&_conf);
    if (_conf._state & AD_conf::ST_OPTS) _jclient->set_config (&_conf);
    if (_conf._state & AD_conf::ST_MATR) _jclient->set_matrix (&_conf);
    syncdec (EV_GO_PROCESS);
    _conf._state = 0;
}


void Mainwin::syncdec (int e)
{
    send_event (e, 1);
    e = get_event (1 << e);
}


void Mainwin::makedisp (void)
{
    char s [64];
    int  i, x, x0, y0;

    x0 = 120;
    y0 = 3;
    _xd = 540;
    _yd = 185;
    _disp = new X_subwin (this, x0, y0, _xd, _yd, XftColors [C_DISP_BG]->pixel);
    _disp->x_add_events (ExposureMask);
    _disp->x_map ();

    x = 8;
    _sclt = new X_vscale (_disp, &Sdb1, x, 8, 15, 0);
    _sclt->x_map ();
    x += 26;
    Bst0.size.x = 36;
    Bst0.size.y = 17;
    for (i = 0; i < MAXOP; i++)
    {
        _dlev [i] = new Meter (_disp, x, 10);
        _btst [i] = new X_tbutton (this, this, &Bst0, x0 + x - 14, y0 + ((i & 1) ? 208 : 189), 0, 0, i);
	sprintf (s, "%d", i + 1);
	_btst [i]->set_text (s, 0);
        x += 20;
    }
    _scrt = new X_vscale (_disp, &Sdb1, x, 8, 15, 0);
    _scrt->x_map ();

    (new X_vscale (this, &Sdb2, 40, 5, 15, 0))->x_map ();
    (new X_textln (this, &Tst1, 40, 171, 50, 18, "Volume", 0))->x_map ();
    _fvol = new  X_vslider (this, this, &Fst1, &Sdb2, 60, 5, 16, 0);
    _fvol->x_map ();

    Bst0.size.x = 70;
    Bst0.size.y = 17;
    _bconf = new X_tbutton (this, this, &Bst0, 2, y0 + 189, "Config", 0, B_CONF);
    _bconf->x_map ();
    Bst0.size.x = 36;
    Bst0.size.y = 17;
    _bintt = new X_tbutton (this, this, &Bst0, 90, y0 + 189, "Int", 0, B_INTT);
    _bintt->x_map ();
    _bextt = new X_tbutton (this, this, &Bst0, 90, y0 + 208, "Ext", 0, B_EXTT);
    _bextt->x_map ();
    Bst0.size.x = 17;
    Bst0.size.y = 17;
    _bup = new X_ibutton (this, this, &Bst0, 2, y0 + 168, disp ()->image1515 (X_display::IMG_UP), B_UP);
    _bdn = new X_ibutton (this, this, &Bst0, 2, y0 + 168, disp ()->image1515 (X_display::IMG_DN), B_DN);
}


void Mainwin::confdisp (void)
{
    int      i;
    X_hints  H;

    _xd = 60 + _nout * 20;
    _scrt->x_move (_xd - 27, 8);
    _disp->x_resize (_xd, _yd);

    _xm = _xd + 124;
    _ym = _yd + 46;
    x_resize (_xm, _ym);
    H.size (_xm, _ym);
    H.minsize (120, 60);
    H.maxsize (_xm, _ym);
    x_apply (&H);

    for (i = 0; i < _nout; i++)
    {
	if (!_fixop)
	{
            _btst [i]->set_text (_conf._speakers [i]._label, 0);
            _btst [i]->redraw ();
	}
        _btst [i]->set_stat (0);
        _dlev [i]->update (0.0f);
        _btst [i]->x_map ();
        _dlev [i]->x_map ();
    }
    for (i = _nout; i < MAXOP; i++)
    {
	_btst [i]->x_unmap ();
	_dlev [i]->x_unmap ();
    } 
    _bintt->set_stat (0);
    _bextt->set_stat (0);
    _bup->x_map ();

    _jclient->clr_mute (~0LL);
    _jclient->clr_solo (~0LL);
    _jclient->set_test (0);
}


void Mainwin::drawdisp (void)
{
    X_draw D (dpy (), _disp->win (), dgc (), 0);

    D.setcolor (XftColors [C_MAIN_DS]->pixel);
    D.move (0, _yd - 1);
    D.draw (0, 0);
    D.draw (_xd - 1, 0);
    D.setcolor (XftColors [C_MAIN_LS]->pixel);
    D.move (_xd - 1, 0);
    D.draw (_xd - 1, _yd - 1);
    D.draw (0, _yd - 1);
}


void Mainwin::testbutt (int b)
{
    switch (b)
    {
    case B_INTT:
        _bextt->set_stat (0);
        _bintt->set_stat (_bintt->stat () ^ 1);
        break;
    case B_EXTT:
        _bintt->set_stat (0);
        _bextt->set_stat (_bextt->stat () ^ 1);
        break;
    }
    if      (_bintt->stat ()) _jclient->set_test (1);
    else if (_bextt->stat ()) _jclient->set_test (2);
    else                      _jclient->set_test (0);
}


void Mainwin::chanbutt (int b, XButtonEvent *E)
{
    int       i;
    X_button  *B;

    B = _btst [b];
    if (E->state & ShiftMask)
    {
	switch (B->stat ())
	{
	case 0:
	    B->set_stat (2);
	    _jclient->set_mute (1LL << b);
	    break;
	case 2:
	    B->set_stat (0);
	    _jclient->clr_mute (1LL << b);
	    break;
	}
        return;
    }
    switch (B->stat ())
    {
    case 0:
        B->set_stat (1);
        if (E->button == Button3)
        {
            _jclient->set_solo (1LL << b);
            break;
        }
        for (i = 0; i < MAXOP; i++) 
        {
            if (i == b) continue;
  	    B = _btst [i];
	    if (B->stat () == 1) B->set_stat (0);
        }
        _jclient->clr_solo (~0LL);
        _jclient->set_solo (1LL << b);
        break;
    case 1:    
        B->set_stat (0);
        _jclient->clr_solo (1LL << b);
        break;
    }
}

