/* 
    callbacks for Bt848 frame grabber driver

    Copyright (C) 1996,97 Marcus Metzler (mocm@thp.uni-koeln.de)

    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.
*/

/*
 * callbacks.cc 
 */
#include <stdio.h>
#include <stdlib.h>
#include <iostream.h>
#include <fstream.h>
#include <strstream.h>

#include <Xm/Scale.h>
#include <Xm/List.h>
#include <Xm/TextF.h>
#include <Xm/DialogS.h>
#include <Xm/MessageB.h>
#include <Xm/ToggleBG.h>
#include <Xm/Protocols.h>
#include <Xm/Text.h>


#include "globals.h"
#include "callbacks.h"
#include "TVscreen.h"
#include "allwidgets.h"
#include "snap.h"
#include "sound.h"


/*                          */
/* user answers             */
/*                          */
void nandemo(Widget w, XtPointer client_data, XtPointer call_data)
{
  int *kotae =(int *) client_data;
  XmAnyCallbackStruct *cbs = (XmAnyCallbackStruct *) call_data;
  
  switch(cbs->reason){
  case XmCR_OK:
    *kotae= 1;
    break;

  case XmCR_CANCEL:
    *kotae= 0;
    break;
    
  default:
    return;
  }
}
    



/*                          */
/* write message            */
/*                          */
void mess(char *msg, char *msg2=NULL)
{
  char buff[1024];
  ostrstream str(buff,1024);

  str << "XTV: " << msg;
  if (msg2)  str << msg2;
  str << ends;

  if(Message && XtIsRealized(Message)) 
    XtVaSetValues(Message,XmNvalue,buff,NULL);
  if(Message2 && XtIsRealized(Message2)) 
    XtVaSetValues(Message2,XmNvalue,buff,NULL);
}
  
/*                          */
/* help messages            */
/*                          */
void help_mess(Widget w, XtPointer client_data, XEvent *event,char *any=NULL)
{
  int me=int(client_data);
  
  switch(me){

  case TEXTMESS:
    mess("Input Channel Name");
    break;

  case LISTMESS:
    mess("Choose Channel");
    break;

  case NORMMESS:
    mess("Select TV Norm");
    break;

  case INPUTMESS:
    mess("Select Input");
    break;

  case TUNERMESS:
    mess("Use Tuner Input");
    break;
    
  case COMP1MESS:
    mess("Use Composite 1 Input");
    break;
    
  case COMP2MESS:
    mess("Use Composite 2 Input");
    break;
    
  case SVHSMESS:
    mess("Use SVHS Input");
    break;
    
  case BRIGHTMESS:
    mess("Adjust Brightness");
    break;
    
  case COLORMESS:
    mess("Adjust Color");
    break;
    
  case CONTRASTMESS:
    mess("Adjust Contrast");
    break;
    
  case HUEMESS:
    mess("Adjust Hue");
    break;
    
  case PALMESS:
    mess("Set TV-norm to PAL");
    break;
    
  case SECAMMESS:
    mess("Set TV-norm to SECAM");
    break;
    
  case NTSCMESS:
    mess("Set TV-norm to NTSC");
    break;
    
  case DELAYMESS:
    mess("Adjust Screen Refresh Delay");
    break;
    
  case FREQMESS:
    mess("Adjust Frequency");
    break;
    
  case CHANMESS:
    mess("Change Channel");
    break;
    
  case FINEMESS:
    mess("Fine Tuning");
    break;
    
  case QUITMESS:
    mess("Quit Program");
    break;
    
  case CLOSEMESS:
    mess("Close Window");
    break;
    
  case CHANNELMESS:
    mess("Open Channel Window");
    break;
    
  case ADDMESS:
    mess("Add Channel to List");
    break;
    
  case LOADMESS:
    mess("Load Channel");
    break;
    
  case SAVEMESS:
    mess("Save Channel");
    break;

  case DELETEMESS:
    mess("Delete Channel");
    break;
    
  case CHANUP:
    mess("Switch Channel up");
    break;

  case CHANDOWN:
    mess("Switch Channel down");
    break;
    
  case VOLMESS:
    mess("Adjust Volume"); 
    break;
    
  case SEQMESS:
    mess("Changing Sequence Length"); 
    break;

  }
}

