# -*- coding: utf-8 -*-

#	Copyright © 2014 dyknon
#
#	This file is part of Pylib-nicovideo.
#
#	Pylib-nicovideo is free software: you can redistribute it and/or modify
#	it under the terms of the GNU General Public License as published by
#	the Free Software Foundation, either version 3 of the License, or
#	(at your option) any later version.
#
#	This program is distributed in the hope that it will be useful,
#	but WITHOUT ANY WARRANTY; without even the implied warranty of
#	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#	GNU General Public License for more details.
#
#	You should have received a copy of the GNU General Public License
#	along with this program.  If not, see <http://www.gnu.org/licenses/>.

from . import err
from . import tools
from . import fontwidth
from . import logger
import re
import random
import string

class Commands:
	#コメントのコマンドを管理するClass
	def __init__(self, initializer=None):
		self.colorcode_detector = re.compile("^#([0-9a-fA-F]{6})$")
		self.attime_detector = re.compile("^[@＠]([0-9]+)$")
		self.premium = False
		self.owner = False
		self.size = "medium"
		self.color = "FFFFFF"
		self.position = "naka"
		self.flag_invisible = False
		self.flag_sage = False
		self.flag_full = False
		self.flag_patissier = False
		self.flag_ender = False
		self.flag_184 = False
		self.flag_from_button = False
		self.flag_is_button = False
		self.flag_live = False
		self.flag_iphone = False
		self.flag_docomo = False
		self.flag_softbank = False
		self.time = 300
		if initializer:
			self.update(initializer)

	def update(self, data):
		#dataがstrならコマンドであるとして処理
		#dictならコメント情報であるとして処理
		colortable = {			 
			"white":	"FFFFFF",
			"red":		"FF0000",
			"pink":		"FF8080",
			"orange":	"FFC000",
			"yellow":	"FFFF00",
			"green":	"00FF00",
			"cyan":		"00FFFF",
			"blue":		"0000FF",
			"purple":	"C000FF",
			"black":	"000000" 
		}
		premium_colortable = {		 
			"niconicowhite":"CCCC99",
			"white2":		"CCCC99",
			"truered":		"CC0033",
			"red2":			"CC0033",
			"passionorange":"FF6600",
			"orange2":		"FF6600",
			"madyellow":	"999900",
			"yellow2":		"999900",
			"elementalgreen":"00CC66",
			"green2":		"00CC66",
			"marineblue":	"3399FF",
			"blue2":		"3399FF",
			"nobleviolet":	"6633CC",
			"purple2":		"6633CC",
			"black2":		"666666"
		}
		positions = ["ue", "naka", "shita"]
		sizes = ["big", "medium", "small"]

		if isinstance(data, dict):
			if "fork" in data:
				if int(data["fork"]):
					self.owner = True
				else:
					self.owner = False
			if "premium" in data:
				if int(data["premium"]):
					self.premium = True
				else:
					self.premium = False
			if "mail" in data:
				data = data["mail"]
			else:
				return
		for command in data.split():
			if command == "invisible":
				self.flag_invisible = True
			elif command == "sage":
				self.flag_sage = True
			elif command == "full":
				self.flag_full = True
			elif command == "patissier":
				self.flag_patissier = True
			elif command == "ender":
				self.flag_ender = True
			elif command == "184":
				self.flag_184 = True
			elif command == "from_button":
				self.flag_from_button = True
			elif command == "is_button":
				self.flag_is_button = True
			elif command == "_live":
				self.flag_live = True
			elif command == "iPhone":
				self.flag_iphone = True
			elif command == "docomo":
				self.flag_docomo = True
			elif command == "softbank":
				self.flag_softbank = True
			elif command in positions:
				self.position = command
			elif command in sizes:
				self.size = command
			elif command in colortable:
				self.color = colortable[command]
			if self.premium or self.owner:
				if command in premium_colortable:
					self.color = premium_colortable[command]
				else:
					mo = self.colorcode_detector.match(command)
					if mo:
						self.color = mo.group(1).upper()
			if self.owner:
				mo = self.attime_detector.match(command)
				if mo:
					self.time = int(mo.group(1)) * 100

