/**
MONOPOLE LOOPS IN QED:
jrh, adc, kv, cg
in SBEII
**/

// read stored data and prepare.
(
	(
	q = ();
	q.curDir = Document.current.dir;
	q.paths = ["monopoles/monopoles_0.000.dat", "monopoles/monopoles_0.050.dat", "monopoles/monopoles_0.100.dat", "monopoles/monopoles_0.150.dat", "monopoles/monopoles_0.200.dat", "monopoles/monopoles_0.250.dat", "monopoles/monopoles_0.300.dat", "monopoles/monopoles_0.350.dat", "monopoles/monopoles_0.400.dat", "monopoles/monopoles_0.450.dat", "monopoles/monopoles_0.500.dat", "monopoles/monopoles_0.550.dat", "monopoles/monopoles_0.600.dat", "monopoles/monopoles_0.650.dat", "monopoles/monopoles_0.700.dat", "monopoles/monopoles_0.750.dat", "monopoles/monopoles_0.800.dat", "monopoles/monopoles_0.850.dat", "monopoles/monopoles_0.900.dat", "monopoles/monopoles_0.950.dat", "monopoles/monopoles_1.000.dat", "monopoles/monopoles_1.050.dat", "monopoles/monopoles_1.100.dat", "monopoles/monopoles_1.150.dat", "monopoles/monopoles_1.200.dat", "monopoles/monopoles_1.250.dat", "monopoles/monopoles_1.300.dat", "monopoles/monopoles_1.350.dat", "monopoles/monopoles_1.400.dat", "monopoles/monopoles_1.450.dat", "monopoles/monopoles_1.500.dat"].collect { |x| q.curDir +/+ x };
	
	q.allData = q.paths.collect { |path| 
		FileReader.read(path, true, true, { |x| x.asInteger })
	};
	// special operation!!
	q.allData = q.allData.collect { |x|
			x.flat.clump(4).clump(1000).collect { |x| x.clump(100).collect { |x| x.clump(10) } }
	};
	q.mono = q.allData.at(5);
	);

	/*//
	q.mono = q.allData.at(4);

	*/
	(
	q.monoAt = { |q, indices| 
		var res = q.mono; 
		indices.do { |i| res = res.wrapAt(i) };
		res
	};
	
	(
	q.neighbors = [
		[1, 0, 0, 0],
		[0, 1, 0, 0],
		[0, 0, 1, 0],
		[0, 0, 0, 1]
	];
	q.neighbors = q.neighbors ++ q.neighbors.neg;
	);
	
	q.allNeighbors = { |q, indices|
			q.neighbors.collect { |x| indices + x };
	};
	
	q.allNeighbors([4, 4, 4, 4]);
	q.allNeighbors([0, 9, 9, 0]);
	q.allNeighbors([0, 9, 9, 0]);
	q.allNeighborsMonoAt = { |q, indices|
		q.allNeighbors(indices).collect { |x| q.monoAt(x) };
	};

	// 				[-2,  -1,      0,    1,    2]
	
	q.monoFreqs = [1477.2, 802.3, 40, 802.3 * 1.01, 1077.2 * 1.01];

	q.eventNeighborsMono = { |q, indices|
			var all = q.allNeighborsMonoAt(indices);
			var event = (
				freq: q.monoFreqs, 
				instrument: \sine, 
				sustain: 0.05 * #[1, 1, 0.04, 1, 1]
			);
			var histo = all.flat.as(Bag).contents;
			var amps = #[-2, -1, 0, 1, 2].collect { |key|
				var num = histo.at(key) ? 0;
				var amp = num.linlin(0, 31, 0, 0.5);
				amp
			};
			amps = amps * [2, 1, 3, 1, 2]; // null im bauch, // vary this...
			event.put(\amp, amps, \pan, #[-1, -1, 0, 1, 1]); // pan according to sign
	};
	
	(
	SynthDef(\sine, { arg out=0, freq=1000, attack=0, sustain=0.1, release=0.05, amp=0.1, iphase=0, pan; 
		var env, u;
		amp = AmpCompA.kr(freq) * amp;
		env = EnvGen.ar(
			Env(
				[0, amp, amp, 0], 
				[attack, sustain, release], 
				[\lin, \lin, \lin]
			),
			doneAction:2);
		u = Pan2.ar(SinOsc.ar(freq, iphase, env), pan);
		OffsetOut.ar(out, u)
	}, [\kr, \kr, \ir, \ir, \ir, \kr, \ir, \kr]).store;
	);

	// for a number of given links, find a next one.
	q.decideLink = { |q, links|
		links[q.dimOrder].detectIndex { |link| link != 0 }.postln
	};
	
	/*	
	q.decideLink = { |q, links|
		links.detectIndex { |link| link != 0 }.postln
	};
	*/
	
	// precedence of dimensions if we find several possibilities
	q.dimOrder = [0, 1, 2, 3];
	
	// for a coordinate, find next coordinate 
	q.nextNeighbor = { |q, indices|
		var mono = q.monoAt(indices);
		var nextIndex = q.decideLink(mono);
		if(nextIndex.isNil) { "loop ended".warn } {
			[0, 0, 0, 0].put(nextIndex, mono.at(nextIndex).sign) + indices
		};
			
	};
	
	q.monoHasLinks = { |q, indices|
		q.monoAt(indices).any(_ != 0);
	};
	
	q.findStartPoint = { |q|
		var addr;
		while {
			addr = { 10.rand } ! 4;
			q.monoHasLinks(addr).not
		};
		addr
	};
	
	)

	
);