/*                          */
/* get saved list filename  */
/*                          */
get_xtvrc(char *path,unsigned size)
{
  const char *home=getenv("HOME");
  const char *file=".xtvrc";

  strncpy(path,home,size);
  strncat(path,"/",size);
  strncat(path,file,size);
}

/*                            */
/* Help Callback with message */
/*                            */
#define MSG "XTvscreen Version 0.5.22\n No Help available"
// still needs some work

void help_cb(Widget w, XtPointer client_data, XtPointer call_data)
{
  static Widget dial;

  if(!dial){
    Arg args[5];
    int n=0;
    XmString msg = XmStringCreateLtoR (MSG,XmFONTLIST_DEFAULT_TAG);
    XtSetArg (args[n],XmNmessageString,msg); n++;
    dial =XmCreateInformationDialog(toplevel,"help_dialog",args,n);
  }
  XtManageChild(dial);
  XtPopup(XtParent(dial),XtGrabNone);
  mess("There's no help");
}


/*                        */
/* Callback for File Menu */
/*                        */
// just the Quit button
void file_cb(Widget w, XtPointer client_data, XtPointer call_data)
{
  int item_no=int(client_data);

  switch(item_no){
  case 0:
    /* Quit */
    mess("bye bye");
    XtAppSetExitFlag(app_context);
  }  
}



/*                            */
/* Set frequency for TV Tuner */
/*                            */
void new_freq(Widget w, XtPointer client_data, XtPointer call_data)
{
  int fr,ch,fi;

  if(freq==w){ // new value is set directly via frequency
    XmScaleGetValue(w,&fr);
    fr=fr*16/100;
    ch=CountryList.freqtochan(fr);
    if (!ch) ch=200;
    fi=CountryList.freqtofine(fr);
    XtVaSetValues(chan,XtNvalue,ch,NULL);
    XtVaSetValues(fine,XtNvalue,fi,NULL);
  }
  if(chan==w){ // new value is set via channel selection
    XmScaleGetValue(w,&ch);
    fr=CountryList.cftofreq(ch,0);
    fi=0;
    XtVaSetValues(freq,XtNvalue,fr*100/16,NULL);
    XtVaSetValues(fine,XtNvalue,fi,NULL);
  }
  if(fine==w){ // new value is set using fine tuning
    XmScaleGetValue(chan,&ch);
    fr=CountryList.cftofreq(ch,0);
    XmScaleGetValue(fine,&fi);
    fr+=fi;
    XtVaSetValues(freq,XtNvalue,fr*100/16,NULL);
  }
  XtVaSetValues(screen,XtNfreq,fr,NULL);
}

/*                                  */
/* Set Delay                        */
/* Set Brightness                   */
/* Set Color                        */
/* Set Hue                          */
/* Set Contrast                     */
/*                                  */

void new_dbch(Widget w, XtPointer client_data, XtPointer call_data)
{
  int call=(int)client_data;
  
  switch(call){
  case 0:
    int delay;
    XmScaleGetValue(w,&delay);
    XtVaSetValues(screen,XtNdelay,delay,NULL);
    break;
    
  case 1:
    int bright;
    XmScaleGetValue(w,&bright);
    XtVaSetValues(screen,XtNbright,bright,NULL);
    break;

  case 2:
    int color;
    XmScaleGetValue(w,&color);
    XtVaSetValues(screen,XtNcol,color,NULL);
    break;

  case 3:
    int hue;
    XmScaleGetValue(w,&hue);
    XtVaSetValues(screen,XtNhue,hue,NULL);
    break;

  case 4:
    int contrast;
    XmScaleGetValue(w,&contrast);
    XtVaSetValues(screen,XtNcontrast,contrast,NULL);
    break;

  case 5:
    int vol;
    XmScaleGetValue(w,&vol);
    set_master_vol(vol);
    break;
  }
}

