// ----------------------------------------------------------------------------
//
//  Copyright (C) 2010-2015 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 <string.h>
#include <math.h>
#include "styles.h"
#include "global.h"
#include "png2img.h"
#include "mainwin.h"


Mainwin::Mainwin (X_rootwin *parent, X_resman *xres, Jclient *jclient) :
    A_thread ("Main"),
    X_window (parent, 100, 100, XSIZE, YSIZE, XftColors [C_MAIN_BG]->pixel),
    _xres (xres),
    _stop (false),
    _jclient (jclient)
{
    int       xp, yp, xs, ys;
    char      s [1024];
    X_hints   H;
  
    _atom = XInternAtom (dpy (), "WM_DELETE_WINDOW", True);
    XSetWMProtocols (dpy (), win (), &_atom, 1);
    _atom = XInternAtom (dpy (), "WM_PROTOCOLS", True);

    sprintf (s, "%s-%s  [ %s ]", "zita-mu1", VERSION, jclient->jname ());
    x_set_title (s);

    xp = 100;
    yp = 100;
    xs = XSIZE;
    ys = YSIZE;
    xres->geometry (".geometry", disp ()->xsize (), disp ()->ysize (), 1, xp, yp, xs, ys);
    x_moveresize (xp, yp, XSIZE, YSIZE);
    H.position (0, 0);
    H.minsize (XSIZE, YSIZE);
    H.maxsize (XSIZE, YSIZE);
    H.rname (xres->rname ());
    H.rclas (xres->rclas ());
    x_apply (&H); 

    makegui ();
    x_add_events (ExposureMask);
    x_map (); 
    XFlush (dpy ());
    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 == win ()) redraw_main ();
    if (E->window == _dispwin->win ()) redraw_disp ();
}


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


void Mainwin::handle_time (void)
{
    int i;

    for (i = 0; i < 2; i++) update1 (i);
    update2 ();
    _cmeter->update (_jclient->read_cmeter ());
    XFlush (dpy ());
    inc_time (50000);
}


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


void Mainwin::update1 (int k)
{
    float r, p;

    _jclient->read_kmeter (k, &r, &p);
    _meters [k]->update (r, p);
    if (p > _peak1) _peak1 = p;
}


void Mainwin::update2 (void)
{
    float p;
    char  s [16];

    if (_peak1 > _peak2)
    {	   
	_peak2 = _peak1;
	p = 20 * log10f (_peak2);
	sprintf (s, (p < -9.9f) ? "%3.0lf" : "%3.1lf", p);
	_pkbutt->set_text (s, 0);
	if      (p >=   0) _pkbutt->set_stat (3);
	else if (p >=  -6) _pkbutt->set_stat (2);
	else if (p >= -20) _pkbutt->set_stat (1);
	_pkbutt->redraw ();
    } 
    _peak1 = 1e-4f;
}


void Mainwin::handle_callb (int type, X_window *W, XEvent *E)
{
    int  cl, ev, k, b;

    cl = cb_class (type);
    ev = cb_event (type);

    switch (cl)
    {
    case 0:
    {
	switch (ev)
	{
        case PushButton::PRESS:
        case PushButton::RELSE:
        {
  	    PushButton *B = (PushButton *) W;
	    k = B->cbind ();
	    b = PushButton::button ();
	    if      (k < B_LEFT) handle_inpsel (ev, k, b);
	    else if (k < B_OUT1) handle_monmod (ev, k, b);
	    else if (k < B_TKB1) handle_outsel (ev, k, b);
	    else                 handle_talkbk (ev, k, b);
	    break;
	}
        case RotaryCtl::DELTA:
        {
	    RotaryCtl *R = (RotaryCtl *) W;
	    switch (R->cbind ())
	    {
	    case R_VOL1:
		_jclient->set_out1gain (R->value ());
		break;
	    case R_VOL2:
		_jclient->set_out2gain (R->value ());
		break;
	    case R_TB1:
		_jclient->set_tb1gain (R->value ());
		break;
	    case R_TB2:
		_jclient->set_tb2gain (R->value ());
		break;
	    }
	    break;
	}
	}
	break;
    }

    case X_callback::BUTTON:
    {
        X_button *Z = (X_button *) W;
	switch (ev)
	{
	case X_button::PRESS:
	    k = Z->cbid ();
	    switch (k)
	    {
	    case B_PRES:
	        _pkbutt->set_text (0, 0);
	        _pkbutt->set_stat (0);
	        _pkbutt->redraw ();
	        _peak1 = 1e-4f;
	        _peak2 = 1e-4f;
		break;
	    }
	}
	break;
    }
    }
}