class StandardFontSizeDecider:
	def __init__(self):
		#各種定数
		self.fontsize = {		"big":		39	,	#20
								"medium":	24	,	#12
								"small":	14	}	#8
		self.height_limit = 				130
		self.width_limit = {	False:		544	,
								True:		672	}
		self.margine = {		"above":	4	,
								"below":	6	,
								"right":	3	,
								"left":		2	}
		self.small_margine = {	"above":	3	,
								"below":	3	,
								"right":	2	,
								"left":		0	}
		#self.space	 = {		"big":		6	,	#4
		#						"medium":	5	,	#3
		#						"small":	4	}	#2
		self.linespace_base =				2
		self.linespace_font =				1 / 10
		self.widthpad =						0
		self.font_HWalphanumeric = fontwidth.font_HWalphanumeric
		self.font_priority = fontwidth.font_priority
		self.fontchange_chars = fontwidth.font_change_chars 
		self.fontlist = fontwidth.fontlist 

	def get(self, comment, commands=None):
		#注意:commandsが与えられるとcommants["mail"]は無視します。
		if not commands:
			commands = Commands(comment)
		default_font = self.font_priority[0]
		for char in comment["*"]:
			for font in self.font_priority:
				if char in self.fontchange_chars[font]:
					default_font = font
					break
			else:
				continue
			break
		h = 0
		w = 0
		lnws = []
		fontlist = []
		for line in comment["*"].split("\n"):
			fontinline = []
			h += 1
			kw = 0
			for char in line:
				if char in string.printable:
					font_for_char = self.font_HWalphanumeric
				else:
					font_for_char = default_font
				fontinline.append(font_for_char)
				try:
					kw += self.fontlist[font_for_char]["w"][char] \
							/ self.fontlist[font_for_char]["h"]
				except KeyError:
					kw += 1
			if kw > w:
				w = kw
			lnws.append(kw)
			fontlist.append(fontinline)

		fs = self.fontsize[commands.size]
		sp = int(self.linespace_base + self.linespace_font * fs)
		am = sp - 1
		bm = sp + 1
		lm = self.margine["left"]
		rm = self.margine["right"]
		ha = int(h * fs + (h-1) * sp + am + bm)
		wa = 0
		i = 0
		for linw in lnws:
			kw = int(linw*fs + (len(fontlist[i])-1)*self.widthpad + lm + rm)
			if kw > wa:
				wa = kw
			i += 1
		if ha > self.height_limit and not commands.flag_ender:
			fs = int(self.fontsize[commands.size]/2 + 0.5)
			sp = int(self.linespace_base + self.linespace_font * fs)
			am = sp - 1
			bm = sp + 1
			lm = self.small_margine["left"]
			rm = self.small_margine["right"]
			ha = int(h * fs + (h-1) * sp + am + bm)
			wa = 0
			i = 0
			for linw in lnws:
				kw = int(linw*fs + (len(fontlist[i])-1)*self.widthpad + lm + rm)
				if kw > wa:
					wa = kw
				i += 1
		if wa > self.width_limit[commands.flag_full] \
			and commands.position != "naka":
			mn = self.width_limit[commands.flag_full] / wa
			fs = int(self.fontsize[commands.size]*mn)
			sp = int(self.linespace_base + self.linespace_font * fs)
			am = sp - 1
			bm = sp + 1
			lm = int(self.margine["left"]*mn)
			rm = int(self.margine["right"]*mn)
			ha = int(h * fs + (h-1) * sp + am + bm)
			wa = 0
			i = 0
			for linw in lnws:
				kw = int(linw*fs + (len(fontlist[i])-1)*self.widthpad + lm + rm)
				if kw > wa:
					wa = kw
				i += 1
		enoffset = []
		hp = am
		for lnw in lnws:
			enoffset.append((lm, hp))
			hp += fs + sp
		return {"s":	fs		,
				"a":	(wa, ha),
				"o":	enoffset,
				"f":	fontlist}

class Thread:
	#コメントを複数個含めることのできるスレッド。
	#サーバーから戻ってくるコメントのリストはこれで分割される
	def __init__(self, ops=None):
		self.comments = []
		self.num_clicks = []
		if ops != None:
			self.thread_id = ops["thread"]
			if "last_res" in ops:
				self.last = ops["last_res"]
			else:
				self.last = None
			self.time = ops["server_time"]
			if ("fork" in ops) and (int(ops["fork"]) != 0):
				self.owner = True
			else:
				self.owner = False
	
	def add(self, comment):
		#コメントを追加するが、同じ番号のものは上書きする。
		#ただし番号0の場合は上書きしない(番号不明として処理)
		if int(comment["no"]):
			for i in range(len(self.comments)):
				if self.comments[i]["no"] == comment["no"]:
					self.comments[i] = comment
					return
		self.comments.append(comment)
	
	def add_num_click(self, num_click):
		for i in range(len(self.num_clicks)):
			if self.num_clicks[i]["no"] == num_click["no"]:
				self.num_clicks[i] = num_click
				return
		self.num_clicks.append(num_click)

	def __str__(self):
		ret = ""
		for c in self.comments:
			ret += c["no"] + ": " + c["*"] + "\n"
		return ret