/*                                     */
/*   Set number of images per sequence */
/*                                     */
void new_seq(Widget w, XtPointer client_data, XtPointer call_data)
{
  int snum;
  XmScaleGetValue(w,&snum);
  app_data.seqnum=snum;
}
 
/*                          */
/*  Options callback */
/*                          */
void opt_cb(Widget &w,Boolean DO)
{
  if (DO) {
    XtPopup(OptW,XtGrabNone);
  } else if (OptW) {
    XtPopdown(OptW);
  }
}


/*                                  */
/* Toggle Norm                      */
/*                                  */
void norm_tog(Widget w, XtPointer client_data, XtPointer call)
{
  int norm=int(client_data);
  XmToggleButtonCallbackStruct *state=(XmToggleButtonCallbackStruct *)call;

 if(state->set){
   XtVaSetValues(screen,XtNnorm,norm,NULL); 
   
   switch(norm){
   case PAL:
     mess("New TV norm is PAL");
     break;
   case NTSC:
     mess("New TV norm is NTSC");
     break;
   case SECAM:
     mess("New TV norm is SECAM");
     break;
   }
 }
}


/*                          */
/* Input selection Callback */
/*                          */
void input_sel(Widget w, XtPointer client_data, XtPointer call)
{
  int j;
  static int col=0;
  int in= int(client_data);
  static Boolean set=FALSE;

  /* Get normal shadow color */ 
  //there is probably an easier way, but this works
  if (!set){
    int col1,col2,col3;
    XtVaGetValues(input1[1],XmNbottomShadowColor,&col1,NULL);
    XtVaGetValues(input1[2],XmNbottomShadowColor,&col2,NULL);
    XtVaGetValues(input1[3],XmNbottomShadowColor,&col3,NULL);
    if (col1==col2) col=col1;
    else if(col1==col3) col=col1;
    else col=col2;
    set=TRUE;
  }

  
  // check where the call is coming from and set shades and toggle buttons
  if(w==input2[in]){
    XmToggleButtonCallbackStruct *state=(XmToggleButtonCallbackStruct *)call;
    if(state->set){
      XtVaSetValues(screen,XtNinputSelect,in,NULL);
      for (j=0;j<4;j++){
	XtVaSetValues(input1[j],XmNbottomShadowColor,col,NULL);
      }
      XtVaSetValues(input1[in],XmNbottomShadowColor,
		    BlackPixel(XtDisplay(input1[in]),
			       DefaultScreen(XtDisplay(input1[in]))),NULL);
    }
  } else { 
    XmToggleButtonGadgetSetState(input2[in],True,True);
    XtVaSetValues(screen,XtNinputSelect,in,NULL);
    for (j=0;j<4;j++){
      XtVaSetValues(input1[j],XmNbottomShadowColor,col,NULL);
    }
    XtVaSetValues(input1[in],XmNbottomShadowColor,
		  BlackPixel(XtDisplay(input1[in]),
			     DefaultScreen(XtDisplay(input1[in]))),NULL); 
  }

  switch(in){
  case TUNER:
    mess("Selected Tuner input");
    break;
  case COMP1:
    mess("Selected Composite 1 input");
    break;
  case COMP2:
    mess("Selected Composite 2 input");
    break;
  case SVHS:
    mess("Selected S-VHS input");
    break;
  }
}


/*                                  */
/* Set Channel Color, Brightnes,    */
/* Hue and Contrast                 */
/*                                  */
set_cbhc(int c,int b,int h,int co)
{
  Widget widget;

  XtVaSetValues(screen,XtNcol,c,XtNbright,b,XtNhue,h,
		    XtNcontrast,co,NULL);
  
  if (widget =XtNameToWidget(conwid,"Color")) XmScaleSetValue(widget,c);
  if (widget =XtNameToWidget(conwid,"Bright")) XmScaleSetValue(widget,b);
  if (widget =XtNameToWidget(conwid,"Hue")) XmScaleSetValue(widget,h);
  if (widget =XtNameToWidget(conwid,"Contrast")) XmScaleSetValue(widget,co);
}

