/* PointEditor.c */
/* Paul Boersma, March 8, 2002 */

#include "PointEditor.h"
#include "PointProcess_and_Sound.h"
#include "EditorM.h"

#define PointEditor_members FunctionEditor_members \
	int ownSound; \
	Widget addPointAtDialog;
#define PointEditor_methods FunctionEditor_methods
class_create_opaque (PointEditor, FunctionEditor)

static void updateMenus (PointEditor me) {
	Editor_setMenuSensitive (me, "Selection", my endSelection > my startSelection);
}

/********** DESTRUCTION **********/

static void destroy (I) {
	iam (PointEditor);
	if (my ownSound) forget (my sound);
	inherited (PointEditor) destroy (me);
}

/********** MENU COMMANDS **********/

DIRECT (PointEditor, cb_getJitter)
	if (my startSelection == my endSelection) return Melder_error ("Make a selection first.");
	Melder_informationReal (PointProcess_getJitter (my data, my startSelection, my endSelection, 1e-4, 0.02), NULL);
END

DIRECT (PointEditor, cb_getJitter_vj)
	if (my startSelection == my endSelection) return Melder_error ("Make a selection first.");
	Melder_informationReal (PointProcess_getJitter_vj (my data, my startSelection, my endSelection, 1e-4, 0.02), NULL);
END

DIRECT (PointEditor, cb_getJitter_jr)
	if (my startSelection == my endSelection) return Melder_error ("Make a selection first.");
	Melder_informationReal (PointProcess_getJitter_jr (my data, my startSelection, my endSelection, 1e-4, 0.02), NULL);
END

DIRECT (PointEditor, cb_getJitter_rap)
	if (my startSelection == my endSelection) return Melder_error ("Make a selection first.");
	Melder_informationReal (PointProcess_getJitter_rap (my data, my startSelection, my endSelection, 1e-4, 0.02), NULL);
END

DIRECT (PointEditor, cb_getJitter_pvi)
	if (my startSelection == my endSelection) return Melder_error ("Make a selection first.");
	Melder_informationReal (PointProcess_getJitter_pvi (my data, my startSelection, my endSelection, 1e-4, 0.02), NULL);
END

DIRECT (PointEditor, cb_getShimmer)
	if (my startSelection == my endSelection) return Melder_error ("Make a selection first.");
	Melder_informationReal (PointProcess_Sound_getShimmer (my data, my sound.data, my startSelection, my endSelection, 1e-4, 0.02), NULL);
END

DIRECT (PointEditor, cb_getShimmer_vs)
	if (my startSelection == my endSelection) return Melder_error ("Make a selection first.");
	Melder_informationReal (PointProcess_Sound_getShimmer_vs (my data, my sound.data, my startSelection, my endSelection, 1e-4, 0.02), NULL);
END

DIRECT (PointEditor, cb_getShimmer_apq)
	if (my startSelection == my endSelection) return Melder_error ("Make a selection first.");
	Melder_informationReal (PointProcess_Sound_getShimmer_apq (my data, my sound.data, my startSelection, my endSelection, 1e-4, 0.02), NULL);
END

DIRECT (PointEditor, cb_removePoints)
	Editor_save (me, "Remove point(s)");
	if (my startSelection == my endSelection)
		PointProcess_removePointNear (my data, my startSelection);
	else
		PointProcess_removePointsBetween (my data, my startSelection, my endSelection);
	FunctionEditor_redraw (me);
	Editor_broadcastChange (me);
END

DIRECT (PointEditor, cb_addPointAtCursor)
	Editor_save (me, "Add point");
	PointProcess_addPoint (my data, 0.5 * (my startSelection + my endSelection));
	FunctionEditor_redraw (me);
	Editor_broadcastChange (me);
END

FORM (PointEditor, cb_addPointAt, "Add point", 0);
	REAL ("Position", "0.0");
	OK
SET_REAL ("Position", 0.5 * (my startSelection + my endSelection));
DO
	Editor_save (me, "Add point");
	PointProcess_addPoint (my data, GET_REAL ("Position"));
	FunctionEditor_redraw (me);
	Editor_broadcastChange (me);
