module('apps.CodeUpdate').requires('lively.TestFramework').toRun(function() {

TestCase.subclass('LinkRepairTest',
'default category', {
	doc1: function() {
		return stringToXML(
'<defs xmlns:xlink="http://www.w3.org/1999/xlink">' +
	'<script type="text/ecmascript" xlink:href="JSON.js"/>' +
	'<script type="text/ecmascript" xlink:href="../../lib/Core.js"/>' +
	'<script type="text/ecmascript" xlink:href="../lib/localconfig.js"/>' +
	'<script type="text/ecmascript" xlink:href="Main.js"/>' +
	'<script type="text/ecmascript" xlink:href="Scratch.js"/>' +
'</defs>');
},
	doc2: function() {
		return stringToXML(
'<defs xmlns:xlink="http://www.w3.org/1999/xlink">' +
	'<script type="text/ecmascript" xlink:href="JSON.js"/>' +
	'<script type="text/ecmascript" xlink:href="../../lib/Core.js"/>' +
	'<script type="text/ecmascript" xlink:href="../lib/localconfig.js"/>' +
	'<script type="text/ecmascript" xlink:href="../config.js"/>' +
	'<script type="text/ecmascript" xlink:href="config.js"/>' +
	'<script type="text/ecmascript" xlink:href="Main.js"/>' +
	'<script type="text/ecmascript" xlink:href="Scratch.js"/>' +
'</defs>');
},
	test01FixScriptUrl: function() {
	var doc = this.doc1();
	var sut = new LinkRepairer(doc);
	var newPrefix = '../lively/';
	sut.fixLinks(newPrefix);
	sut.getScripts().forEach(function(elem) {
		this.assert(XLinkNS.getHref(elem).startsWith(newPrefix), 'did not fix links');
	}, this)
},
	test02AddConfig: function() {
	var doc = this.doc1();
	var sut = new LinkRepairer(doc);
	var newPrefix = '../../lively/';
	sut.addConfigJs(newPrefix);
	var localconfigScript = sut.getScripts().detect(function(elem) { 
		return XLinkNS.getHref(elem).endsWith('localconfig.js');
	});
	var next = localconfigScript.nextSibling;
	next = next.nextSibling; // jump over nl
	this.assertEqual('../../config.js', next.getAttribute('xlink:href'));
	next = next.nextSibling;
	next = next.nextSibling; // jump over nl

	this.assertEqual('../config.js', next.getAttribute('xlink:href'));
	next = next.nextSibling;
	next = next.nextSibling; // jump over nl
	this.assertEqual('config.js', next.getAttribute('xlink:href'));
},
	test03AddConfigAndEnsureNoDuplicates: function() {
	var doc = this.doc2();
	var sut = new LinkRepairer(doc);
	var newPrefix = '../../lively/';
	sut.addConfigJs(newPrefix);
	var result = sut.getScripts().select(function(elem) {
		var href = elem.getAttribute('xlink:href')
		return href.endsWith('config.js') && !href.endsWith('localconfig.js') && !href.endsWith('defaultconfig.js')
	});
	this.assertEqual(3, result.length, 'wrong number of config.js links');
},
});

Object.subclass('LinkRepairer',
'default category', {
	initialize: function(document) {
	this.doc = document;
},
	getDoc: function() { return this.doc },
	getScripts: function() { return $A(this.getDoc().getElementsByTagName('script')) },
	fixLinks: function(newPrefix) {
	if (!newPrefix) newPrefix = '';
	if (newPrefix.length > 0 && !newPrefix.endsWith('/')) newPrefix += '/';
	var scripts = this.getDoc().getElementsByTagName('script');
	$A(scripts).forEach(function(script) {
		var link = XLinkNS.getHref(script);
		var file = link.substring(link.lastIndexOf('/') + 1, link.length); // ../lib/Core.js --> Core.js
		XLinkNS.setHref(script, newPrefix + file);
	});
},
	addConfigJs: function(newPrefix) {
	// count the level ups and add a link to config.js for each level
	var dirUp = '..', configJs = 'config.js', localconfigJs = 'localconfig.js';

	// remove old config.js links
	var linksToRemove = this.getScripts().forEach(function(elem) {
		var href = XLinkNS.getHref(elem);
		if (!href.endsWith(configJs)  || href.endsWith(localconfigJs) || href.endsWith('defaultconfig.js'))
			return;
		elem.parentElement.removeChild(elem);
	});

	// add new config.js links
	var addAfter = this.getScripts().detect(function(e) { return XLinkNS.getHref(e).endsWith(localconfigJs) });
	var addBefore = addAfter.nextSibling;

	var levels = newPrefix.split('/').select(function(pathPart) { return pathPart == dirUp }).length;
	for (var i = levels; i >= 0; i--) {
		var dirUps = Array.range(1, i).collect(function() { return dirUp }).join('/');
		var href = (dirUps ? dirUps + '/' : '') + configJs;
		var newLink =  XLinkNS.create(href, this.getDoc());
		var nl = this.getDoc().createTextNode ?
			this.getDoc().createTextNode("\n") : 
			this.getDoc().ownerDocument.createTextNode("\n")
		addBefore.parentElement.insertBefore(nl, addBefore);
		addBefore.parentElement.insertBefore(newLink, addBefore);
	}

},
	fixAll: function(newPrefix) {
	this.fixLinks(newPrefix);
	this.addConfigJs(newPrefix);
	return this.getDoc();
},
});


Object.subclass('CodeUpdate',
'default category', {
	initialize: function(from, to, dryRun) {
		this.from = from;
		this.to = to;
		this.dryRun = dryRun;
	},
	updateCode: function() {
		var fromURL = this.from;
		var toURL = this.to;

		var matchFunc = eval('(' + $morph('copyRepoFilesMatching').textString + ')')
		var files = new WebResource(fromURL).forceUncached().getSubElements().subDocuments.select(function(ea) { 
			return matchFunc(ea.getURL().toString()) });
		var browser = this.browser;

		var logText = $morph('textLog');
		logText.setTextString("= Update All From Repository =");
		var log = function(aString) {
			logText.setTextString(logText.textString + "\n" + aString )

		}
		log("source: " +fromURL)
		log("dest: " +toURL)
		if (this.dryRun)
			log("dry run");
		files.forEachShowingProgress(
			$morph('progress'), 
			function(ea) { 
				var filename =  ea.getURL().filename();
				var oldContentURL = new URL(toURL).withFilename(filename);
				oldContentURL  = oldContentURL.withRelativePartsResolved()
				var oldContent = new WebResource(oldContentURL).forceUncached().get().content;
				var newContent = ea.forceUncached().get().content;
				if (oldContent != newContent) {
					log("update " + filename)
					log("url: " + oldContentURL)
					if (!this.dryRun) {
						log("put new content " + oldContent.length + " -> " + newContent.length + "bytes")			
						new WebResource(toURL + ea.getURL().filename()).put(newContent, 'text/javascript')
					}
				}
			}.bind(this),
			function(ea) { return "copied " + ea.getURL().filename()},
			function(ea) { $morph('progress').setLabel('finished')}	
		)
	},

	updateCodeAndXHTML: function() {
		var fromURL = this.from;
		var toURL = this.to;

		var matchFunc = function(filename) {
			return !filename.startsWith('index') &&
				!filename.startsWith('.') && 		
				!filename.endsWith('localconfig.js');
		};
		var files = new WebResource(fromURL).getSubElements().subDocuments.select(function(ea) { 
			return matchFunc(ea.getURL().toString()) });
		var browser = this.browser;

		var logText = $morph('textLog');
		logText.setTextString("= Update All From Repository =");
		var log = function(aString) {
			logText.setTextString(logText.textString + "\n" + aString )

		}
		log("source: " +fromURL)
		log("dest: " +toURL)
		if (this.dryRun)
			log("dry run");
		files.forEachShowingProgress(
			$morph('progress'), 
			function(ea) { 
				var filename =  ea.getURL().filename();
				var oldContent = new WebResource(toURL + filename).forceUncached().get().content;
				var newContent = ea.get().content;
				if (oldContent != newContent) {
					log("update " + filename)
					if (!this.dryRun)
						new WebResource(toURL + ea.getURL().filename()).put(newContent, 'text/javascript')
				}
			}.bind(this),
			function(ea) { return "copied " + ea.getURL().filename()},
			function(ea) { $morph('progress').setLabel('finished')}	
		)
	},

});


BoxMorph.subclass('CheckBoxMorph',
'default category', {
	defaultExtent: pt(25,25),
	handlesMouseDown: Functions.True,
	suppressHandles: true,
	initialize: function($super, loc) {
	$super(loc.extent(this.defaultExtent));
	this.applyStyle({fill: Color.gray.lighter(), borderWidth: 1, borderColor: Color.black});
	this.state = false;
	this.buildLabel();
	this.updateLabel();
},
	buildLabel: function() {
	this.label = this.addMorph(new TextMorph(new Rectangle(0,0, 30, 10)))
	this.label.applyStyle({fill: null, borderWidth: 0, fontSize: 18})
	this.label.emphasizeAll({align: 'center'})
	this.label.ignoreEvents()
},
	onMouseDown: function(evt) { this.toggleState() },
	onMouseMove: function(evt) { },
	toggleState: function() {
	this.state = !this.state;
	this.updateLabel();
},
	updateLabel: function() {
	this.label.setTextString(this.state ? 'X' : 'O');
	this.label.align(this.label.getCenter().addPt(pt(1,1)), this.shape.bounds().center())
},
	okToBeGrabbedBy: function(evt) { return null },
});


}) // end of module