/*                                  */
/* Set Channel Norm and Input       */
/*                                  */
set_ni(int n,int i)
{
  XtVaSetValues(screen,XtNnorm,n,XtNinputSelect,i,NULL);

  input_sel(input1[i], (XtPointer) i, NULL);
  XmToggleButtonGadgetSetState(norm[n],True,True);
}

/*                          */
/* close popup by button    */
/*                          */
// works on all popups
void close(Widget w, XtPointer client_data, XtPointer call_data)
{
    XtPopdown(Widget(client_data));  
}

/*                          */
/* channel up/down          */
/*                          */
void chan_ud(Widget w, XtPointer client_data, XtPointer call_data)
{
  channel *h=&head;
  int ud=int(client_data);
  int *pos_lst;
  int pos_cnt;
  int newp=0;

  if(XmListGetSelectedPos(list,&pos_lst,&pos_cnt))
    {
      switch (ud){
	
      case CHANDOWN:
	newp=pos_lst[0]+1;
	if(newp>h->count()) newp=1;
	break;
	
      case CHANUP:
	newp=pos_lst[0]-1;
	if(newp<1) newp=h->count();
	break;
      }
      XtFree((char*)pos_lst);
    }
 
  XmListSelectPos(list,newp,True);
}


/*                          */
/* select from channel list */
/*                          */
void select(Widget w, XtPointer client_data, XtPointer call_data)
{
  XmListCallbackStruct *cbs=(XmListCallbackStruct *)call_data;
  channel *h=&head;
  int fr,ch,fi;

  while (h->next && h->number != cbs->item_position) h=h->next; // which item?
  if(h->input == TUNER){
    fr=h->frequency;             // set frequency and channel
    ch=CountryList.freqtochan(fr);
    fi=CountryList.freqtofine(fr);
    XtVaSetValues(screen,XtNfreq,fr,NULL);
    XtVaSetValues(freq,XmNvalue,fr*100/16,NULL);
    XtVaSetValues(chan,XmNvalue,ch,NULL);
    XtVaSetValues(fine,XmNvalue,fi,NULL);
  }
  set_cbhc(h->color,h->brightness,h->hue,h->contrast);
  set_ni(h->norm,h->input);

  mess("Selected channel ",h->name);
}

/*                                */
/* delete item from channel list  */
/*                                */
void delete_item(Widget w, XtPointer client_data, XtPointer call_data)
{
  channel *h=&head;
  int *posl,poscnt,p;

  if(XmListGetSelectedPos(list,&posl,&poscnt)){
    p=posl[0];
    while (h->next && h->number != p) h=h->next; // which one is it?
    mess("Deleted channel ",h->name);
    h->~channel();
    XmListDeletePos(list,p);
    free(posl);
  }  
}


/*                          */
/* save channel list        */
/*                          */
void save_xtv(Widget w, XtPointer client_data, XtPointer call_data)
{
  char FILE[30];
  channel *h;
  
  get_xtvrc(FILE,30);
  ofstream fout(FILE);

  fout << "#" << endl;
  fout << "# List of Channels for xtvscreen" << endl;
  fout << "#" << endl;

  h=&head;
  while(h->next){ 
    h=h->next;
    fout << *h << endl;
  }

  mess ("Saving channel list");
}