class VirtualScreen:
	#ニコニコ動画のコメント描画領域をシュミレーションするためのClass
	#時間に対するコメントの動きをシミュレーションする。
	def __init__(self, preset=None):
		self.comments = []
		if preset == "4:3":
			self.font_size_decider = StandardFontSizeDecider()
			self.screen_x = 544
			self.screen_y = 385
			self.screen_offset_x = -16
			self.screen_offset_y = 0
			self.nakaspeedbase = 544
			self.movie_x = 512
			self.movie_y = 384
			self.fontname = "ＭＳ Ｐゴシック"
		elif preset == "16:9":
			self.font_size_decider = StandardFontSizeDecider()
			self.screen_x = 672
			self.screen_y = 385
			self.screen_offset_x = -16
			self.screen_offset_y = -11
			self.nakaspeedbase = 544
			self.movie_x = 640
			self.movie_y = 360
			self.fontname = "ＭＳ Ｐゴシック"
		else:
			self.font_size_decider = None
			self.screen_x = None
			self.screen_y = None
			self.screen_offset_x = None
			self.screen_offset_y = None
			self.nakaspeedbase = None
			self.movie_x = None
			self.movie_y = None
			self.fontname = None

	def get_ass(self):
		ass = """[Script Info]
Title: ニコニコ動画コメント字幕
Original Script: nicovideo-tools
Original Timing: nicovideo-tools
ScriptType: v4.00+
Collisions: Normal
ScaledBorderAndShadow: Yes
PlayResX: {}
PlayResY: {}
Timer: 100.0000
WrapStyle: 0

[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour,
 BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle,
 BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,{},24,&H22FFFFFF,&H55000000,&H55000000,&H55000000,0,0,0,0,
  100,100,0,0.00,1,1,0,7,20,20,10,128

[Events]
Format: Layer, Start, End, Style, Actor, MarginL, MarginR, MarginV, Effect, Text
"""
		ass = ass.format(self.movie_x, self.movie_y, self.fontname)
		#self.font_size_decider
		log = [{"ue": [], "naka": [], "shita": []},
		       {"ue": [], "naka": [], "shita": []}]
		asslines = [[], []]
		for comment in self.comments:
			commands = Commands(comment)
			sizetable = self.font_size_decider.get(comment, commands)
			assdata = {}
			assdata["actor"] = "no={}:user_id={}:date={}:vpos={}:mail={}"\
				.format(comment["no"], comment["user_id"], comment["date"],\
						comment["vpos"], comment["mail"])
			assdata["actor"] = assdata["actor"].replace(",", "")
			if commands.flag_invisible:
				assdata["type"] = "Comment"
			else:
				assdata["type"] = "Dialogue"
			assdata["sizeinfo"] = sizetable
			assdata["color"] = commands.color[4:6] + commands.color[2:4] + commands.color[0:2]
			assdata["text"] = comment["*"]
			if commands.position == "ue":
				while True:
					for i in range(len(log[comment["fork"]]["ue"])):
						if log[comment["fork"]]["ue"][i]["goneat"]\
								<= comment["vpos"]:
							del log[comment["fork"]]["ue"][i]
							break
					else:
						break
				atop = 0
				abottom = 0
				if self.screen_y > sizetable["a"][1]:
					while True:
						abottom = atop + sizetable["a"][1] - 1
						for lg in log[comment["fork"]]["ue"]:
							if lg["top"] <= abottom and lg["bottom"] >= atop:
								atop = lg["bottom"] + 1
								break
						else:
							break
					if abottom >= self.screen_y:
						atop = random.randint(0,self.screen_y-sizetable["a"][1])
						abottom = atop + sizetable["a"][1] - 1
				else:
					atop = -random.randint(0,sizetable["a"][1]-self.screen_y)
					abottom = atop + sizetable["a"][1] - 1
				assdata["start"] = comment["vpos"]
				assdata["end"] = comment["vpos"] + commands.time
				lpos = int((self.screen_x-sizetable["a"][0]) / 2)
				assdata["pos"] = (lpos, atop)
				log[comment["fork"]]["ue"].append({
						"top":		atop,
						"bottom":	abottom,
						"goneat":	comment["vpos"] + commands.time})
			elif commands.position == "naka":
				while True:
					for i in range(len(log[comment["fork"]]["naka"])):
						if log[comment["fork"]]["naka"][i]["goneat"]\
								<= comment["vpos"]-100:
							del log[comment["fork"]]["naka"][i]
							break
					else:
						break
				atop = 0
				abottom = 0
				speed = (self.nakaspeedbase+sizetable["a"][0]) / \
						(commands.time+100)
				startat = comment["vpos"] - 100
				startat -= (self.screen_x-self.nakaspeedbase) / 2 / speed
				startat = int(startat + 0.5)
				goneat = comment["vpos"] + commands.time
				goneat += (self.screen_x-self.nakaspeedbase) / 2 / speed
				goneat = int(goneat + 0.5)
				leave_r = int(startat + sizetable["a"][0] / speed + 0.5)
				arrive_l = int(goneat - sizetable["a"][0] / speed + 0.5)
				if self.screen_y > sizetable["a"][1]:
					while True:
						abottom = atop + sizetable["a"][1] - 1
						for lg in log[comment["fork"]]["naka"]:
							if lg["top"] <= abottom and \
							lg["bottom"] >= atop and \
							(lg["startat"] <= goneat and \
							lg["goneat"] >= startat) and \
							(lg["lr"] >= startat or \
							lg["goneat"] >= arrive_l):
								atop = lg["bottom"] + 1
								break
						else:
							break
					if abottom >= self.screen_y:
						atop = random.randint(0,self.screen_y-sizetable["a"][1])
						abottom = atop + sizetable["a"][1] - 1
				else:
					atop = -random.randint(0,sizetable["a"][1]-self.screen_y)
					abottom = atop + sizetable["a"][1] - 1
				assdata["start"] = startat
				assdata["end"] = goneat
				assdata["pos"] = (self.screen_x, atop)
				assdata["move"] = (-sizetable["a"][0], atop)
				log[comment["fork"]]["naka"].append({
						"top":		atop,
						"bottom":	abottom,
						"goneat":	goneat,
						"startat":	startat,
						"lr":		leave_r,
						"al":		arrive_l})
			elif commands.position == "shita":
				while True:
					for i in range(len(log[comment["fork"]]["shita"])):
						if log[comment["fork"]]["shita"][i]["goneat"]\
								<= comment["vpos"]:
							del log[comment["fork"]]["shita"][i]
							break
					else:
						break
				abottom = self.screen_y - 1
				atop = 0
				if self.screen_y > sizetable["a"][1]:
					while True:
						atop = abottom - sizetable["a"][1] + 1
						for lg in log[comment["fork"]]["shita"]:
							if lg["top"] <= abottom and lg["bottom"] >= atop:
								abottom = lg["top"] - 1
								break
						else:
							break
					if atop < 0:
						atop = random.randint(0,self.screen_y-sizetable["a"][1])
						abottom = atop + sizetable["a"][1] - 1
				else:
					atop = -random.randint(0,sizetable["a"][1]-self.screen_y)
					abottom = atop + sizetable["a"][1] - 1
				assdata["start"] = comment["vpos"]
				assdata["end"] = comment["vpos"] + commands.time
				lpos = int((self.screen_x-sizetable["a"][0]) / 2)
				assdata["pos"] = (lpos, atop)
				log[comment["fork"]]["shita"].append({
						"top":		atop,
						"bottom":	abottom,
						"goneat":	comment["vpos"] + commands.time})
			asslines[comment["fork"]].append(assdata)

		def to_asstime(time):
			if time < 0:
				time = 0
			h = int(time / 360000)
			time %= 360000
			m = int(time / 6000)
			time %= 6000
			s = int(time / 100)
			time %= 100
			return "{:0>1}:{:0>2}:{:0>2}.{:0>2}".format(h, m, s, time)

		def to_asstext(tex):
			#通常の文字列をASSで問題なく表示されるようにする。
			#すべてのバックスラッシュ(環境によっては通貨記号)を
			#よく似た文字である∖(U+2216 SET MINUS)に置き換えて改行を\Nにする。
			#また、ArialにはU+2216のグリフが存在しないため、フォントを変える
			#ちなみに、ニコニコ上では日本語winでも\はバックスラッシュである
			#バックスラッシュ置換候補:
			#U+2216 SET MINUS						これで妥協
			#U+27CD MATHEMATICAL FALLING DIAGONAL	フォントにグリフが無い
			#U+29F5 REVERSE SOLIDUS OPERATOR		フォントにグリフが無い
			#U+29F9 BIG REVERSE SOLIDUS				フォントにグリフが無い
			#U+FF3C FULLWIDTH REVERSE SOLIDUS		完全に全角なので却下
			#と、思ったけど、フォントArialだからどっちにしてもグリフないや。
			#だいぶ無理矢理な方法なので何とかしたいが、
			#ASSの仕様にはメタ文字のエスケープについて見当たらなかった。
			ret = tex.replace("\\", "\u2216")
			ret = ret.replace("\n", "\\N")
			return ret

		asslayer = 1
		for layer in [1, 0]:
			layerdata = asslines[layer]
			layerdata = sorted(layerdata, key=lambda item: item["start"])
			for com in layerdata:
				fontinfo = com["sizeinfo"]["f"]
				linen = 0
				for line in com["text"].split("\n"):
					offset = com["sizeinfo"]["o"][linen]
					offset = (offset[0]+self.screen_offset_x,
					          offset[1]+self.screen_offset_y)
					ass += com["type"] + ": "
					ass += str(asslayer) + ","
					ass += to_asstime(com["start"]) + ","
					ass += to_asstime(com["end"]) + ","
					ass += "Default,"
					ass += com["actor"] + ","
					ass += "0,0,0,,"
					ass += "{\\an7}{\\org(0,0)}"
					ass += "{\\fs" + str(com["sizeinfo"]["s"]) + "}"
					ass += "{\\b1}"
					ass += "{\\bord1}"
					ass += "{\\be1}"
					ass += "{\\c&H" + com["color"] + "&}"
					if "move" in com:
						ass += "{\\move("
						ass += str(com["pos"][0] + offset[0]) + ", "
						ass += str(com["pos"][1] + offset[1]) + ", "
						ass += str(com["move"][0] + offset[0]) + ", "
						ass += str(com["move"][1] + offset[1])
						ass += ")}"
					else:
						ass += "{\\pos("
						ass += str(com["pos"][0] + offset[0]) + ", "
						ass += str(com["pos"][1] + offset[1])
						ass += ")}"
					fontinfo_line = fontinfo[0]
					currentfont = None
					line = to_asstext(line)
					for char in line:
						if currentfont != fontinfo_line[0]:
							currentfont = fontinfo_line[0]
							ass += "{\\fn" + currentfont  + "}"
						ass += char
						fontinfo_line = fontinfo_line[1:]
					ass += "\n"
					fontinfo = fontinfo[1:]
					linen += 1
				ass += "Comment" + ": "
				ass += "area={}-{}".format(com["sizeinfo"]["a"][0], \
										com["sizeinfo"]["a"][1])
				ass += "\n"
				asslayer += 1
		return ass

	def add_comment(self, comment):
		comment = comment.copy()
		for key in ["fork", "vpos", "no", "date"]:
			if key in comment:
				comment[key] = int(comment[key])
			else:
				comment[key] = 0
		for key in ["user_id", "mail"]:
			if not key in comment:
				comment[key] = ""
		comments = self.comments
		ind = 0
		for i in range(len(comments)):
			if comments[i]["vpos"] < comment["vpos"]:
				ind = i + 1
			elif comments[i]["vpos"] == comment["vpos"] \
			and  comments[i]["no"] < comment["no"]:
				ind = i + 1
		comments.insert(ind, comment)

	def add_thread(self, thread):
		self.add(thread.comments)

	def add(self, obj):
		if isinstance(obj, dict):
			#単体のコメント
			self.add_comment(obj)
		elif isinstance(obj, list):
			if len(obj) == 0:
				pass
			elif isinstance(obj[0], dict):
				#コメントのリスト
				for com in obj:
					self.add_comment(com)
			else:
				#スレッドのリスト
				for th in obj:
					self.add_thread(th)
		else:
			#単体のスレッド
			self.add_thread(obj)