// loops:
/*
[ 2, 5, 2, 3 ]

*/

// sonification: for a random point, find all neighbours and play them

(
Tdef(\x, {
	loop {
		q.eventNeighborsMono({ 10.rand } ! 4).play;
		(0.03 + 0.01.rand2 * 1.3).wait;
	}
}).play
)

Tdef(\x).reset;

// compare:
q.mono = q.allData.at(1);
q.mono = q.allData.at(2);
q.mono = q.allData.at(3);
q.mono = q.allData.at(4);
q.mono = q.allData.at(5);
q.mono = q.allData.at(6);

// follow loops
(
Tdef(\x, {
	var point = q.findStartPoint;
	//point = [2, 3, 1, 7];
	while {
		point = q.nextNeighbor(point) ;
		q.monoHasLinks(point)
	} {
		point.postln;
		postln("\t" ++ q.monoAt(point));	
		(0.3 + 0.01.rand2 * 1.3).wait;
	};
	"uups. Loop is no loop: %\n".postf(point);
}).play
);

// ??
(
Tdef(\x, {
	var point = q.findStartPoint;
	loop {
	while {
		point = q.nextNeighbor(point) ;
		q.monoHasLinks(point);
	} {
		point.postln;
		(instrument: \sine, freq: point.linexp(0, 30, 200, 8000), sustain: 0.03, release: 0.01).play;
		postln("\t" ++ q.monoAt(point));	
		(0.3 + 0.01.rand2 * 0.3).wait;
	};
	"uups. Loop is no loop: %\n".postf(point);
	1.wait;
	};
}).play
);

// q.allData.at(8).flat.plot2
	
	///////////////////
	/*
	
	q.monoAt([1000, 2001, 20391, 32787667]);
	q.monoAt([1000, 2001, 3007, 4009]);
	q.monoAt([0, 1, 7, 9]);

	q.eventNeighborsMono([4, 4, 5, 6])
	
	[ -2, 0, -1, -2 ].abs.as(Bag).contents
	
	q.allNeighborsMonoAt([4, 4, 4, 4])	

	];
	);

	q.monoAt([0, 3, 4, 5]);
	q.monoHasLinks(q.findStartPoint);

	q.monoAt([1, 1, 1, 1])
	q.nextNeighbor([1, 1, 1, 1])
	q.decideLink([ 1, 0, -1, 0 ]);
	q.decideLink([ 0, 0, -1, 0 ]);

	(instrument: \sine, freq: 800, dur: 0.02).play;
	{ PinkNoise.ar(0.2 ! 2) }.play;	
	
	q.eventNeighborsMono([5, 5, 5, 5]).play;
		
	*/