END

DIRECT (PointEditor, cb_PointEditorHelp) Melder_help ("PointEditor"); END

static void createMenus (I) {
	iam (PointEditor);
	inherited (PointEditor) createMenus (me);

	Editor_addCommand (me, "Query", "-- query jitter --", 0, NULL);
	Editor_addCommand (me, "Query", "Get jitter", 0, cb_getJitter);
	Editor_addCommand (me, "Query", "Get jitter (vj)", 0, cb_getJitter_vj);
	Editor_addCommand (me, "Query", "Get jitter (jr)", 0, cb_getJitter_jr);
	Editor_addCommand (me, "Query", "Get jitter (rap)", 0, cb_getJitter_rap);
	Editor_addCommand (me, "Query", "Get jitter (pvi)", 0, cb_getJitter_pvi);
	if (my sound.data) {
		Editor_addCommand (me, "Query", "-- query shimmer --", 0, NULL);
		Editor_addCommand (me, "Query", "Get shimmer", 0, cb_getShimmer);
		Editor_addCommand (me, "Query", "Get shimmer (vs)", 0, cb_getShimmer_vs);
		Editor_addCommand (me, "Query", "Get shimmer (apq)", 0, cb_getShimmer_apq);
	}

	Editor_addMenu (me, "Point", 0);
	Editor_addCommand (me, "Point", "Add point at cursor", 'P', cb_addPointAtCursor);
	Editor_addCommand (me, "Point", "Add point at...", 0, cb_addPointAt);
	Editor_addCommand (me, "Point", "-- remove point --", 0, NULL);
	Editor_addCommand (me, "Point", "Remove point(s)", motif_OPTION + 'P', cb_removePoints);

	Editor_addCommand (me, "Help", "PointEditor help", '?', cb_PointEditorHelp);
}

/********** DRAWING AREA **********/

static void draw (I) {
	iam (PointEditor);
	PointProcess point = my data;
	Sound sound = my sound.data;
	long first, last, i;
	Graphics_setWindow (my graphics, my startWindow, my endWindow, -1.0, 1.0);
	if (sound != NULL && Sampled_getWindowSamples (sound, my startWindow, my endWindow, & first, & last) > 1) {
		Graphics_setLineType (my graphics, Graphics_DOTTED);
		Graphics_line (my graphics, my startWindow, 0.0, my endWindow, 0.0);
		Graphics_setLineType (my graphics, Graphics_DRAWN);      
		Graphics_function (my graphics, sound -> z [1], first, last,
			Sampled_indexToX (sound, first), Sampled_indexToX (sound, last));
	}
	Graphics_setColour (my graphics, Graphics_BLUE);
	for (i = 1; i <= point -> nt; i ++) {
		double t = point -> t [i];
		if (t >= my startWindow && t <= my endWindow)
			Graphics_line (my graphics, t, -0.9, t, 0.9);
	}
	Graphics_setColour (my graphics, Graphics_BLACK);
	updateMenus (me);
}

static void play (I, double tmin, double tmax) {
	iam (PointEditor);
	if (my sound.data) {
		Sound_playPart (my sound.data, tmin, tmax, our playCallback, me);
	} else {
		if (! PointProcess_playPart (my data, tmin, tmax)) Melder_flushError (NULL);
	}
}

class_methods (PointEditor, FunctionEditor)
	class_method (destroy)
	class_method (createMenus)
	class_method (draw)
	class_method (play)
class_methods_end

PointEditor PointEditor_create (Widget parent, const char *title, PointProcess point, Sound sound, int ownSound) {
	PointEditor me = new (PointEditor); cherror
	my ownSound = ownSound;
	if (sound && ownSound) {
		my sound.data = Data_copy (sound); cherror   /* Deep copy; ownership transferred. */
	} else {
		my sound.data = sound;   /* Reference copy; ownership not transferred. */
	}
	FunctionEditor_init (me, parent, title, point); cherror
	updateMenus (me);
end:
	iferror forget (me);
	return me;
}

/* End of file PointEditor.c */
