#!@PYTHON@

# yodl2texinfo-post.py
# 
# source file of the GNU LilyPond music typesetter
# 
# (c) 1998 Jan Nieuwenhuizen

program_name = 'yodl2texinfo-post'
version = '@TOPLEVEL_VERSION@'

import os
import sys

import getopt
from string import *
import regex
import regsub
import time

def program_id ():
	return program_name + ' version ' + version;

def identify ():
	sys.stdout.write (program_id () + '\n')

def help ():
	sys.stdout.write ("Usage: %s [OPTION]... [FILE]...\n"
		"Resolve yodl tags, autogenerate nodes and menus.\n\n"
		+ "Options:\n"
		+ "  -h, --help		   print this help\n"
		% (program_name)
		)
	sys.exit (0)


identify ()
(options, files) = getopt.getopt (
	sys.argv[1:], 'h', ['help'])
for opt in options:
	o = opt[0]
	a = opt[1]
	if o == '--help' or o == '-h':
		help ()
	else:
		print o
		raise getopt.error


def gulp_file (f):
	try:
		i = open (f)
		i.seek (0, 2)
		n = i.tell ()
		i.seek (0,0)
	except:
		sys.stderr.write ('can\'t open file %s\n ' % f)
		return ''
	s = i.read (n)
	if len (s) <= 0:
		sys.stderr.write ('gulped empty file: %s\n'% f)
	return s


class Node:
	def __init__ (this, pos, name):
		this.pos = pos
		this.name = name
		this.description = ''
		this.prev = ''
		this.next = ''
		this.top = ''

class Menu (Node):
	def __init__ (this, pos, name):
		Node.__init__ (this, pos, name)
		this.node_list = []

TAB_POS = 34

