import configuration
config = configuration.Config()
rom = bytearray(open(config.rom_path, "r").read())

songs = [
	"PalletTown",
	"Pokecenter",
	"Gym",
	"Cities1",
	"Cities2",
	"Celadon",
	"Cinnabar",
	"Vermilion",
	"Lavender",
	"SSAnne",
	"MeetProfOak",
	"MeetRival",
	"MuseumGuy",
	"SafariZone",
	"PkmnHealed",
	"Routes1",
	"Routes2",
	"Routes3",
	"Routes4",
	"IndigoPlateau",
	"GymLeaderBattle",
	"TrainerBattle",
	"WildBattle",
	"FinalBattle",
	"DefeatedTrainer",
	"DefeatedWildMon",
	"DefeatedGymLeader",
	"TitleScreen",
	"Credits",
	"HallOfFame",
	"OaksLab",
	"JigglypuffSong",
	"BikeRiding",
	"Surfing",
	"GameCorner",
	"IntroBattle",
	"Dungeon1",
	"Dungeon2",
	"Dungeon3",
	"CinnabarMansion",
	"PokemonTower",
	"SilphCo",
	"MeetEvilTrainer",
	"MeetFemaleTrainer",
	"MeetMaleTrainer",
	"UnusedSong",
	]
"""
songs = [
	"YellowIntro",
	"SurfingPikachu",
	"MeetJessieJames",
	"YellowUnusedSong",
	]
"""
music_commands = {
	0xd0: ["notetype", {"type": "nibble"}, 2],
	0xe0: ["octave", 1],
	0xe8: ["toggleperfectpitch", 1],
	0xea: ["vibrato", {"type": "byte"}, {"type": "nibble"}, 3],
	0xeb: ["pitchbend", {"type": "byte"}, {"type": "byte"}, 3],
	0xec: ["duty", {"type": "byte"}, 2],
	0xed: ["tempo", {"type": "word"}, 3],
	0xee: ["stereopanning", {"type": "byte"}, 2],
	0xf0: ["volume", {"type": "nibble"}, 2],
	0xf8: ["executemusic", 1],
	0xfc: ["dutycycle", {"type": "byte"}, 2],
	0xfd: ["callchannel", {"type": "label"}, 3],
	0xfe: ["loopchannel", {"type": "byte"}, {"type": "label"}, 4],
	0xff: ["endchannel", 1],
	}

param_lengths = {
	"nibble": 1,
	"byte": 1,
	"word": 2,
	"label": 2,
	}

music_notes = {
	0x0: "C_",
	0x1: "C#",
	0x2: "D_",
	0x3: "D#",
	0x4: "E_",
	0x5: "F_",
	0x6: "F#",
	0x7: "G_",
	0x8: "G#",
	0x9: "A_",
	0xa: "A#",
	0xb: "B_",
	}