/*                          */
/* load channel list        */
/*                          */
void load_xtv(Widget w, XtPointer client_data, XtPointer call_data)
{
  char FILE[30];
  channel *h;
  channel *n;
  Boolean add;

  get_xtvrc(FILE,30);
  ifstream fin(FILE);


  while(fin.ignore(1000, '*')!=NULL){
    n= new channel(&head,0,"empty",DCOL,DBRI,DHUE,DCONTRAST,DNORM,DIN);
    fin >> *n;
    h=&head;
    add=TRUE;
    while(h->next){
      h=h->next;
                     // do we already have that frequency ?
      if(!strcmp(h->name,n->name) && n!=h){ 
	n->~channel();
	add=FALSE;	
	mess("Can't load existing channel:",h->name);
      }
    }
    if(add){
      XmString str=XmStringCreateLocalized(h->name);
      XmListAddItemUnselected(list,str,0);
      XmStringFree(str);
    }
  }

  int fr,in;
  XtVaGetValues(screen,XtNfreq,&fr,XtNinputSelect,&in,NULL);
  h=&head;
 
// if there is no default frequency   
// use first item of the list
  if (!fr && h->next){
    int ch,fi;
    h=h->next;
    if(h->input == TUNER){
      fr=h->frequency;
      ch=CountryList.freqtochan(fr);
      fi=CountryList.freqtofine(fr);
      XtVaSetValues(screen,XtNfreq,fr,NULL);
      XtVaSetValues(freq,XmNvalue,fr*100/16,NULL);
      XtVaSetValues(chan,XmNvalue,ch,NULL);
      XtVaSetValues(fine,XmNvalue,fi,NULL);
    }
    set_cbhc(h->color,h->brightness,h->hue,h->contrast);
    set_ni(h->norm,in);
  }

  mess("Loading channel list ");
}


/*                          */
/* channel callback         */
/*                          */
void chan_cb(Widget w, XtPointer client_data, XtPointer call_data)
{
  channel *ch;
  int fr;
  char *name;
  int n=0,call;
  XmString str,*strlist;
  static Arg args[10];
  int c,br,hu,co,no,in;

  call=int(client_data);
  
  XtManageChild(list);
  XtManageChild(rowcol);

  switch(call){
  case 0:   //popup channel list
    XtPopup(ChanW,XtGrabNone);
    break;
    
  case 1:  // add new channel and popup list
    name=XmTextFieldGetString(text);
    if (!name || !*name){
      XtFree(name);
      return;
    }
    channel *h=&head;
    Boolean add=TRUE;
    while(h->next){
      h=h->next;
      if(!strcmp(h->name,name)) {
	add=FALSE;	
	mess("This name is already in list: ",h->name);
	
	char q[255];
	ostrstream str(q,255);
	str << "Do you want to overwrite channel " << name << "?" << ends;
	if(Nandeska(w,q)){
	  XtVaGetValues(screen,XtNcol,&c,XtNbright,&br,XtNhue,&hu,
			XtNcontrast,&co,XtNnorm,&no,XtNinputSelect,&in,XtNfreq,&fr,
			NULL);
	  if(in != TUNER) fr=0;
	  h->frequency=fr;
	  h->color=c;
	  h->brightness=br;
	  h->hue=hu;
	  h->contrast=co;
	  h->norm=no;
	  h->input=in;
	}
      }
    }
    if(add){
      XtVaGetValues(screen,XtNcol,&c,XtNbright,&br,XtNhue,&hu,
		    XtNcontrast,&co,XtNnorm,&no,XtNinputSelect,&in,XtNfreq,&fr,
		    NULL);
      if(in != TUNER) fr=0;
      ch= new channel(&head,fr,name,c,br,hu,co,no,in);
      str=XmStringCreateLocalized(name);
      mess("Adding channel ",name);
      XtFree(name);
      XmListAddItemUnselected(list,str,0);
      XmStringFree(str);
      XmTextFieldSetString(text,"");
    } 
    XtPopup(ChanW,XtGrabNone);
    break;
  }
}
  

/*                          */
/*   PopdownCallback        */
/*                          */
void popdown(Widget w, XtPointer client_data, XtPointer call_data)
{
 XtVaSetValues(Widget(client_data),XmNset,False,NULL);
}

/*                          */
/*  Popup Control Window    */
/*                          */
void popcon(Widget &w,Boolean DO)
{
  if (DO) {
    XtPopup(dialog,XtGrabNone);
  } else if (dialog) {
    XtPopdown(dialog);
  }
}