class Pre_texinfo_file (Menu):
	def __init__ (this, filename):
		Menu.__init__ (this, 0, 'Top')
		this.filename = filename
		this.node_list = []
		this.s = ''
		this.offset = 0
		this.last = Node (0, '')
		this.prefix = ''
		this.default = Node (0,'')
		this.default.top = '(dir)'
		this.node_dict = {}
		this.xref_list = []

	def gulp (this):
		this.s = gulp_file (this.filename)

	def new_menu (this, pos, name):
		m = Menu (pos, name)
		if this.default.name:
			m.name = this.default.name
		if this.default.description:
			m.description = this.default.description
		m.name = this.prefix + m.name
		return m

	def new_node (this, pos, name):
		n = Node (pos, name)
		if this.default.name:
			n.name = this.default.name
		if this.default.description:
			n.description = this.default.description
		n.name = this.prefix + n.name
		while this.node_dict.has_key (n.name):
			n.name = n.name + 'i'
		this.node_dict[n.name] = 0
		n.top = this.default.top
		n.prev = this.last.name
		this.last.next = n.name
		this.last = n
		return n

	def eat_tag (this):
		i = regex.search ('@TAGSTART@', this.s)
		j = 0
		if i < 0:
			return 0
		j = regex.search ('@TAGEND@', this.s[i:])
		if j < 0:
			raise 'huh?'
		j = i + j + len ('@TAGEND@')
		tag = this.s[i + len ('@TAGSTART@'):j - len ('@TAGEND@')]
		k = regex.search (' ', tag[1:]) + 1
		tag_name = tag[1:k]
		tag_string = tag[k:len (tag) - 1]
		while tag_string[:1] == ' ':
			tag_string = tag_string[1:]
		#
		# urg, commas in titles...
		# but even more urg, makeinfo (3.12) chokes on 
		# various other characters in node names too
		# 
		not_in_node = '[,:\.?!\\\'`{}]'
		tag_string = regsub.gsub (not_in_node, ' ', tag_string)
		tag_string = regsub.gsub ('@code', ' ', tag_string)
		tag_string = regsub.gsub ('@emph', ' ', tag_string)
		tag_string = regsub.gsub ('@strong', ' ', tag_string)
		if tag_name == 'menu':
			this.default.top = 'Top'
			n = this.new_node (i, tag_string)
			m = this.new_menu (i, tag_string)
			this.default.top = tag_string
			this.default.name = ''
			this.default.description = ''
			m.node_list.append (n)
			this.node_list.append (m)
		elif tag_name == 'node':
			n = this.new_node (i, tag_string)
			this.default.name = ''
			this.default.description = ''
			if not len (this.node_list):
				# m = Menu (i, 'Toplevel')
				m = this.new_menu (i, 'Toplevel')
				this.node_list.append (m)
			this.node_list[len (this.node_list)-1].node_list.append (n)
		elif tag_name == 'node_description':
			this.default.description = tag_string
		elif tag_name == 'node_name':
			this.default.name = tag_string
		elif tag_name == 'node_prefix':
			this.prefix = tag_string
		elif tag_name == 'ref':
			## urg, prefix is broken
			ref = '@xref{' + this.prefix + tag_string + '}'
			this.s = this.s[:i] + ref + this.s[j:]
			this.xref_list.append (this.prefix + tag_string)
			## urg
			return 1
		else:
			sys.stderr.write ("unknown tag: `" + tag + "', skipping\n")
		this.s = this.s[:i] + this.s[j:]
		return 1

	def plug (this):
		os.rename (this.filename, this.filename + '~')
		fd = open (this.filename, 'w')
		fd.write (this.s)
		fd.close ()

	def create_node (this, n):
		node = '@node ' + n.name + ', ' + n.next + ', ' + n.prev + ', ' + n.top + "\n"
		this.s = this.s[:n.pos + this.offset] + node + this.s[n.pos + this.offset:]
		this.offset = this.offset + len (node)
		
	def create_menu (this, m):
		if len (m.node_list) < 2:
			return
		menu = "@menu\n"
		for n in m.node_list[1:]:
			d = n.description
			if not d:
				d = n.name
			menu = menu + ljust ('* ' + n.name + '::', TAB_POS) + d + "\n"
		menu = menu + "@end menu\n"
		this.s = this.s[:m.pos + this.offset] + menu + this.s[m.pos + this.offset:]
		m.node_list[0].pos = m.node_list[0].pos - len (menu)
		this.offset = this.offset + len (menu)

	def create_nodes (this, m):
		for n in m.node_list:
			this.create_node (n)
		
	def parse (this):
#		while this.eat_tag ():
		i = 1
		while i:
			i = this.eat_tag ()

	def nodes_and_menus (this):
		this.default.top = '(dir)'
#		this.pos = 0
		n = this.new_node (0, 'Top')
		m = Menu (0, 'Top')
		m.node_list.append (n)
		this.node_list.insert (0, m)
		n.pos = this.offset
		this.create_menu (this)
		n.pos = - this.offset
		this.create_nodes (m)
		for m in this.node_list[1:]:
			this.create_menu (m)
			this.create_nodes (m)

	def xrefs (this):
		this.prefix = ''
		this.default.top = 'Top'
		n = this.new_node (len (this.s), 'Missing nodes')
		m = Menu (len (this.s), 'Missing nodes')
		m.node_list.append (n)
		this.default.top = 'Missing nodes'
		for i in this.xref_list:
			if not this.node_dict.has_key (i):
				sys.stderr.write (this.filename + ": warning: xref to nonexistent node: `" + i + "'\n")
				n = this.new_node (len (this.s), i)
				m.node_list.append (n)
		if len (m.node_list) > 1:
			this.node_list.append (m)

	def post (this):
		this.gulp ()
		this.parse ()
		this.xrefs ()
		this.nodes_and_menus ()
		# urg?
		this.s = "\n\n\n" + this.s + "\n@bye"
		this.plug ()


for f in files:
	t = Pre_texinfo_file (f)
	t.post ()

