#!/usr/bin/env python
#coding: utf-8
#
# unmap 逆マップ（Open usp Tukubai版）
# 
# designed by Nobuaki Tounaka
# written by Yoshio Katayama
#
# The MIT License
#
# Copyright (C) 2011 Universal Shell Programming Laboratory
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

_usage = "unmap       num=<n>x<m> <file>"
_usage1 = "unmap +yarr num=<n>x<m> <file>"
_usage2 = "unmap +arr  num=<n>x<m> <file>"
_version = "Sat Dec 10 21:04:17 JST 2011"
_code = "Open usp Tukubai (LINUX+FREEBSD/PYTHON2.4/UTF-8)"
_numpat = r'(\d+)(x\d+)?$'
		# 1: num_x
		# 2: 'x' + num_y

import re
import os
import sys
import stat

def error(msg, *arg):
	print >> sys.stderr, 'Error[unmap] :', msg % arg
	sys.exit(1)

def usage():
	print >> sys.stderr, "Usage   :", _usage
	print >> sys.stderr, "        :", _usage1
	print >> sys.stderr, "        :", _usage2
	print >> sys.stderr, "Version :", _version
	print >> sys.stderr, "         ", _code
	sys.exit(1)

class FieldLine:
	def __init__(self, line, allow_z = False):
		self.__allow_zero = allow_z
		line = line.rstrip('\n')
		self.__fields = [ line ]
		self.__fields += [ x for x in line.split(' ') if x ]

	def size(self):
		return len(self.__fields) - 1

	def getFieldNum(self, key):
		if type(key) == type(0):
			return key
		if re.match(r'\d+$', key):
			key = int(key)
		elif key == 'NF':
			key = self.size()
		else:
			key = self.size() - int(key[3:])
			if key <= 0:
				error("NF-x の x が大きすぎます。")
		if key < 0:
			error("フィールド番号が負です。")
		if key == 0 and not self.__allow_zero:
			error("フィールド番号が０です。")
		if key > self.size():
			error("フィールド番号が大きすぎます。")
		return key

	def getField(self, s, e = None):
		s = self.getFieldNum(s)
		if e == None:
			e = s
		else:
			e = self.getFieldNum(e)
		if s <= e:
			return ' '.join(self.__fields[s : e + 1])
		else:
			t = self.__fields[e : s + 1]
			t.reverse()
			return ' '.join(t)

	def getFieldList(self, s, e = None):
		if e:
			return self.__fields[s : e + 1]
		else:
			return self.__fields[s : ]

#
# 入力ファイルオープン
#
def open_file(n, mode = 'r'):
	if n >= len(sys.argv) or sys.argv[n] == '-':
		file = sys.stdin
	else:
		try:
			file = open(sys.argv[n], mode)
		except:
			error("ファイル %s をオープンできません。", sys.argv[n])
	return file

#
# 横キーの取得
#
def get_ykey(header, num_x):
	#
	# サブキーの有無の確認
	#
	l = header[0].size()
	if l > num_x and header[0].getField(num_x + 1) == '*':
		subkey = 1
	else:
		subkey = 0

	#
	# フィールド数の確認
	#
	for h in header:
		if h.size() <= num_x + subkey:
			error("フィールドが足りません。")
		elif h.size() != l:
			error("フィールド数が異なります。")
		elif subkey and h.getField(num_x + subkey) != '*':
			error("%d 行目のサブキーフィールドが * ではありません。", i)

	#
	# 横キーの取得
	#
	L, last_L = 0, 0
	ykey = [ ' '.join([ h.getField(num_x + subkey + 1) for h in header ]) ]
	for i in range(num_x + subkey + 1, l + 1):
		yk = ' '.join([ h.getField(i) for h in header ])
		if yk != ykey[-1]:
			if L != last_L and last_L != 0:
				error("横キーの個数が不均一です。")
			last_L = L
			L = 0
			ykey += [ yk ]
		L += 1

	if L != last_L and last_L == 0:
		error("横キーの個数が不均一です。")

	return ykey, header[0].size(), L, subkey