void Mainwin::handle_inpsel (int e, int k, int b)
{
    int i;

    i = 1 << (k - B_INP1);
    if ((b == Button3) && (_inpsel != i)) _inpsel ^= i;
    else _inpsel  = i;
    _jclient->set_inpsel (_inpsel);
    disp_inpsel ();
}


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

    i = k - B_LEFT + 1;
    m = _monmod & 3;
    if (k == B_DIMM) _monmod ^= 16;
    else 
    {
	_monmod &= 16;
        if (m != i) _monmod |= i;
    }
    _jclient->set_monmod (_monmod);
    disp_monmod ();
}    
	

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

    if (k == B_HDPH) _outsel ^= 16;
    else
    {
	m = _outsel & 15;
        i = 1 << (k - B_OUT1);
	if (m & i) m ^= i;
	else if (b == Button3) m |= i;
	else m = i;
	_outsel = (_outsel & 16) | m;
    }
    _jclient->set_outsel (_outsel);
    disp_outsel ();
}    
	

void Mainwin::handle_talkbk (int e, int k, int b)
{
    int i;

    i = 1 << (k - B_TKB1);
    if (e == PushButton::PRESS) _talkbk ^= i;
    else if (b == Button1) _talkbk &= ~i;
    _jclient->set_talkbk (_talkbk);
    disp_talkbk ();
}


void Mainwin::disp_inpsel (void)
{
    int         i, m;
    PushButton  *B;

    m = _inpsel;
    for (i = 0; i < 4; i++)
    {
	B = _buttons [B_INP1 + i];
	if (m & 1) B->set_state (B->state () | 2);
	else       B->set_state (B->state () & ~2); 
	m >>= 1;
    }
}


void Mainwin::disp_monmod (void)
{
    int         i, m;
    PushButton  *B;

    m = _monmod & 3;
    for (i = 0; i < 3; i++)
    {
	B = _buttons [B_LEFT + i];
	if (i + 1 == m) B->set_state (B->state () | 2);
	else            B->set_state (B->state () & ~2);
    }
    B = _buttons [B_DIMM];
    if (_monmod & 16) B->set_state (B->state () | 2);
    else              B->set_state (B->state () & ~2);
}


void Mainwin::disp_outsel (void)
{
    int         i, m;
    PushButton  *B;

    for (i = 0, m = _outsel; i < 2; i++, m >>= 1)
    {
	B = _buttons [B_OUT1 + i];
	if (m & 1) B->set_state (B->state () | 2);
	else       B->set_state (B->state () & ~2);
    }
    B = _buttons [B_HDPH];
    if (_outsel & 16) B->set_state (B->state () | 2);
    else              B->set_state (B->state () & ~2);
}


void Mainwin::disp_talkbk (void)
{
    int         i, m;
    PushButton  *B;

    m = _talkbk;
    for (i = 0; i < 2; i++)
    {
	B = _buttons [B_TKB1 + i];
	if (m & 1) B->set_state (B->state () | 2);
	else       B->set_state (B->state () & ~2); 
	m >>= 1;
    }
}


