require 'darkhall/action'

module DarkHall

	class Enemy < Character
		def decide_action(&proc)
			set_action(EnemyAIContext.decide(self, &proc))
		end
	
		def action_static
			nil
		end
	
		def action_beast
			decide_action do
				short_attack 1
			end
		end
		
		
		def action_vine
			decide_action do
				short_attack 75
				special 25, short_targeting, 'VineBind'
				if find_enemy('Bandit') or find_enemy('StraySorcerer') then
					special 25, short_targeting, 'VineBind'
				end
			end
		end
		
		def action_stray_sorcerer
			decide_action do
				spell_cast 50, long_targeting, 'IceBolt'
				spell_cast 30, long_targeting, 'SparkShot'
				attack 20
			end
		end
		
		def action_malor_jin
			decide_action do
				spell_cast 40, long_targeting, 'SparkShot'
				spell_cast 20, group_targeting, 'FlameDasher'
				special 20, group_targeting, 'SwordDance'
				spell_cast 5, @actor, 'HealingHand'
				attack 30
			end
		end

		
		def action_demonic_priest
			decide_action do
				damaged_enemies = $troop.all_enemies.find_all{|x| x.hp < x.hp_max}
				unless damaged_enemies.empty? then
					spell_cast 60, Util.random_pick(damaged_enemies), 'HealingHand'
				end
				
				member_number = GS.party.alive_members.size
				terror_member_number = GS.party.alive_members.find_all{|x| x.find_state(TerrorState)}.size
				
				if (terror_member_number.to_f / member_number.to_f) < 0.5 then
					spell_cast 35, group_targeting, 'Terror'
				end
				
				attack 5
			end
		end

		
		def action_pixie
			decide_action do
				spell_cast 30, group_targeting, 'Sleep' 
				wait 70
			end
		end


		
		def action_spirit
			decide_action do
				short_attack 30
				special 20, long_targeting, 'SpiritFire'
				special 25, long_targeting, 'SpiritCurse'
				wait 25
			end
		end
		
		def action_ghost
			decide_action do
				special 40, long_targeting, 'GhostHand'
				special 60, long_targeting, 'GhostEye'
			end
		end
		
		def action_imp
			decide_action do
				short_attack 70
				special 30, long_targeting, 'ImpFire'
			end
		end
		
		def action_kobolt_leader
			decide_action do
				if find_enemy('KoboltWorker') then
					special 1, nil, 'KoboltLeaderImperium'
				else
					short_attack 1
				end
			end
		end


		def action_bird
			decide_action do
				short_attack 60
				long_attack 40
			end

		end
		
		def action_slime
			decide_action do
				short_attack 40
				special 20, short_targeting, 'SlimePoison'
				special 15, short_targeting, 'SlimeCorrosion'
				wait 25
			end
		end
		
		def action_blazing_storm
			decide_action do
				short_attack 30
				long_attack 20
				special 50, group_targeting, 'HeatBless'
			end
		end

		def action_freezing_storm
			decide_action do
				short_attack 30
				long_attack 20
				special 50, group_targeting, 'ColdBless'
			end
		end

		
		def action_ruin_feather
			decide_action do
				member_actions = $battle.last_round_operated_actions.find_all{|x| x.actor.kind_of?(Member)}
				spell_attacks = member_actions.find_all{|x|
					x.kind_of?(SpellCastAction) and
					x.get_effect.kind_of?(AttackEffect) and
					x.get_effect.damaging?
				}
				
				if round % 4 == 3 then
					# 3ラウンド目, およびそれ以降4ラウンドごとにかまいたちを発動
					special 100, group_targeting, 'FeatherSlasher'

				elsif GS.rule.advanced_enemy_ai? and $troop.groups.size <= 2 then
					# ハードルールでは仲間を呼ぶ
					special 100, [@actor], 'RuinFeatherCall'
				elsif (chg = member_actions.find{|x| x.kind_of?(ReleaseArrowAction)}) and chg.actor.alive? then
					# チャージ攻撃を受けた場合、確実にその弓闘士を狙いにいく
					attack 100, [chg.actor]
				elsif @actor.hp_rate <= 0.3 and not @actor.find_state(BodyAccelerationState) and
				@actor.usable_mp?(DB.find_trick('BodyAcceleration').mp_cost, 'Acceleration') then
					# ダメージが大きくなると加速の呪文を使う
					spell_cast 100, [@actor], 'BodyAcceleration'
				elsif not spell_attacks.empty? then
					# ダメージを伴う攻撃術を受けた場合、遠距離攻撃の確率が高くなる
					long_attack 50
					special 50, group_targeting, 'FeatherSlasher'
				else
					# 通常のパターン
					if @actor.back? then
						long_attack 100
					else
						double_ranpage 30
						short_attack 40
						long_attack 30
						
					end
					#special 25, group_targeting, 'FeatherSlasher'
				end
			
			end
		end
		
		def action_lost_armor
			decide_action do
				proc_on_special = Proc.new{
					@actor.ai_memory[:last_special] = round
				}
			
				if @actor.ai_memory.empty? then
					@actor.ai_memory[:last_special] = nil
					@actor.ai_memory[:used_curse?] = false
				end
				
				if (round <= 2 + rand(2) or @actor.hp_rate >= 0.8) and not GS.rule.advanced_enemy_ai? then
					# 最初のうちは通常攻撃のみ（ハードルールでは、このパターンは省略）
					attack 100
					
				elsif not @actor.ai_memory[:used_curse?] and @actor.hp_rate < 0.4 then
				
					# HPが減少したら優先的に呪い発動
					special(100, group_targeting, 'LostArmorCurse'){
						proc_on_special.call
						@actor.ai_memory[:used_curse?] = true
					}
					
				else
				
					# 通常の行動パターン（特技を連続で使う確率は低い）
					if (last = @actor.ai_memory[:last_special]) then
						interval = round - last
					else
						interval = 5
					end
					
					if rand(100) < [interval * (GS.rule.advanced_enemy_ai? ? 40 : 15), 75].min then
						# 特技を使う
						if @actor.hp_rate < 0.4 and group_targeting.find_all{|x| x.find_state(CurseState)}.size <= 2 then
							special(100, group_targeting, 'LostArmorCurse', &proc_on_special)
						else
							ranpage 50, &proc_on_special
							smash 50, &proc_on_special
						end
						double_ranpage 50, &proc_on_special if GS.rule.advanced_enemy_ai?
					else
						# 特技を使わない
						attack 100
					end
				end
			end
		end
		

	end



	class EnemyAIContext
		Candidate = Struct.new(:rate, :action, :on_choice)
		
		def self.decide(actor, &block)
			context = self.new(actor)
			context.instance_eval(&block)
			
			return context.pick
		end
		
		def initialize(actor)
			@actor = actor
			@candidates = []
			@rate_total = 0
		end
		
		def pick
			value = rand(@rate_total)
			
			start = 0
			# 一つ一つの候補を順にチェック
			@candidates.each do |cand|
				if value < start + cand.rate then
					cand.on_choice.call if cand.on_choice
					return cand.action 
				end
				
				start += cand.rate
			end
			
			# どのアクションも選択されなければnil
			return nil
		end
		
		private
		
		def round
			$battle.round
		end
		
		def find_enemy(enemy_id)
			$troop.all_enemies.find{|x| x.data_id == enemy_id}
		end
		
		def attack(rate, targets = short_targeting, &on_choice)
			add_candidate(rate, AttackAction.new(@actor, targets), &on_choice)
		end
		
		def ranpage(rate, targets = short_targeting, &on_choice)
			add_candidate(rate, RanpageAction.new(@actor, targets), &on_choice)
		end
		
		def double_ranpage(rate, targets = double_short_targeting, &on_choice)
			add_candidate(rate, DoubleRanpageAction.new(@actor, targets), &on_choice)
		end

		
		def smash(rate, targets = short_targeting, &on_choice)
			add_candidate(rate, SmashAction.new(@actor, targets), &on_choice)
		end
		
		def guard(rate, &on_choice)
			add_candidate(rate, GuardAction.new(@actor), &on_choice)
		end


		# obsolete
		def short_attack(rate, &on_choice)
			attack(rate, &on_choice)
		end

		
		def long_attack(rate, &on_choice)
			attack(rate, long_targeting, &on_choice)
		end
		
		def spell_cast(rate, targets, trick_id, &on_choice)
			trick = DB.find_trick(trick_id)
			if @actor.mp[trick.spell_id] and @actor.mp[trick.spell_id] >= trick.mp_cost then
				add_candidate(rate, SpellCastAction.new(@actor, targets, trick_id), &on_choice)
			else
				nil
			end
		end
		
		def special(rate, targets, effect_id, &on_choice)
			add_candidate(rate, SpecialAction.new(@actor, targets, effect_id.to_s), &on_choice)
		end
		
		def wait(rate, &on_choice)
			add_candidate(rate, nil, &on_choice)
		end





		def add_candidate(rate, action, &on_choice)
			@candidates << Candidate.new(rate, action, on_choice)
			@rate_total += rate
		end
		

		
		def short_targeting
			if @actor.forward? then
				targets = GS.party.forward_members.find_all{|x| x.alive?}
				blockers = targets.find_all{|x| x.blocking?}
				targets = blockers unless blockers.empty?
				return [Util.random_pick(targets)]
			else
				return []
			end
		end
		
		def long_targeting
			targets = GS.party.standing_members
			blockers = targets.find_all{|x| x.blocking?}
			targets = blockers unless blockers.empty?
			return [Util.random_pick(targets)]
		end
		
		def double_short_targeting
			short_targeting + short_targeting
		end
		
		def double_long_targeting
			long_targeting + long_targeting
		end

		
		def group_targeting
			GS.party.standing_members
		end
	end

	

end