/*                                     */
/*   Callback for Start MenuButton     */
/*                                     */
void start_cb(Widget w, XtPointer client_data, XtPointer call_data)
{
  int item_no=int(client_data);
  Boolean CAP,CON,ASP;
  static Boolean done=False;
  static Boolean done2=False;

  switch(item_no){
  case 0:
    /* Capture on/off */
    XtVaGetValues(screen,XtNcap,&CAP,NULL);
    XtVaSetValues(screen,XtNcap,!CAP,NULL);
    if (!CAP) mess("Capturing is on");
    else mess("Capturing is off");
    break;

  case 1:
    /* Control Window on/off */
    XtVaGetValues(w,XmNset,&CON,NULL);
    XtManageChild(conwid);
    XtManageChild(chan);
    XtManageChild(Message2);
    XtManageChild(pane);
    popcon(w,CON);
    int n;
    XtVaGetValues(screen,XtNnorm,&n,NULL);
    XmToggleButtonGadgetSetState(norm[n],True,True);
    
    n=0;
    XtVaGetValues(screen,XtNinputSelect,&n,NULL);
    XmToggleButtonGadgetSetState(input2[n],True,True);

    

    if (!done) XtAddCallback(dialog,XmNpopdownCallback,popdown,XtPointer(w));
    done=True;
    break;

  case 2:
    /* channel list on */
    chan_cb(w,0,NULL);
    break;

  case 3:
    /* options on/off */
    XtVaGetValues(w,XmNset,&CON,NULL);

    XtManageChild(OptF);
    opt_cb(w,CON);
    if (!done2) XtAddCallback(dialog,XmNpopdownCallback,popdown,XtPointer(w));
    done2=True;
    break;

  case 4:
    /* fixed aspect ratio on/off */
    XtVaGetValues(w,XmNset,&ASP,NULL);
    
    if (ASP) {
      int h;
      
      XtVaGetValues(toplevel,XtNheight,&h,NULL);
      app_data.aspect=app_data.dasp;
      XtVaSetValues(toplevel,XtNheight,h+4,NULL);
    }
    else app_data.aspect=NOASP;
    break;
  
  case 5:
    XtVaGetValues(screen,XtNcap,&CAP,NULL);
    if (CAP){
      int nb;
      char nbr[5];
      ostrstream o(nbr,5);

      nb=snapit(screen,app_data.snapname,app_data.full_screen);
      o << hex << setw(3) << setfill(int('0')) << nb << ends;
      mess("Made snapshot number:",nbr);
    } else mess ("Capturing is off");      
    break;

  case 6:
    XtVaGetValues(screen,XtNcap,&CAP,NULL);
    if (CAP){
      int nb;
      char nbr[20];
      ostrstream o(nbr,20);

      mess("Starting sequence");
      nb=sequence(screen,app_data.sequname,app_data.seqnum);

      o << hex << setw(3) << setfill(int('0')) << nb;
      mess("Made sequence number:",nbr);
    } else mess ("Capturing is off");      
    break;
 
  default:
    break;
  }
}


/*                                     */
/*   Callback for Audio MenuButton     */
/*                                     */
void audio_cb(Widget w, XtPointer client_data, XtPointer call_data)
{
  int item_no=int(client_data);
  
  switch(item_no){
  case 0:
  case 1:
  case 2:
  case 3:
  case 4:
    XtVaSetValues(screen,XtNaudio,item_no,NULL);
    break;
  default:
   break;
  }
}





void sizecheck(Widget w, XtPointer client_data, XEvent *event, char *c)
{  
  XConfigureEvent *ce=(XConfigureEvent *) event;
  if (ce->type !=ConfigureNotify) return;
  if (app_data.aspect == NOASP) return;
  
  int xa,ya;
  int dw=0,dh=0;
  int  ww=0,hw=0,we=0,he=0;  
  
  XtVaGetValues(screen,XtNwidth,&ww,XtNheight,&hw,NULL);
  
  we=ce->width;
  he=ce->height;
  dw=we-ww;
  dh=he-hw;
  
  switch (app_data.aspect)
    {
    case A4_3:
      xa=4; ya=3;
      break;
      
    case A16_9:
      xa=16; ya=9;
      break;
    }

  if (he < ya*ww/xa){
    XtVaSetValues(toplevel,XtNheight,ya*ww/xa+dh,NULL);
  }else
    if (he > ya*ww/xa){
      XtVaSetValues(toplevel,XtNwidth,xa*hw/ya+dw,NULL);
    }
}