void Mainwin::makegui (void)
{
    int i, x, y;

    XImage *I;

    x = 10;
    y = 5;
    I = loadpng ("binp1");
    _buttons [B_INP1] = new Pbutt1 (this, this, B_INP1, I, x, y, 26, 26);
    I = loadpng ("binp2");
    _buttons [B_INP2] = new Pbutt1 (this, this, B_INP2, I, x + 30, y, 26, 26);
    I = loadpng ("binp3");
    _buttons [B_INP3] = new Pbutt1 (this, this, B_INP3, I, x + 60, y, 26, 26);
    I = loadpng ("binp4");
    _buttons [B_INP4] = new Pbutt1 (this, this, B_INP4, I, x + 90, y, 26, 26);
    addtext (this, &Tst0, x + 10, y + 26, 100, 20, "Input", 0);
    _inpsel = 1;
    _buttons [B_INP1]->set_state (2);

    x = 10;
    y = 63;
    I = loadpng ("bleft");
    _buttons [B_LEFT] = new Pbutt1 (this, this, B_LEFT, I, x,      y, 20, 20);
    I = loadpng ("brite");
    _buttons [B_RITE] = new Pbutt1 (this, this, B_RITE, I, x + 25, y, 20, 20);
    I = loadpng ("bmono");
    _buttons [B_MONO] = new Pbutt1 (this, this, B_MONO, I, x + 50, y, 20, 20);
    I = loadpng ("bdimm");
    _buttons [B_DIMM] = new Pbutt1 (this, this, B_DIMM, I, x + 80, y, 35, 20);
    _monmod = 0;

    x = 155;
    I = loadpng ("bopon");
    _buttons [B_OUT1] = new Pbutt1 (this, this, B_OUT1, I, x,       y, 26, 20);
    _buttons [B_OUT2] = new Pbutt1 (this, this, B_OUT2, I, x + 80,  y, 26, 20);
    I = loadpng ("bhdph");
    _buttons [B_HDPH] = new Pbutt1 (this, this, B_HDPH, I, x + 125, y, 26, 20);
    _outsel = 1;
    _buttons [B_OUT1]->set_state (2);

    x = 880;
    y = 44;
    I = loadpng ("btkb1");
    _buttons [B_TKB1] = new Pbutt2 (this, this, B_TKB1, I, x, y, 36, 36);
    I = loadpng ("btkb2");
    _buttons [B_TKB2] = new Pbutt2 (this, this, B_TKB2, I, x + 44, y, 36, 36);
    addtext (this, &Tst0, 845, y + 5, 25, 20, "TB", 1);
    _talkbk = 0;

    for (i = 0; i < N_BUTT; i++) _buttons [i]->x_map ();

    _volgeom._backg = XftColors [C_MAIN_BG];
    _volgeom._image [0] = loadpng ("volumesect");
    _volgeom._lncol [0] = 1;
    _volgeom._x0 = 19;
    _volgeom._y0 = 10;
    _volgeom._dx = 41;
    _volgeom._dy = 41;
    _volgeom._xref = 20.5;
    _volgeom._yref = 20.5;
    _volgeom._rad = 16;

    _rvolume [0] = new Rlevel100A (this, this, R_VOL1, &_volgeom, 145, 1, 10);
    _rvolume [0]->x_map ();
    _rvolume [0]->set_value (-20.0);
    _rvolume [1] = new Rlevel100A (this, this, R_VOL2, &_volgeom, 225, 1, 10);
    _rvolume [1]->x_map ();
    _rvolume [1]->set_value (-20.0);

    _jclient->set_out1gain (_rvolume [0]->value ());
    _jclient->set_out2gain (_rvolume [1]->value ());
    _jclient->set_out2hdph (false);

    _tb1geom._backg = XftColors [C_MAIN_BG];
    _tb1geom._image [0] = loadpng ("talkbksect");
    _tb1geom._lncol [0] = 1;
    _tb1geom._x0 = 0;
    _tb1geom._y0 = 0;
    _tb1geom._dx = 35;
    _tb1geom._dy = 35;
    _tb1geom._xref = 17.5;
    _tb1geom._yref = 17.5;
    _tb1geom._rad = 10;
    _rtblev1 = new Rlevel100A (this, this, R_TB1, &_tb1geom, 880, 0, 10);
    _rtblev1->x_map ();
    _jclient->set_tb1gain (_rtblev1->value ());

    _tb2geom._backg = XftColors [C_MAIN_BG];
    _tb2geom._image [0] = _tb1geom._image [0];
    _tb2geom._lncol [0] = 1;
    _tb2geom._x0 = 44;
    _tb2geom._y0 = 0;
    _tb2geom._dx = 35;
    _tb2geom._dy = 35;
    _tb2geom._xref = 17.5;
    _tb2geom._yref = 17.5;
    _tb2geom._rad = 10;
    _rtblev2 = new Rlevel100A (this, this, R_TB2, &_tb2geom, 880, 0, 10);
    _rtblev2->x_map ();
    _jclient->set_tb2gain (_rtblev2->value ());

    _dispwin = new X_subwin (this, 330, 2, DISP_XS, DISP_YS, XftColors [C_DISP_BG]->pixel); 
    _dispwin->x_add_events (ExposureMask);
    _dispwin->x_map ();

    x = 12;
    y = 20;
    for (i = 0; i < 2; i++)
    {
        _meters [i] = new Kmeter (_dispwin, x, y, Kmeter::HOR, Kmeter::K20);
	_meters [i]->x_map ();
        y += Kmeter::LINEW + 4;
    }

    y += 20;
    _cmeter = new Cmeter (_dispwin, x + 227, y, Cmeter::HOR);
    _cmeter->x_map ();
    _pkbutt = new X_tbutton (_dispwin, this, &Bst0, x + 445, y + 3, 0, 0, B_PRES);
    _pkbutt->x_map ();
    _peak1 = 1e-4f;
    _peak2 = 1e-4f;
}