def interpret_xml(xml):
	#xmlがstrまたはbytesならコメントXMLとして解釈し、
	#それ以外ならファイルオブジェクトとして処理します。
	if not(isinstance(xml, str) or isinstance(xml, bytes)):
		xml = xml.read()
	if isinstance(xml, bytes):
		#XML宣言から文字コードを読み取り、デコードする
		#ただし、1byte目が0x00の場合、UTF-16であると仮定してデコードする
		#また、それ以外の場合でXML宣言が検出できなければ
		#UTF-8であるとしてデコードする
		#まあ、渡されるのはUTF-8のXML宣言あり以外考えられないが。
		if xml[0] == 0:
			xml = xml.decode("UTF-16", "replace")
		else:
			code = "UTF-8"
			if xml.find(b"<") == xml.find(b"<?xml "):
				index = xml.find(b"<")
				end = xml.find(b"?>", index)
				head = xml[index+len(b"<?xml "):end].decode("ascii", "replace")
				for att in head.split(" "):
					if att == "":
						continue
					sp = att.split("=", 1)
					if sp[0] == "encoding":
						code = sp[1][1:len(sp)-1]
			if code.strip() == "":
				code = "UTF-8"
			xml = xml.decode(code, "replace")
	if xml.find("<") == xml.find("<?xml "):
		stb = xml.find(">")
		xml = xml[stb+1:]
	#コメントXMLの仕様について(研究結果,正確さについて保証なし)
	#root要素としてpacketがある。これについては特に気にしなくていい
	#packetの中にリクエストしたthreadが入っているが、
	#各threadに属するコメントはthread要素の内容としてではなく
	#threadと同じ階層にあるchat要素として現れる
	#chatがどのスレッドに属するかはchatの属性であるthreadを見てもいいが
	#それではthread番号が同じ場合(視聴者コメントと投稿者コメントなど)
	#どちらに属しているのか他のところを参照する必要があり、
	#ややこしいが、どうやら各スレッドの終わりに
	#resultcode=11のthreadがあるっぽいのでこれを使用。
	#ついでに書いておくと、真面目にXMLを展開する気はあんまりない。
	#だからコメントAPIの仕様変更があったら崩壊だね!!

	#<packet>の中身を取り出す
	xml = xml.strip()
	if xml[: len("<packet>")] != "<packet>" or \
	   xml[-len("</packet>") :] != "</packet>":
		raise err.InterpretErr("comment xml")
	buf = xml[len("<packet>") : -len("</packet>")]

	threads = []
	active_thread = -1
	view_counter = None

	#此処から先、いろいろカオスに付き注意
	#「もっと整理しろよ!!」「めんどくさい」
	state = "default"
	add = None
	buftop = 0
	while len(buf) > buftop:
		if state == "default":
			if buf[buftop] == "<":
				state = "<"
			elif buf[buftop].isspace():
				pass
			else:
				#おかしい。けど無視
				pass
			buftop += 1
		elif state == "<":
			if buf[buftop] == "/":
				state = "</"
				buftop += 1
			else:
				index = buf.find(" ", buftop)
				add = {"#": buf[buftop:index]}
				buftop = index + 1
				state = "<*"
		elif state == "</":
			buftop = buf.find(">", buftop) + 1
			if add["#"] == "chat":
				del add["#"]
				threads[active_thread].add(add)
			else:
				#知らないタグがあったようだ。スルー。
				pass
			state = "default"
		elif state == "<*":
			if buf[buftop].isspace():
				buftop += 1
			elif buf[buftop] == ">":
				state = "<*>"
				buftop += 1
			elif buf[buftop:buftop+2] == "/>":
				buftop += 2
				state = "default"
				if add["#"] == "thread":
					if int(add["resultcode"]) == 0:
						for thn in range(len(threads)):
							th = threads[thn]
							if th.thread_id == add["thread"] and \
							th.owner == ("fork" in add and int(add["fork"])):
								active_thread = thn
								break
						else:
							threads.append(Thread(add))
							active_thread = len(threads) - 1
					else:
						active_thread = -1
				elif add["#"] == "view_counter":
					del add["#"]
					view_counter = add
				elif add["#"] == "num_click":
					del add["#"]
					threads[active_thread].add_num_click(add)
			else:
				index = buf.find("=", buftop)
				name = buf[buftop:index]
				buftop = index + 2
				index = buf.find("\"", buftop)
				add[name] = tools.xml_unref(buf[buftop:index])
				buftop = index + 1
		elif state == "<*>":
			index = buf.find("<", buftop)
			add["*"] = tools.xml_unref(buf[buftop:index])
			buftop = index
			state = "default"
		else:
			raise "unknown state " + state
	if view_counter:
		comment_inf = view_counter
		comment_inf["threads"] = threads
		return comment_inf
	else:
		raise err.InterpretErr("コメントファイルが必要な情報を含んでいません")
