(
var clockStringFunc, metronomeStringFunc, metronomeColorFunc, updateTransport, updateSection, updateSubsection,
buildGenerator, buildMetronome, buildTransport, buildTempoControl, buildMasterFader, buildTrackFader,
buildMasterView, buildFaderView, buildHelpView, currentSection = 1, currentSubsection = 1;

// these funcs update the elements of the transport panel
clockStringFunc = {
	arg measure, beat;
	var measureString, beatString, leadSpace;
	measureString = measure.asInteger.asString;
	beatString = beat.asInteger.asString;
	leadSpace = (3 - measureString.size).collect({" "}).join;
	leadSpace ++ measureString ++ "." ++ beatString
};

// [-30, -105, -104] and [-30, -105, -113] are unicode inverse bullet and normal bullet, respectively
metronomeStringFunc = { arg beat; if(beat == 1,
	{[-30, -105, -104].collect({arg int; int.asAscii}).as(String)},
	{[-30, -105, -113].collect({arg int; int.asAscii}).as(String)})};
metronomeColorFunc = { arg beat; if(beat == 1, {Color.red},{Color.black})};

updateTransport = {arg clock, metronome, sectionDisplay, measure, beat, section, subsection;
	sectionDisplay.string = "section: " ++ section.asInteger ++ "." ++ subsection.asInteger;
	clock.string = clockStringFunc.value(measure, beat);
	metronome.stringColor = metronomeColorFunc.value(beat);
	metronome.string = metronomeStringFunc.value(beat);
	{0.75.wait; {metronome.string = ""}.defer}.fork(~tempoClock, quant: 0);
}.inEnvir;

buildGenerator = {arg view;
	var ranSeed;
	HLayout(
		ranSeed = TextField(view).string_("19800725"),
		Button(view).states_([["reset seed"]]).action_({ ranSeed.string = "19800725"}.inEnvir),
		Button(view).states_([["random seed"]]).action_({ ranSeed.string = 50000000.rand.asString}.inEnvir),
		Button(view).states_([["generate"]]).action_({
			{~genAll.value(ranSeed.string.asInteger); ~appStatus.string = "status: ready"}.fork(AppClock);
			~appStatus.string = "status: generating"}.inEnvir),
		[~appStatus = StaticText(view).string_("status: ready"), stretch: 1],
		Button(view).states_([["transcribe"]]).action_({
			{~transcribe.value(~scoreData, ~sectionData, ranSeed.value); ~appStatus.string = "status: ready"}.fork(AppClock);
			~appStatus.string = "status: transcribing"}.inEnvir),
		Button(view).states_([["bounce audio"]]).action_({
			{~bounceAudio.value(ranSeed.value); ~appStatus.string = "status: ready"}.fork(AppClock);
			~appStatus.string = "status: bouncing audio"}.inEnvir),
		nil)
};

buildMetronome = {arg win;
	var clock, metronome, layout;

	clock = StaticText(win).string_("  1.1").font_(Font("Liberation Mono", 200));
	metronome = StaticText(win).string_([-30, -105, -104].collect({arg int; int.asAscii})
			.as(String)).font_(Font("Liberation Mono", 300)).stringColor_(Color.red);

	layout = HLayout(
		clock,
		StaticText(win).string_("|").font_(Font("Liberation Mono", 200)),
		metronome
	);

	[clock, metronome, layout]
};

updateSection = {arg mod, clock, metronome, sectionDisplay, refresh = true, indirect = false;
	var changeSection;
	case
	{(currentSubsection > 1) && (mod < 0)} {
		currentSubsection = 1;
	}
	{(currentSubsection <= 1) && (mod < 0) && (currentSection > 1)} {
		currentSection = currentSection + mod;
		if(indirect, {
			currentSubsection = ~sectionNavDict[[currentSection, 1]][1]
		}, {
			currentSubsection = 1;
		})
	}
	{(mod > 0) && (~sectionNavDict[[currentSection + mod, 1]] != nil)} {
		currentSection = currentSection + mod;
		currentSubsection = 1;
	};

	if(refresh, {
		updateTransport.value(clock, metronome, sectionDisplay,
			~sectionNavDict[[currentSection, currentSubsection]][0], 1,
			currentSection, currentSubsection
		);
	});
};

updateSubsection = {arg mod, clock, metronome, sectionDisplay, refresh = true;
	if(~sectionNavDict[[currentSection, currentSubsection + mod]] != nil, {
		currentSubsection = currentSubsection + mod;
		if(refresh, {
			updateTransport.value(clock, metronome, sectionDisplay,
				~sectionNavDict[[currentSection, currentSubsection]][0], 1,
				currentSection, currentSubsection
			);
		});
	}, {
		updateSection.value(mod, clock, metronome, sectionDisplay, refresh, true)
	})
};

buildTransport = {arg win, view, clock, metronome, preampBusses, accompBusses, postampBusses;
	var sec, subsec, sectionDisplay, layout, player;

	sectionDisplay = StaticText(win).string_("section: 1.1").font_(Font("Liberation Mono", 70));

	OSCFunc({ arg msg, time;
		{
			var measure, beat, section, subsection;
			# measure, beat, section, subsection = msg[3..];
			currentSection = sec = section.asInteger;
			currentSubsection = subsec = subsection.asInteger;
			updateTransport.value(clock, metronome, sectionDisplay, measure, beat, section, subsection);
		}.inEnvir.defer;
	},'/measureClock_' ++ ~hash, s.addr);

	layout = HLayout(
		Button(view).states_([["<<", Color.black]]).action_({arg pState; updateSection.value(-1, clock, metronome, sectionDisplay)}.inEnvir),
		Button(view).states_([["<", Color.black]]).action_({arg pState; updateSubsection.value(-1, clock, metronome, sectionDisplay)}.inEnvir),
		Button(view).states_([["play", Color.black], ["stop", Color.black, Color.grey]]).action_({arg pState;
			if(pState.value == 1, {
				player = {
					var startMeasure = ~sectionNavDict[[currentSection, currentSubsection]][0] - 1;
					~patternProxy.source = ~genPlayablePatterns.value(startMeasure, ~patterns, preampBusses, accompBusses, postampBusses);
					Pbind(\instrument, \click_ ++ ~hash, \beat, Pseq([1, 2, 1, 2]), \dur, 1).play(~tempoClock, quant: 0);
					[1, 2, 1, 2].do({arg beat;
						{
							metronome.stringColor = metronomeColorFunc.value(beat);
							metronome.string = metronomeStringFunc.value(beat);
						}.defer;
						0.75.wait;
						{metronome.string = ""}.defer;
						0.25.wait;
					});
					~patternProxy.play(~tempoClock, quant: 0)
				}.fork(~tempoClock, quant: 0)
			}, {
				~patternProxy.pause;
				//player.stop;
				updateTransport.value(clock, metronome, sectionDisplay,
					~sectionNavDict[[currentSection, currentSubsection]][0], 1,
					currentSection, currentSubsection);
			});
		}.inEnvir),
		Button(view).states_([[">", Color.black]]).action_({arg pState; updateSubsection.value(1, clock, metronome, sectionDisplay)}.inEnvir),
		Button(view).states_([[">>", Color.black]]).action_({arg pState; updateSection.value(1, clock, metronome, sectionDisplay)}.inEnvir), nil,
		sectionDisplay, nil);
	[sectionDisplay, layout]
};

buildTempoControl = {arg view;
	var layout, tempoField, address, updateSection;
	layout = HLayout(
		tempoField = TextField(view).string_("60").action_({arg v;
			var tempo = v.value.asInteger; ~tempoClock.tempo = tempo / 60}.inEnvir),
		Button(view).states_([["set tempo"]]).action_({arg v; ~tempoClock.tempo = tempoField.string.asInteger / 60}.inEnvir),
		[StaticText(view).string_("  "), stretch: 1]);
	[layout, tempoField]
};

buildMasterFader = {arg view;
	var trackIndicators, layout, volSlider, muteButton, outMenu;

	trackIndicators = {LevelIndicator()} ! 2;

	OSCFunc.new({arg msg;
		{trackIndicators[0].value = msg[3].ampdb.linlin(-50, 0, 0, 1)}.defer;
		{trackIndicators[1].value = msg[4].ampdb.linlin(-50, 0, 0, 1)}.defer
	}, '/masterLevels_' ++ ~hash, s.addr);

	layout = HLayout([
		VLayout(
			HLayout(
				volSlider = Slider(view).value_(0.8).action_(
					{arg v; var masterVol = v.value * 1.25; ~play.set(\masterVol, masterVol)}.inEnvir),
				trackIndicators[0],
				trackIndicators[1]),
			muteButton = Button(view).states_([["mute", Color.black], ["mute", Color.black, Color.grey]]).action_(
				{arg v; var masterMute = (1 - v.value).abs; ~play.set(\masterMute, masterMute)}.inEnvir),
			StaticText(view).string_("out").align_(\center),
			outMenu = PopUpMenu(view).items_((1..16).collect({arg o; o + "-" + (o + 1)})).action_(
				{arg v; var out = v.value.postln; ~play.set(\masterOut, out)}.inEnvir),
			StaticText(view).string_("master").align_(\center)
	), stretch: 2], nil);
	[layout, volSlider, muteButton, outMenu]
};

buildTrackFader = {arg view, name, index;
	var trackIndicator, netAddr, layout, volSlider, soloButton, muteButton, panKnob, outMenu;

	netAddr = NetAddr("127.0.0.1", NetAddr.langPort);
	trackIndicator = LevelIndicator();

	OSCFunc.new({arg msg; {trackIndicator.value = msg[3].ampdb.linlin(-50, 0, 0, 1)}.defer},
		'/trackLevel_' ++ index ++ "_" ++ ~hash, s.addr);

	layout = HLayout(
		VLayout(
			HLayout(
				volSlider = Slider(view).value_(0.8).action_(
					{arg v; var vol = v.value * 1.25; ~play.set(\vol_ ++ index, vol)}.inEnvir),
				trackIndicator),
			soloButton = Button(view).states_([["solo", Color.black], ["solo", Color.black, Color.grey]]).action_(
				{netAddr.sendMsg("/soloer_" ++ ~hash, index)}.inEnvir).value_(0),
			muteButton = Button(view).states_([["mute", Color.black], ["mute", Color.black, Color.grey]]).action_(
				{arg v; var mute = (1 - v.value).abs;
					~play.set(\mute_ ++ index, mute)}.inEnvir).valueAction_(if(index < 4, {1}, {0})),
			VLayout(
				StaticText(view).string_("pan").align_(\center),
				panKnob = Knob(view).action_({arg v; var pan = v.value * 2 - 1; ~play.set(\pan_ ++ index, pan)}.inEnvir).valueAction_(0.5)
			),
			StaticText(view).string_("out").align_(\center),
			outMenu = PopUpMenu(view).items_(["master"] ++ (1..16)).action_(
				{arg v; var out = v.value; ~play.set(\out_ ++ index, out)}.inEnvir).valueAction_(if(index < 6, {0}, {3})),
			StaticText(view).string_(name).align_(\center)
			//StaticText(view).string_("output").align_(\center),
		),
		nil);
	[layout, volSlider, soloButton, muteButton, panKnob, outMenu]
};

buildMasterView = {arg win, preampBusses, accompBusses, postampBusses;
	var view, generatorLayout, clock, metronome, metronomeLayout, transportLayout,
	tempoContol, auxControlsLayout, countOff, ranSeed, order, tempo, sectionDisplay, address;

	view = View(win);
	generatorLayout = buildGenerator.value(view);
	# clock, metronome, metronomeLayout = buildMetronome.value(win);
	# sectionDisplay, transportLayout = buildTransport.value(win, view, clock, metronome, preampBusses, accompBusses, postampBusses);
	tempoContol = buildTempoControl.value(view);
	auxControlsLayout = tempoContol[0];

	view.layout_(
		HLayout(
			[
				VLayout(
					metronomeLayout,
					[StaticText(view).string_("  "), stretch: 1],
					transportLayout,
					[StaticText(view).string_("  "), stretch: 1],
					auxControlsLayout,
					[StaticText(view).string_("  "), stretch: 1],
					generatorLayout),
				alignment: \top
			]
		)
	);
	[view, tempoContol[1]]
};

buildFaderView = {arg win, tempoField;
	var view, masterIndicators, trackIndicators, master, tracks, openButton, basePath, saveButton;
	var partAbbr = ["*", "III", "II", "I", "accomp_II", "accomp_I", "click"];
	var trackNames = ["*", "III", "II", "I", "accomp_II", "accomp_I", "click"];
	var partVols, partMutes, partPans;
	var masterMute, masterVol;
	var netAddr = NetAddr("127.0.0.1", NetAddr.langPort);
	var player = ~play;

	// set initial mixer values
	partVols = [1, 1, 1, 1, 1, 1];
	partMutes = [0, 1, 1, 1, 1, 0];
	partPans = [0, 0, 0, 0, 0, 0];
	masterMute = 1;
	masterVol = 1;

	view = View(win);
	masterIndicators = {LevelIndicator()} ! 2;
	trackIndicators = {LevelIndicator()} ! 6;

	master = buildMasterFader.value(view);
	tracks = {arg part;
		buildTrackFader.value(view, trackNames[part], part);
	} ! 7;

	OSCFunc.new({arg msg; {
		tracks.slice(nil, 3).do({arg mute, m;
			if(tracks[msg[1]][2].value == 1, {
				mute.valueAction = if(msg[1] == m, {0}, {1});
				tracks[m][2].value = if(msg[1] != m, {0}, {1})
			}, {
				mute.valueAction = 0
			});
		});
	}.defer}, '/soloer_' ++ ~hash, netAddr);

	basePath = ~dir +/+ ".." +/+ "mixer_settings";

	openButton = Button(view).states_([["open", Color.black]]).action_({
		Dialog.openPanel({ arg path;
			var settings;
			settings = File.readAllString(path).parseJSON;
			tempoField.valueAction = settings["tempo"];
			master[1].valueAction = settings["master_volume"];
			master[2].valueAction = settings["master_pan"];
			master[3].valueAction = settings["master_out"];
			settings["track_volumes"].do({arg val, v; tracks[v][1].valueAction = val});
			settings["track_solos"].do({arg val, v; tracks[v][2].valueAction = val});
			settings["track_mutes"].do({arg val, v; tracks[v][3].valueAction = val});
			settings["track_pans"].do({arg val, v; tracks[v][4].valueAction = val});
			settings["track_outs"].do({arg val, v; tracks[v][5].valueAction = val});
		},{}, false, basePath);
	});

	saveButton = Button(view).states_([["save", Color.black]]).action_({
		Dialog.savePanel({ arg path;
			var settings, file;
			settings = "{\n";
			settings = settings ++ "\"tempo\": " ++ tempoField.string ++ ",\n";
			settings = settings ++ "\"master_volume\": " ++ master[1].value ++ ",\n";
			settings = settings ++ "\"master_mute\": " ++ master[2].value ++ ",\n";
			settings = settings ++ "\"master_out\": " ++ master[3].value ++ ",\n";
			settings = settings ++ "\"track_volumes\": [" ++ tracks.collect({arg track; track[1].value}).join(",") ++ "],\n";
			settings = settings ++ "\"track_solos\": [" ++ tracks.collect({arg track; track[2].value}).join(",") ++ "],\n";
			settings = settings ++ "\"track_mutes\": [" ++ tracks.collect({arg track; track[3].value}).join(",") ++ "],\n";
			settings = settings ++ "\"track_pans\": [" ++ tracks.collect({arg track; track[4].value}).join(",") ++ "],\n";
			settings = settings ++ "\"track_outs\": [" ++ tracks.collect({arg track; track[5].value}).join(",") ++ "]\n";
			settings = settings ++ "}";
			file = File(path, "w");
			file.write(settings);
			file.close;
		},{}, basePath);
	});

	view.layout_(HLayout(HLayout(master[0], nil, *tracks.slice(nil, 0)), VLayout(nil, saveButton, openButton)))
};

buildHelpView = {arg win;
	TextView(win).string_(File.readAllString(~dir +/+ "tkam_readme.scd")).editable_(false);
};

~generateGUI = {arg preampBusses, accompBusses, postampBusses;
	var win, tabButtonReset, transportButton, mixerButton, helpButton, masterConrol, tempoControl, masterView, faderView, helpView, tabs;
	win = Window("to kill a monarch", Rect(500, 500, 1100, 575), false).front;
	tabButtonReset = {transportButton.value = 1; mixerButton.value = 1; helpButton.value = 1};
	masterConrol = buildMasterView.value(win, preampBusses, accompBusses, postampBusses);
	masterView = masterConrol[0];
	tempoControl = masterConrol[1];
	faderView = buildFaderView.value(win, tempoControl);
	helpView = buildHelpView.value(win);

	win.layout = VLayout(
		HLayout(
			HLayout(
				[
					transportButton = Button().states_([["transport", Color.white, Color.grey], ["transport", Color.black]]).action_(
						{tabButtonReset.value; transportButton.value = 0; tabs.index = 0 }.inEnvir).value_(0), stretch: 1
				], [
					mixerButton = Button().states_([["mixer", Color.white, Color.grey], ["mixer", Color.black]]).action_(
						{tabButtonReset.value; mixerButton.value = 0; tabs.index = 1 }.inEnvir).value_(1), stretch: 1
				]
			),
			helpButton = Button().states_([["help", Color.white, Color.grey], ["help", Color.black]]).action_(
				{tabButtonReset.value; helpButton.value = 0; tabs.index = 2 }.inEnvir).value_(1)
		),
		tabs = StackLayout(masterView, faderView, helpView));
};
)