XImage *Mainwin::loadpng (const char *file)
{
    char    s [1024];
    XImage  *I;

    sprintf (s, "%s/%s.png", SHARED, file);
    I = png2img (s, disp (), 0);
    if (I) return I;
    exit (1);
}


void Mainwin::redraw_main (void)
{
    XPutImage (dpy (), win (), dgc (), _volgeom._image [0], 0, 0, 145, 1, 79, 61);
    XPutImage (dpy (), win (), dgc (), _volgeom._image [0], 0, 0, 225, 1, 79, 61);
    XPutImage (dpy (), win (), dgc (), _tb1geom._image [0], 0, 0, 880, 0, 79, 35);
}


void Mainwin::redraw_disp (void)
{
    int      i, x, y;
    X_draw   D (dpy (), _dispwin->win (), dgc (), 0);
    XImage   *I;

    D.setcolor (XftColors [C_MAIN_DS]->pixel);
    D.move (0, DISP_YS - 1);
    D.draw (0, 0);
    D.draw (DISP_XS - 1, 0);
    D.setcolor (XftColors [C_MAIN_LS]->pixel);
    D.move (1, DISP_YS - 1);
    D.draw (DISP_XS - 1, DISP_YS - 1);
    D.draw (DISP_XS - 1, 1);
    I = _meters [0]->scale ();

    x = 12;
    y =  4;
    XPutImage (dpy (), _dispwin->win (), dgc (), I, 0, 4, x, y, I->width, 16);
    y += 22;
    for (i = 1; i < 2; i++)
    {
        XPutImage (dpy (), _dispwin->win (), dgc (), I, 0, 0, x, y, I->width, 4);
        y += Kmeter::LINEW + 4;
    }
    XPutImage (dpy (), _dispwin->win (), dgc (), I, 0, 0, x, y, I->width, 16);
    I = _cmeter->scale ();
    y += 30;
    XPutImage (dpy (), _dispwin->win (), dgc (), I, 0, 0, x + 227, y, I->width, 16);
}


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