def printnoisechannel(songname, songfile, startingaddress, bank, output):
	noise_commands = {
		0xfd: ["callchannel", {"type": "label"}, 3],
		0xfe: ["loopchannel", {"type": "byte"}, {"type": "label"}, 4],
		0xff: ["endchannel", 1],
		}
	
	noise_instruments = {
		0x01: "snare1",
		0x02: "snare2",
		0x03: "snare3",
		0x04: "snare4",
		0x05: "snare5",
		0x06: "triangle1",
		0x07: "triangle2",
		0x08: "snare6",
		0x09: "snare7",
		0x0a: "snare8",
		0x0b: "snare9",
		0x0c: "cymbal1",
		0x0d: "cymbal2",
		0x0e: "cymbal3",
		0x0f: "mutedsnare1",
		0x10: "triangle3",
		0x11: "mutedsnare2",
		0x12: "mutedsnare3",
		0x13: "mutedsnare4",
		}
	
	# pass 1, build a list of all addresses pointed to by calls and loops
	address = startingaddress
	labels = []
	labelsleft= []
	while 1:
		byte = rom[address]
		if byte < 0xc0:
			command_length = 2
		elif byte < 0xe0:
			command_length = 1
		else:
			command_length = noise_commands[byte][-1]
			if byte == 0xfd or byte == 0xfe:
				label = rom[address + command_length - 1] * 0x100 + rom[address + command_length - 2]
				labels.append(label)
				if label > address % 0x4000 + 0x4000: labelsleft.append(label)
		address += command_length
		if len(labelsleft) == 0 and (byte == 0xfe and rom[address - command_length + 1] == 0 and rom[address - 1] * 0x100 + rom[address - 2] < address % 0x4000 + 0x4000 or byte == 0xff): break
		while address % 0x4000 + 0x4000 in labelsleft: labelsleft.remove(address % 0x4000 + 0x4000)
	# once the loop ends, start over from first address
	if rom[address] == 0xff: address += 1
	end = address
	address = startingaddress
	byte = rom[address]
	output += "Music_{}_Ch4:: ; {:02x} ({:0x}:{:02x})\n".format(songname, address, bank, address % 0x4000 + 0x4000)
	# pass 2, print commands and labels for addresses that are in labels
	while address != end:
		if address % 0x4000 + 0x4000 in labels and address != startingaddress:
			output += "\nMusic_{}_branch_{:02x}::\n".format(songname, address)
		if byte < 0xc0:
			output += "\t{} {}".format(noise_instruments[rom[address + 1]], byte % 0x10 + 1)
			command_length = 2
		elif byte < 0xd0:
			output += "\trest {}".format(byte % 0x10 + 1)
			command_length = 1
		elif byte < 0xe0:
			output += "\tdspeed {}".format(byte % 0x10)
			command_length = 1
		else:
			command = noise_commands[byte]
			output += "\t{}".format(command[0])
			command_length = 1
			params = 1
			# print all params for current command
			while params != len(noise_commands[byte]) - 1:
				param_type = noise_commands[byte][params]["type"]
				address += command_length
				command_length = param_lengths[param_type]
				param = rom[address]
				if param_type == "byte":
					output += " {}".format(param)
				else:
					param += rom[address + 1] * 0x100 - 0x4000 + (bank * 0x4000)
					if param == startingaddress: output += " Music_{}_Ch4".format(songname)
					else: output += " Music_{}_branch_{:02x}".format(songname, param)
				params += 1
				if params != len(noise_commands[byte]) - 1: output += ","
		output += "\n"
		address += command_length
		byte = rom[address]
	output += "; {}\n".format(hex(address))
	songfile.write(output)