#
# ブロックの読み込み
#
def read_block(file, first, line, num_x, subkey, nfld, nline):
	if not line:
		line = file.readline()
		if not line:
			return None
		line = FieldLine(line)
	tkey = line.getField(1, num_x)
	block = []
	nl = 0
	while True:
		if line.size() != nfld:
			error("フィールド数が異なります。")
		tk = line.getField(1, num_x)
		if tk != tkey:
			if not first and nl < nline:
				error("同じ縦キーの行が足りません。")
			return block, tkey, nl, line
		block += [ line.getFieldList(num_x + subkey + 1) ]
		nl += 1
		if not first and nl > nline:
			error("同じ縦キーの行が多過ぎます。")
		line = file.readline()
		if not line:
			if not first and nl < nline:
				error("行が足りません。")
			return block, tkey, nl, line
		line = FieldLine(line)

#
# ブロックの出力 (+yarr / +arr 有り)
#
def putblk_array(tkey, ykey, block, L, nline):
	for (i, yk) in enumerate(ykey):
		print tkey, yk, 
		for j in range(nline):
			for k in range(L):
				print block[j][L * i + k],
		print

#
# ブロックの出力 (+yarr / +arr 無し)
#
def putblk_normal(tkey, ykey, block, L, nline):
	for (i, yk) in enumerate(ykey):
		print tkey, yk, 
		for j in range(L):
			for k in range(nline):
				print block[k][L * i + j],
		print

#
# unmap 処理 (+yarr / +arr 有り)
#
def do_unmap_array(file, num_x, subkey, nfld, L, ykey, line):
	#
	# read_block() --> block, tkey, nline, line
	#
	t = read_block(file, True, line, num_x, subkey, nfld, 0)
	putblk_array(t[1], ykey, t[0], L, t[2])
	while True:
		t = read_block(file, False, t[3], num_x, subkey, nfld, t[2])
		if not t:
			return
		putblk_array(t[1], ykey, t[0], L, t[2])

#
# unmap 処理 (+yarr / +arr 無し)
#
def do_unmap_normal(file, num_x, subkey, nfld, L, ykey, line):
	#
	# read_block() --> block, tkey, nline, line
	#
	t = read_block(file, True, line, num_x, subkey, nfld, 0)
	putblk_normal(t[1], ykey, t[0], L, t[2])
	while True:
		t = read_block(file, False, t[3], num_x, subkey, nfld, t[2])
		if not t:
			return
		putblk_normal(t[1], ykey, t[0], L, t[2])


#
#メイン関数
#
if __name__ == '__main__':

	if len(sys.argv) <= 1 \
	 or sys.argv[1] == '--help' \
	 or sys.argv[1] == '--version':
		usage()

	#
	# +yarr
	#
	if sys.argv[1] == '+yarr' or sys.argv[1] == '+arr':
		array = True
		del sys.argv[1]
		if len(sys.argv) <= 1:
			usage()
	else:
		array = False

	#
	# num=<n>x<m>
	#
	r = re.match('num=' + _numpat, sys.argv[1])
	if not r:
		usage()
	num_x = int(r.group(1))
	if r.group(2):
		num_y = int(r.group(2)[1:])
	else:
		num_y = 1
	if num_x * num_y == 0:
		usage()

	#
	# ファイルのオープン
	#
	file = open_file(2)

	#
	# ヘッダーの入力
	#
	header = []
	for i in range(num_y):
		line = file.readline()
		if not line:
			if i == 0:
				sys.exit(0)
			else:
				error("ヘッダー行の途中でファイルが終了していま す。")
		header += [ FieldLine(line) ]

	#
	# 横キーの取得
	#
	ykey, nfld, L, subkey = get_ykey(header, num_x)

	#
	# サブキー行の確認
	#
	line = file.readline()
	if not line:
		sys.exit(0)
	line = FieldLine(line)
	if line.getField(1) == '*':
		line = None

	#
	# 本体処理
	#
	if array:
		do_unmap_array(file, num_x, subkey, nfld, L, ykey, line)
	else:
		do_unmap_normal(file, num_x, subkey, nfld, L, ykey, line)

	sys.exit(0)
