# USAGE:
#	gawk -f MkLotTbl.awk 入力ファイル > 出力ファイル

#*******************************************************************************
# エラーメッセージを表示して終了します。
function die(s) {
	print "### " s > "/dev/stderr"
	_die_exit = 1
	exit 1
}

#*******************************************************************************
# テーブルを開始します。
function BeginTable() {
	# 確実に、テーブルを終了します。
	EndTable()
	# テーブル名が重複定義されていないことを確認します。
	if($1 in aTableName) {
		die(FILENAME "(" FNR ")" "テーブル名が重複定義されています。")
	}
	aTableName[$1] = 1
	# テーブル名を格納します。
	TableName = $1
	# テーブルデータをクリアします。
	NumCols = 0
	NumRows = 0
	delete TableData
}

#*******************************************************************************
# テーブルを終了します。
function EndTable() {
	# テーブルが開始していたら…
	if(TableName != "") {
		# 少なくとも一行以上のデータが必要です。
		# - 最小のテーブル構成は、{テーブル名＋抽選列idの行}です。当選値と重み値の行は、一行も無くても構いません。
		if(NumRows == 0) {
			die(FILENAME "(" FNR ")" "データの無いテーブルが有ります。少なくとも一行以上のデータが必要です。")
		}
		# 二列目以降を抽選列と見なします。各抽選列について…
		tmpSize = 0
		tmpStr = ""
		for(Col = 2; Col <= NumCols; Col++) {
			# 重み値が0でない数を数え、当選値と(重み値-1)を文字列に追加します。
			ColId = TableData[Col, 1]	# 抽選列id
			ColSize = 0			# 重み値が0でない数
			ColStr = ""			# 当選値と(重み値-1)を追加するための文字列
			# 一行目は、抽選列idの行です。当選値と重み値の行は、二行目から開始します。
			# - 最小のテーブル構成は、{テーブル名＋抽選列idの行}です。当選値と重み値の行は、一行も無くても構いません。
			#   従って、以下のループは一回も通らない可能性も有ります。
			for(Row = 2; Row <= NumRows; Row++) {
				# 一列目を当選値と見なします。
				HitVal = TableData[1, Row]
				# 重み値を取得します。
				Weight = TableData[Col, Row]
				# 重み値が0でなければ…
				if(Weight != 0) {
					# 重み値が0でない数を増やします。
					ColSize++
					# 当選値と(重み値-1)を文字列に追加します。
					ColStr = ColStr "," HitVal ",(" Weight ")-1"	# 重み値が、数式,又は,シンボルで定義されていた場合、ここで(Weight-1)の値を計算できないため、文字列"-1"を付与することにしました。
				}
			}
			# 抽選列id、重み値が0でない数、及び、当選値と(重み値-1)の文字列を追加します。
			tmpSize += (2 + ColSize * 2)
			tmpStr = tmpStr ColId "," ColSize ColStr ",\n"
		}
		# 抽選表を重複無く登録します。
		if(!(tmpStr in aTable)) {
			sSource = sSource (NumCols-1) ",//" TableIndex "\n" tmpStr	# ソース文字列に追加
			aTable[tmpStr] = TableIndex
			TableIndex += (1 + tmpSize)
		}
		sHeader = sHeader "#define LotTbl_" TableName " " aTable[tmpStr] "\n"	# ヘッダ文字列に追加
	}
	# テーブル名をクリアします。
	TableName = ""
	# テーブルデータをクリアします。
	NumCols = 0
	NumRows = 0
	delete TableData
}

#*******************************************************************************
# 開始時の処理を行います。
BEGIN {
	#フィールドセパレータをタブに設定します。(CSV入力)
	FS = "\t"
	# ソース文字列を出力する時に、[0]は無効要素として明示的に出力します。有効要素は[1]から登録します。
	TableIndex = 1
}

#*******************************************************************************
# 空行以外を処理します。
NF != 0 {
	# 各フィールドの先頭と末尾の空白を削除します。
	for(Col = 1; Col <= NF; Col++) {
		gsub(/(^[[:blank:]]+)|([[:blank:]]+$)/, "", $Col)
	}
	# フィールド数が1ならば…
	if(NF == 1) {
		# テーブル名と見なし、テーブルを開始します。
		BeginTable()
	# フィールド数が2以上ならば…
	} else {
		# テーブルデータ行です。テーブルが開始していなくてはなりません。
		if(TableName == "") {
			die(FILENAME "(" FNR ")" "テーブル開始前のデータ定義があります。データよりも先に、テーブル名が必要です。")
		}
		# テーブル開始後、一行目のテーブルデータ行ならば…
		if(NumCols == 0) {
			# 列数を記憶します。
			NumCols = NF
		# テーブル開始後、二行目以降のテーブルデータ行ならば…
		} else {
			# 列数が同じであることを確認します。
			if(NF != NumCols) {
				die(FILENAME "(" FNR ")" "列数が異なります。一つのテーブル内の全ての行の列数は、同じでなくてはなりません。")
			}
		}
		# 行数を増やします。
		NumRows++
		# テーブルデータを格納します。
		for(Col = 1; Col <= NumCols; Col++) {
			TableData[Col, NumRows] = $Col
			# 抽選表の中の全ての要素は、空であってはなりません。
			if($Col == "") {
				die(FILENAME "(" FNR ")" Col "列目に、不正な要素の定義があります。抽選表の中の全ての要素は、空であってはなりません。")
			}
			# 一行目は、抽選列idの行です。
			if(NumRows == 1) {
				if(Col == 1) {
					# 一行目の一列目のセルは無視します。何を書いても構いません。
					#** no job **#
				} else {
					# 一行目の二列目以降は、抽選列idです。数値、数式、又は、シンボルです。
					# Awkスクリプトで、数値,数式,又は,シンボルであることを確認するのは難しいため、ここでは確認せず、出力データのコンパイルエラーで判断することにしました。
				}
			# 二行目以降は、当選値と重み値の行です。
			} else {
				if(Col == 1) {
					# 二行目以降の一列目のセルは、当選値です。整数式、又は、シンボルです。
					# Awkスクリプトで、数値,数式,又は,シンボルであることを確認するのは難しいため、ここでは確認せず、出力データのコンパイルエラーで判断することにしました。
				} else {
					# 二行目以降の二列目以降のセルは、重み値です。整数式、又は、シンボルです。
					# Awkスクリプトで、数値,数式,又は,シンボルであることを確認するのは難しいため、ここでは確認せず、出力データのコンパイルエラーで判断することにしました。
				}
			}
		}
	}
}

#*******************************************************************************
# 終了時の処理を行います。
END {
	# die()経由ならば、ENDのアクションを実行せずに終了させます。
	if(_die_exit) {
		exit	# Awkの仕様: 引数付きのexit文を最初に実行し、その後で引数無しのexit文を実行すると、その前に使った引数が終了ステータスとして使われる
	}
	# 確実に、テーブルを終了します。
	EndTable()
	# ヘッダ文字列を出力します。
	print  "//{{HEADER"
	printf sHeader
	print  "#define LotTbl_SIZE " TableIndex
	print "extern const unsigned short TBL_LotTbl[LotTbl_SIZE];"
	print  "//}}HEADER"
	# ソース文字列を出力します。
	print  "//{{SOURCE"
	print  "#include \"app.h\""
	print  "const unsigned short TBL_LotTbl[" TableIndex "]={0,"
	printf sSource
	print  "};"
	print  "//}}SOURCE"
}