for i, songname in enumerate(songs):
	songfile  = open("music/" + songname.lower() + ".asm", 'a')
	if songname == "PalletTown": header = 0x822e
	if songname == "GymLeaderBattle": header = 0x202be
	if songname == "TitleScreen": header = 0x7c249
	if songname == "YellowIntro": header = 0x7c294
	if songname == "SurfingPikachu": header = 0x801cb
	bank = header / 0x4000
	startingaddress = rom[header + 2] * 0x100 + rom[header + 1] - 0x4000 + (0x4000 * bank)
	curchannel = 1
	lastchannel = (rom[header] >> 6) + 1
	exception = False
	if songname == "MeetRival" or songname == "Cities1":
		startingaddress -= 7
		exception = True
	if songname == "UnusedSong":
		bank = 2
		startingaddress = 0xa913
		lastchannel = 2
	output = ''
	while 1:
		# pass 1, build a list of all addresses pointed to by calls and loops
		address = startingaddress
		labels = []
		labelsleft = []
		if songname == "MeetRival":
			if curchannel == 1:
				labels.append(0x719b)
				labelsleft.append(0x719b)
				labels.append(0x71a2)
				labelsleft.append(0x71a2)
			if curchannel == 2:
				labels.append(0x721d)
				labelsleft.append(0x721d)
			if curchannel == 3:
				labels.append(0x72b5)
				labelsleft.append(0x72b5)
		while 1:
			byte = rom[address]
			if byte < 0xd0:
				command_length = 1
			elif byte < 0xe0:
				command_length = 2
			elif byte < 0xe8:
				command_length = 1
			else:
				command_length = music_commands[byte][-1]
				if byte == 0xfd or byte == 0xfe:
					label = rom[address + command_length - 1] * 0x100 + rom[address + command_length - 2]
					labels.append(label)
					if label > address % 0x4000 + 0x4000: labelsleft.append(label)
			address += command_length
			if len(labelsleft) == 0 and (exception == False or address > startingaddress + 7) and (byte == 0xfe and rom[address - command_length + 1] == 0 and rom[address - 1] * 0x100 + rom[address - 2] < address % 0x4000 + 0x4000 or byte == 0xff): break
			while address % 0x4000 + 0x4000 in labelsleft: labelsleft.remove(address % 0x4000 + 0x4000)
		# once the loop breaks, start over from first address
		if rom[address] == 0xff: address += 1
		end = address
		if curchannel != lastchannel and songname != "UnusedSong": end = rom[header + 5] * 0x100 + rom[header + 4] + (0x4000 * (bank - 1))
		address = startingaddress
		byte = rom[address]
		# if song has an alternate start to channel 1, print a label and set startingaddress to true channel start
		if exception:
			output += "Music_{}_branch_{:02x}::\n".format(songname, address)
			startingaddress += 7
		# pass 2, print commands and labels for addresses that are in labels
		while address != end:
			if address == startingaddress:
				if exception: output += "\n"
				output += "Music_{}_Ch{}:: ; {:02x} ({:0x}:{:02x})\n".format(songname, curchannel, address, bank, address % 0x4000 + 0x4000)
			elif address % 0x4000 + 0x4000 in labels:
				output += "\nMusic_{}_branch_{:02x}::\n".format(songname, address)
			if byte < 0xc0:
				output += "\t{} {}".format(music_notes[byte >> 4], byte % 0x10 + 1)
				command_length = 1
			elif byte < 0xd0:
				output += "\trest {}".format(byte % 0x10 + 1)
				command_length = 1
			else:
				if byte < 0xe0:
					command = music_commands[0xd0]
					output += "\t{} {},".format(command[0], byte % 0x10)
					byte = 0xd0
				elif byte < 0xe8:
					command = music_commands[0xe0]
					output += "\t{} {}".format(command[0], 0xe8 - byte)
					byte = 0xe0
				else:
					command = music_commands[byte]
					output += "\t{}".format(command[0])
				command_length = 1
				params = 1
				# print all params for current command
				while params != len(music_commands[byte]) - 1:
					param_type = music_commands[byte][params]["type"]
					address += command_length
					command_length = param_lengths[param_type]
					param = rom[address]
					if param_type == "nibble":
						output += " {}, {}".format(param >> 4, param % 0x10)
					elif param_type == "byte":
						output += " {}".format(param)
					elif param_type == "word":
						output += " {}".format(param * 0x100 + rom[address + 1])
					else:
						param += rom[address + 1] * 0x100 - 0x4000 + (bank * 0x4000)
						if param == startingaddress: output += " Music_{}_Ch{}".format(songname, curchannel)
						else: output += " Music_{}_branch_{:02x}".format(songname, param)
					params += 1
					if params != len(music_commands[byte]) - 1: output += ","
			output += "\n"
			address += command_length
			byte = rom[address]
		header += 3
		if curchannel == lastchannel:
			output += "; {}\n".format(hex(address))
			songfile.write(output)
			break
		curchannel += 1
		output += "\n\n"
		startingaddress = end
		exception = False
		if curchannel == 4:
			printnoisechannel(songname, songfile, startingaddress, bank, output)
			header += 3
			break