(require snd-gtk)

(provide 'snd-gtk-effects-utils.scm)

(with-let *gtk* 

  (define (all-chans)
    (let ((sndlist ())
	  (chnlist ()))
      (for-each (lambda (snd)
		  (do ((i (- (channels snd) 1) (- i 1)))
		      ((< i 0))
		    (set! sndlist (cons snd sndlist))
		    (set! chnlist (cons i chnlist))))
		(sounds))
      (list sndlist chnlist)))
  
  (define (update-label effects)
    (for-each (lambda (effect) (effect)) effects))
  
  (define (effect-target-ok target)
    (case target 
      ((sound) (pair? (sounds)))
      ((selection) (selection?))
      (else (and (selected-sound)
		 (>= (length (marks (selected-sound) (selected-channel))) 2)))))
  
  (define* (make-effect-dialog label ok-callback help-callback reset-callback target-ok-callback)
    ;; make a standard dialog
    ;; callbacks take 2 args: widget data
    (let ((new-dialog (gtk_dialog_new)))
      
      (if (defined? 'gtk_widget_set_clip) ; gtk 3.14.0
	  (gtk_window_set_transient_for (GTK_WINDOW new-dialog) (GTK_WINDOW ((main-widgets) 1))))
      (gtk_window_set_title (GTK_WINDOW new-dialog) label)
      (gtk_window_set_default_size (GTK_WINDOW new-dialog) -1 -1)
      (gtk_window_set_resizable (GTK_WINDOW new-dialog) #t)
      (gtk_widget_realize new-dialog)
      (g_signal_connect new-dialog "delete_event" 
			(lambda (w ev data) 
			  (gtk_widget_hide new-dialog)
			  #t) ; this is crucial -- thanks to Kjetil for catching it!
			#f)
      
      (let ((dismiss-button (gtk_dialog_add_button (GTK_DIALOG new-dialog) "Go Away" GTK_RESPONSE_NONE)))
	(g_signal_connect dismiss-button "clicked" (lambda (w data) (gtk_widget_hide new-dialog)) #f)
	(gtk_widget_show dismiss-button)
	(gtk_widget_set_name dismiss-button "quit_button"))
      
      (let ((ok-button (gtk_dialog_add_button (GTK_DIALOG new-dialog) "DoIt" GTK_RESPONSE_NONE)))
	(g_signal_connect ok-button "clicked" ok-callback #f)
	(gtk_widget_show ok-button)
	(gtk_widget_set_name ok-button "doit_button")
	
	(if reset-callback
	    (let ((reset-button (gtk_dialog_add_button (GTK_DIALOG new-dialog) "Reset" GTK_RESPONSE_NONE)))
	      (g_signal_connect reset-button "clicked" reset-callback #f)
	      (gtk_widget_set_name reset-button "reset_button")
	      (gtk_widget_show reset-button)))
	
	(let ((help-button (gtk_dialog_add_button (GTK_DIALOG new-dialog) "Help" GTK_RESPONSE_NONE)))
	  (g_signal_connect help-button "clicked" help-callback #f)
	  (gtk_widget_show help-button)
	  (gtk_widget_set_name help-button "help_button"))
	
	(if target-ok-callback
	    (begin
	      (gtk_widget_set_sensitive ok-button (target-ok-callback))
	      (hook-push effects-hook
			 (lambda (hook) (gtk_widget_set_sensitive ok-button (target-ok-callback)))))
	    (begin
	      (gtk_widget_set_sensitive ok-button (pair? (sounds)))
	      (hook-push effects-hook
			 (lambda (hook) (gtk_widget_set_sensitive ok-button (pair? (sounds)))))))
	
	(g_object_set_data (G_OBJECT new-dialog) "ok-button" (GPOINTER ok-button))
	new-dialog)))
  
  (define (change-label w new-label)
    (if w (gtk_label_set_text (GTK_LABEL (if (GTK_IS_LABEL w) w (gtk_bin_get_child (GTK_BIN w)))) new-label)))
  
  
  ;; -------- log scaler widget
  
  (define log-scale-ticks 500) ; sets precision (to some extent) of slider 
  
  (define (scale-log->linear lo val hi)
    ;; given user-relative low..val..hi return val as scale-relative (0..log-scale-ticks)
    (let (;; using log 2 here to get equally spaced octaves
	  (log-lo (log (max lo 1.0) 2))
	  (log-hi (log hi 2))
	  (log-val (log val 2)))
      (floor (* log-scale-ticks (/ (- log-val log-lo) (- log-hi log-lo))))))
  
  (define (scale-linear->log lo val hi)
    ;; given user-relative lo..hi and scale-relative val, return user-relative val
    ;; since log-scale widget assumes 0..log-scale-ticks, val can be used as ratio (log-wise) between lo and hi
    (let ((log-lo (log (max lo 1.0) 2))
	  (log-hi (log hi 2)))
      (expt 2.0 (+ log-lo (* (/ val log-scale-ticks) (- log-hi log-lo))))))
  
  (define (scale-log-label lo val hi)
    (format #f "~,2F" (scale-linear->log lo val hi)))
  
  (define (add-sliders dialog sliders)
    ;; sliders is a list of lists, each inner list being (title low initial high callback scale ['log])
    ;; returns list of widgets (for reset callbacks)
    (let* ((mainform (gtk_box_new GTK_ORIENTATION_VERTICAL 2))
	   (use-hbox (and (pair? sliders) (null? (cdr sliders))))
	   (table (if (not use-hbox) (gtk_grid_new))))
      (gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG dialog))) mainform #f #f 4)
      (gtk_widget_show mainform)
      (if (not use-hbox)
	  (begin
	    (gtk_box_pack_start (GTK_BOX mainform) table #f #f 4)
	    (gtk_grid_set_row_spacing (GTK_GRID table) 4)
	    (gtk_grid_set_column_spacing (GTK_GRID table) 4)
	    (gtk_widget_show table)))
      (map 
       (let ((slider 0))
	 (lambda (slider-data)
	   (let* ((title (slider-data 0))
		  (low (slider-data 1))
		  (initial (slider-data 2))
		  (high (slider-data 3))
		  (func (slider-data 4))
		  (scaler (slider-data 5))
		  (use-log (and (= (length slider-data) 7)
				(eq? (slider-data 6) 'log)))
		  (hbox (and use-hbox (gtk_box_new GTK_ORIENTATION_HORIZONTAL 0)))
		  (label (gtk_label_new 
			  (format #f (if use-hbox
					 (if use-log 
					     (values "~A: ~,2F" title initial)
					     (values "~A:" title))
					 (if use-log
					     (values "~A (~,2F)" title initial)
					     (values "~A" title))))))
		  (adj (if use-log 
			   (gtk_adjustment_new (scale-log->linear low initial high) 0 log-scale-ticks 1 10 1)
			   (gtk_adjustment_new initial low high 0.0 0.0 0.0)))
		  (scale (gtk_scale_new GTK_ORIENTATION_HORIZONTAL (GTK_ADJUSTMENT adj))))
	     (if use-hbox
		 (begin
		   (gtk_box_pack_start (GTK_BOX mainform) hbox #f #f 2)
		   (gtk_widget_show hbox)
		   (gtk_box_pack_start (GTK_BOX hbox) label #f #f 6))
		 (gtk_grid_attach (GTK_GRID table) label 0 slider 1 1))
	     (gtk_widget_show label)
	     (gtk_scale_set_digits (GTK_SCALE scale)
				   (cond (use-log 0)
					 ((assoc scaler '((1000 . 3) (100 . 2) (10 . 1)) =) => cdr)
					 (else 0)))
	     (gtk_scale_set_draw_value (GTK_SCALE scale) (not use-log))
	     (if use-hbox
		 (gtk_box_pack_start (GTK_BOX hbox) scale #t #t 0)
		 (begin
		   (gtk_widget_set_hexpand (GTK_WIDGET scale) #t)
		   (gtk_grid_attach (GTK_GRID table) scale 1 slider 1 1)
		   (set! slider (+ 1 slider))))
	     (gtk_widget_show scale)
	     (let ((label-func (if (not use-log)
				   func
				   (lambda (w d) 
				     (func w d)
				     (change-label label 
						   (format #f "~A: ~,2F" 
							   title 
							   (scale-linear->log low (gtk_adjustment_get_value (GTK_ADJUSTMENT adj)) high)))))))
	       (g_signal_connect adj "value_changed" label-func #f))
	     adj)))
       sliders)))
  
  (define (activate-dialog w)
    (gtk_widget_show w)
    (gtk_window_present (GTK_WINDOW w)))
  
  )
