﻿function Parser(selector, startingKey) {
	this.Selector = selector;
	this.KeyIndex = this.GetKeyIndex(startingKey);
};

Parser.prototype = {
	Keys: ['Ab', 'A', 'Bb', 'B', 'C', 'C#', 'D', 'Eb', 'E', 'F', 'F#', 'G'],

	ChordRegex: /^[A-G][b\#]?(2|5|6|7|9|11|13|6\/9|7\-5|7\-9|7\#5|7\#9|7\+5|7\+9|7b5|7b9|7sus2|7sus4|add2|add4|add9|aug|dim|dim7|m\/maj7|m6|m7|m7b5|m9|m11|m13|maj7|maj9|maj11|maj13|mb5|m|sus|sus2|sus4)*(\/[A-G][b\#]*)*$/,

	ChordReplaceRegex: /([A-G][b\#]?(2|5|6|7|9|11|13|6\/9|7\-5|7\-9|7\#5|7\#9|7\+5|7\+9|7b5|7b9|7sus2|7sus4|add2|add4|add9|aug|dim|dim7|m\/maj7|m6|m7|m7b5|m9|m11|m13|maj7|maj9|maj11|maj13|mb5|m|sus|sus2|sus4)*)/g,

	KeyIndex: null,

	IsChordLine: function(input) {

		// Split the SongLine text into an array
		var tokens = input.replace(/\s+/, " ").split(" ");

		// Try to find tokens that aren't chords
		// if we find one we know that this line
		// is not a 'chord' line.
		for (var i = 0; i < tokens.length; i++) {
			if (!$.trim(tokens[i]).length == 0 && !tokens[i].match(this.ChordRegex))
				return false;
		}
		return true;
	},

	IsSongPartLabel: function(input) {
		return input.match(/^(intro|verse|pre-?chorus|chorus|bridge|ending)/gi);
	},

	WrapChords: function(input) {
		return input.replace(this.ChordReplaceRegex, "<span class='c'>$1</span>");
	},

	GetKeyIndex: function(key) {
		for (var i = 0; i < this.Keys.length; i++) {
			if (key == this.Keys[i]) return i;
		}
	},

	GetChordRoot: function(input) {
		if (input.length > 1 && (input.charAt(1) == "b" || input.charAt(1) == "#"))
			return input.substr(0, 2);
		else
			return input.substr(0, 1);
	},

	GetNewKey: function(oldKey, delta) {
		var index = this.GetKeyIndex(oldKey) + delta;

		if (index > this.Keys.length - 1)
			index -= this.Keys.length;
		else if (index < 0)
			index += this.Keys.length;

		return this.Keys[index];
	},

	TransposeSong: function(key) {
		var newIndex = this.GetKeyIndex(key);
		var delta = this.GetDelta(this.KeyIndex, newIndex);
		if (delta == 0) return;

		var $this = this;
		$("span.c", this.Selector).each(function(i, el) {
			$this.TransposeChord(el, delta);
		});
		this.KeyIndex = newIndex;
	},

	GetDelta: function(oldIndex, newIndex) {
		if (oldIndex > newIndex)
			return 0 - (oldIndex - newIndex);
		else if (oldIndex < newIndex)
			return 0 + (newIndex - oldIndex);
		else
			return 0;
	},

	TransposeChord: function(selector, delta) {
		var el = $(selector);
		var oldChord = el.text();
		var oldChordRoot = this.GetChordRoot(oldChord);
		var newChordRoot = this.GetNewKey(oldChordRoot, delta);
		var newChord = newChordRoot + oldChord.substr(oldChordRoot.length);
		el.text(newChord);

		var sib = el[0].nextSibling;
		if (sib && sib.nodeType == 3 && sib.nodeValue.length > 0 && sib.nodeValue.charAt(0) != "/") {
			var wsLength = this.GetNewWhiteSpaceLength(oldChord.length, newChord.length, sib.nodeValue.length);
			sib.nodeValue = this.MakeString(" ", wsLength);
		}
	},

	GetNewWhiteSpaceLength: function(a, b, c) {
		if (a > b)
			return (c + (a - b));
		else if (a < b)
			return (c - (b - a));
		else
			return c;
	},

	MakeString: function(s, repeat) {
		var o = [];
		for (var i = 0; i < repeat; i++) o.push(s);
		return o.join("");
	}
};


$(function() {
    if (window.originalKey) {
        var p = new Parser("pre#song", window.originalKey);

        var keyLinks = [];
        $(p.Keys).each(function(i, key) {
            if (p.KeyIndex == i)
                keyLinks.push("<a href='#' class='selected'>" + key + "</a>");
            else
                keyLinks.push("<a href='#'>" + key + "</a>");
        });

        var keys = $("<div id='keys'></div>");
        keys.html(keyLinks.join(""));
        $("a", keys).click(function(e) {
            e.preventDefault();
            p.TransposeSong($(this).text());
            $("#keys a").removeClass("selected");
            $(this).addClass("selected");
            return false;
        });
        $(p.Selector).before(keys);

        var output = [];
        var text = $(p.Selector).text();
        var lines = text.split("\n");
        var line, tmp = "";

        for (var i = 0; i < lines.length; i++) {
            line = lines[i];

            if (p.IsChordLine(line))
                output.push("<span>" + p.WrapChords(line) + "</span>");
            else
                output.push("<span>" + line + "</span>");
        };

        $(p.Selector).html(output.join("\n"));
    }
});
