#　パーティーのクラス定義
module DarkHall
	class Party < StorableObject

		attr_accessor :member_ids, :x, :y, :back_x, :back_y, :floor_id, :direction
		attr_accessor :encount_border, :encount_up_rest, :encount_down_rest
		attr_accessor :alarm_type
		
		def initialize
			@member_ids = []
			@x = 0
			@y = 0
			@direction = DIR_N
			@back_x = nil
			@back_y = nil
			@floor_id = nil
			self.reset_encount_border
			@encount_up_rest = 0
			@encount_down_rest = 0
			@alarm_type = nil
		end
		
		def encount_up(rest)
			@encount_up_rest = rest
			@encount_down_rest = 0
		end
		
		def encount_down(rest)
			@encount_down_rest = rest
			@encount_up_rest = 0
		end
		
		def floor
			(@floor_id ? FLOORS[@floor_id] : nil)
		end
		
		def in_dungeon?
			(@floor_id ? true : false)
		end
		
		def region
			return (self.floor ? self.floor.cells[@x][@y].region_id : nil)
		end
		alias region_id region
		
		def cell
			return (self.floor ? self.floor.cells[@x][@y] : nil)
		end
		
		def viewable_treasure_box
			(self.section_id && GS.room_treasure_boxes[@floor_id][self.section_id])
		end
		
		
		def members
			return @member_ids.map{|i| GS.members[i]}
		end
		
		def on_save
		end
		
		def alive?
			return !(alive_members.empty?)
		end
		
		def full?
			@member_ids.size >= MEMBER_MAX
		end

		
		def level
			total = 0
			
			if alive_members.empty? then
				return 0
			else
				alive_members.each{|x| total += x.level}
				return (total / alive_members.size).to_i
			end
		end

		def agl
			members.map{|x| x.agl}.min
		end

		
		# メンバー加入
		def join_member(id)
			if GS.members[id] && !(full?) then
				@member_ids << id
				self.update_formation
				return true
			else
				return false
			end
		end
		
		# メンバー離脱
		def leave_member(id)
			if @member_ids.size >= 1 then
				@member_ids.delete(id)
				self.update_formation
				return true
			else
				return false
			end

		end
		
		# 並び順やキャラクターの位置を更新する
		def update_formation
			self.members.each{|x| x.position = nil}
			members = self.standing_members
			number = members.size
			
			case number
			when 1, 2
				forward_number = 1
			when 3, 4
				forward_number = 2
			else
				forward_number = 3
			end
			
			members.each_index do |i|
				if i < forward_number then
					members[i].position = POS_FORWARD
				else
					members[i].position = POS_BACK
				end

			end
			
			
			#LOGGER.puts "隊列変更（前列は#{forward_number}人）"
		end
		
		def reset_encount_border
			@encount_border = ENCOUNT_BORDER_DEFAULT
		end
		
		def walk(dir)
			GS.player_profiles[PPT::Walk] += 1
			self.members.each do |member|
				member.profiles[MPT::Walk] += 1
				member.status.each do |state|
					state.on_walk(member)
				end
			end
		end
		
		def walk_to_wall
			case GS.game_config.wall_effect
			when GameConfig::WE_SHAKE
				SE.hit
				Game.shake_windows([DUNGEON_WINDOW])
			when GameConfig::WE_OUCH
				SE.hit
				Game.working(_('いてっ！')){
					Game.shake_windows([DUNGEON_WINDOW])
				}
			end
			
		end
		
		def forward_walk
			walk(@direction)
		end
		
		def left_walk
			walk(left_direction)
		end
		
		def right_walk
			walk(right_direction)
		end
		
		def back_walk
			walk(back_direction)
		end

		
		def back
			if @back_x and @back_y then
				@x = @back_x
				@y = @back_y
			end
		end
		
		
		
		def encount_border_up
			# 遭遇率上昇
			if self.floor.enemy_appear_on?(self.region) then
				if @encount_up_rest >= 1 then
					2.times do
						@encount_border -= 1 if rand(2) == 0  # 1/2で上昇×5
					end
				elsif @encount_down_rest >= 1 then
					self.reset_encount_border # 初期値固定（遭遇しない）
				else
					@encount_border -= 1 if rand(2) == 0 # 1/2で上昇
				end
			end
			
			
			# お香の効果時間
			if @encount_up_rest >= 1 then
				@encount_up_rest -= 1
			elsif @encount_down_rest >= 1 then
				@encount_down_rest -= 1
			end
		end

		
		def is_position?(x, y, direction = nil, floor_id = nil)
			return false unless x == @x
			return false unless y == @y
			return false if direction and @direction != direction
			return false if floor_id and @floor_id != floor_id
			return true
		end
		
		alias on? is_position?
		
		def set_position(x, y, direction = nil)
			@x = x
			@y = y
			@direction = direction if direction
		end
		
		def room_id
			section_id
		end
		
		def section_id
			self.floor.cells[@x][@y].section_id
		end
		
		def layering_parties
			layerings = []
			GS.dungeon_parties.each do |party|
				unless party.eql?(self) then
					layerings << party if party.is_position?(@x, @y, nil, @floor_id) # 方角はどっち向きでもよい
				end
				
			end
			
			return layerings
		end
		
		def loop_sequence
			@x = @x + self.floor.width if @x <= -1
			@y = @y + self.floor.height if @y <= -1
			@x = @x - self.floor.width if @x >= self.floor.width
			@y = @y - self.floor.height if @y >= self.floor.height
		end
		
		def next_point(dir)
			range = 1
			case dir
			when DIR_N
				return @x, (@y - range)
			when DIR_E
				return (@x + range), @y
			when DIR_S
				return @x, (@y + range)
			when DIR_W
				return (@x - range), @y
			end
		end
		
		def forward_point
			next_point(@direction)
		end
		
			
		def right_point
			next_point(right_direction)
		end
		
		def left_point
			next_point(left_direction)
		end
		
		
		def turn_right
			@direction = right_direction
		end
		
		def turn_left
			@direction = left_direction
		end
		
		def turn_back
			@direction = back_direction
		end
		
		def right_direction
			re = @direction + 1
			re = DIR_N if re > DIR_W
			
			re
		end
		
		def left_direction
			re = @direction - 1
			re = DIR_W if re < DIR_N
			
			re
		end

		def back_direction
			case @direction
			when DIR_N
				DIR_S
			when DIR_E
				DIR_W
			when DIR_S
				DIR_N
			when DIR_W
				DIR_E
			end
		end

		
		
		def alive_members
			self.members.find_all{|x| x.alive?}
		end
		
		def dead_members
			self.members.find_all{|x| x.dead?}
		end
		
		def standing_members
			members.find_all{|x| x.standing?}
		end
		
		def forward_members
			return self.alive_members.find_all{|x| x.forward?}.compact
		end
		
		def back_members
			return self.alive_members.find_all{|x| x.back?}.compact
		end
		
		def default_unlocker_index
			return members.index(alive_members.max{|a, b| a.skill_levels[SKILL::UNLOCK] <=> b.skill_levels[SKILL::UNLOCK]})
		end

		def default_trap_remover_index
			return members.index(alive_members.max{|a, b| a.skill_levels[SKILL::REMOVE_TRAP] <=> b.skill_levels[SKILL::UNLOCK]})
		end

		def find_member(member_cls)
			alive_members.find{|x| x.kind_of?(member_cls)}
		end

		def find_all_members(member_cls)
			alive_members.find_all{|x| x.kind_of?(member_cls)}
		end
		
		def prayer
			find_member(Prayer)
		end

		
		def name
			if self.members.empty? then
				'?' # 本来は到達しないはず
			elsif self.members.size <= 1 then
				self.members.first.name
			else
				_("%{first}達").evaluate(:first => members.first.name)
			end

		end
		
		def first_member
			self.alive_members.first
		end
		
		def gold
			g = 0
			self.members.each do |member|
				g += member.gold
			end
			return g
		end
		
		# パーティーで料金を支払う前に、その割り当て額を計算するためのメソッド
		def allocate_fees(loss)
			# キャッシュ
			members = self.members
			
			# 初期化
			loss_rest = loss
			fees = []
			members.each_with_index do |member, i|
				fees[i] = 0
			end
			
			if GS.party.gold >= loss_rest then
				# 各メンバーの負担額計算
				until loss_rest <= 0  do
					# 金を持つメンバーの所持金のうち、0ではない最低額を算出
					# 同時に金を持っているメンバーの番号を記録しておく
					min = 99999999
					money_member_indexies = []
					members.each_index do |i|
						g = (members[i].gold - fees[i])
						if g >= 1 then
							money_member_indexies << i
							if g < min then
								min = g
							end
						end
					end
					
					# 残った支払額をほぼ平等に支払えるなら、全員に支払い額を分割して終了
					# そうでない場合（金の足りなくなる者が出る場合）には
					# ひとまず金が残っている者全員で「全員が同じだけ出せる額」を出す
					if (min * money_member_indexies.size) >= loss_rest then
						allocation = (loss_rest / money_member_indexies.size).to_i  # 全員で支払う額
						surplus = loss_rest % money_member_indexies.size       # allocationを出した後の残額
						
						money_member_indexies.each do |i|
							fees[i] += allocation
							if surplus >= 1 then
								fees[i] += 1
								surplus -= 1
							end
						end
						break
						
					else
						money_member_indexies.each do |i|
							fees[i] += min
							loss_rest -= min
						end
					end
				end

				
				return fees
			else
				# 全員の所持金を合わせても足りない場合にはエラー
				raise "golds are too few - loss:#{loss}, gold:#{GS.party.gold}"
			end			
		end
		
		# パーティー全体で料金を支払う。
		# 引数は支払総額、もしくはallocate_feesで得た配列
		def lose_gold(fees_or_total_loss)
			if fees_or_total_loss.kind_of?(Array) then
				fees = fees_or_total_loss
			else
				fees = allocate_fees(fees_or_total_loss)
			end
			
			fees.each_with_index do |fee, i|
				members[i].lose_gold(fee)
			end
		end
		
		def gather_gold(member_index = $member_index)
			target = self.members[member_index]
		
			self.members.each_index do |i|
				next if i == member_index
				target.gold += self.members[i].gold
				self.members[i].gold = 0
			end
			
			Game.save(FQ_SMALL_CHANGE, "お金を集めた")
			PARTY_WINDOW.update_items.update
			msg = n_("お金を集めました\n%{member}は金貨%{gold}枚を持っています", "お金を集めました\n%{member}は金貨%{gold}枚を持っています", target.gold)

			msg = msg.evaluate(:member => target.name, :gold => target.gold)

			# ブロックが与えられなかった場合、自動でメッセージ表示
			if block_given? then
				yield(msg)
			else
				Game.message(msg)
			end

		end

		def share_gold
			total = 0
			given = 0
			GS.party.members.each do |member|
				total += member.gold
			end
			GS.party.members.each do |member|
				member.gold = (total / GS.party.members.size).to_i
				given += member.gold
			end
			(total - given).times do |i|
				GS.party.members[i].gold += 1
			end
			
			Game.save(FQ_SMALL_CHANGE, "お金を山分けした")
			PARTY_WINDOW.update
			
			per_member = GS.party.members.first.gold
			if (total - given) == 0 then
				# 割り切れた場合
				msg = n_("お金を山分けしました\n金貨を一人%{gold}枚ずつ持っています", "お金を山分けしました\n金貨を一人%{gold}枚ずつ持っています", per_member)
			else
				# 割り切れなかった場合
				msg = n_("お金を山分けしました\n金貨を一人約%{gold}枚ずつ持っています", "お金を山分けしました\n金貨を一人約%{gold}枚ずつ持っています", per_member)
			end

			# ブロックが与えられなかった場合は、自動でメッセージ表示
			msg = msg.evaluate(:gold => per_member)
			if block_given? then
				yield(msg)
			else
				Game.message(msg)
			end


		end
		
		def get_item(item)
			getter = self.members.find{|x| x.getable_item?(item)}
			if getter then
				getter.get_item(item)
				return getter
			else
				return nil
			end
		end
		
		def find_item_holder(item_symbol)
			members.find{|x| x.find_item(item_symbol)}
		end
		

		def dump
			hash = super
			hash['member_ids'] = @member_ids
			hash['x'] = @x
			hash['y'] = @y
			hash['back_x'] = @back_x
			hash['back_y'] = @back_y
			hash['direction'] = @direction
			hash['floor_id'] = @floor_id
			hash['encount_border'] = @encount_border
			hash['encount_up_rest'] = @encount_up_rest
			hash['encount_down_rest'] = @encount_down_rest
			hash['alarm_type'] = @alarm_type
			return hash
		end
		
		def self.create_from_mpac_model(hash)
			obj = self.new
			obj.member_ids = hash['member_ids']
			obj.x = hash['x']
			obj.y = hash['y']
			obj.back_x = hash['back_x']
			obj.back_y = hash['back_y']
			obj.direction = hash['direction']
			obj.floor_id = (hash['floor_id'] ? hash['floor_id'].to_s : nil)
			obj.encount_border = hash['encount_border'] || 100
			obj.encount_up_rest = hash['encount_up_rest']
			obj.alarm_type = hash['alarm_type']
			return obj
		end

	end
	
end
