require 'gettext'

module DarkHall

	class Database
		include GetText
		attr_reader :treasure_table
		attr_reader :attack_message_table
		
	
		def self.load
			self.new.load
		end

		def initialize(dir_path = './data/')
			@dir_path = Pathname.new(dir_path)
			@models = {:spells => [], :tricks => [], :items => [], :enemies => []}
			@cache_table = {:spells => {}, :tricks => {}, :items => {}, :enemies => {}}
			@attack_message_table = {}
			@enemy_sign_message_table = {}
			@item_message_table = {}
			@treasure_table = {}
			@special_action_model_table = {}
		end
		
		public
		def load
		
			Game.timelog('load item DB'){
				c = ItemContext.new
				c.load_data_file(@dir_path, 'weapon')
				c.load_data_file(@dir_path, 'armor')
				c.load_data_file(@dir_path, 'accessory')
				c.load_data_file(@dir_path, 'item')
				@models[:items] = c.models
			}
			
			Game.timelog('load spell DB'){
				c = SpellContext.new
				c.load_data_file(@dir_path, 'spell')
				@models[:spells] = c.models
	
				# 呪文書のモデルを自動生成
				spells.each do |spell|
					if spell.book_rank then
						model = SpellBookModel.new("SpellBookOf#{spell.id}")
						model.name = _("%{spell}の呪文書").evaluate(:spell => spell.short_name)
						model.description = _("%{spell}を身につけるために必要な書物").evaluate(:spell => spell.name)
						model.spell_id = spell.id
						@models[:items] << model
					end
				end
				
				c = TrickContext.new
				c.load_data_file(@dir_path, 'trick')
				@models[:tricks] = c.models
			}
			
			
			Game.timelog('load special action DB'){
				c = SpecialActionContext.new
				c.load_data_file(@dir_path, 'special_action')
				@special_action_model_table.clear
				c.models.each do |model|
					@special_action_model_table[model.id] = model
				end
			}
			
			Game.timelog('load enemy DB'){
				c = EnemyContext.new
				c.load_data_file(@dir_path, 'enemy')
				@models[:enemies] = c.models
			}
			
			Game.timelog('load message DB'){
				c = AttackMessageContext.new
				c.load_data_file(@dir_path, 'message/attack')
				@attack_message_table = c.message_table
	
				c = ItemMessageContext.new
				c.load_data_file(@dir_path, 'message/item')
				@item_message_table = c.message_table
	
				c = EnemySignMessageContext.new
				c.load_data_file(@dir_path, 'message/enemy_sign')
				@enemy_sign_message_table = c.message_table
			}
				
			
			
			# 宝箱テーブル生成
			Game.timelog('set treasure table'){
				founds = {}
				[TRT::WEAPON, TRT::ARMOR, TRT::ACCESSORY, TRT::ITEM, TRT::POTION].each do |type|
					founds[type] = items.find_all{|x| x.treasure_type == type and x.treasure_rank}
				end
				
				@treasure_table.clear
				founds.each_pair do |type, models|
					@treasure_table[type] = []
					models.each do |model|
						if model.treasure_rank then
							@treasure_table[type][model.treasure_rank] ||= []
							@treasure_table[type][model.treasure_rank] << model.id
						end
					end
				end
			}
			
			# 以下、後方互換のための処理
			ITEM_DATA.clear
			items.each do |model|
				ITEM_DATA[model.id] = model
			end

			SPELL_ORDER.clear
			SPELL_DATA.clear
			TRICK_DATA.clear
			ENEMY_DATA.clear

			spells.each do |model|
				SPELL_ORDER << model.id
				SPELL_DATA[model.id] = model
			end
			
			tricks.each do |model|
				TRICK_DATA[model.id] = model
			end
		
			spells.each do |model|
				model.trick_ids.each do |trick_id|
					TRICK_DATA[trick_id].spell_id = model.id
				end
			end
			
			enemies.each do |model|
				ENEMY_DATA[model.id] = model
			end

		
			return self
		end
		
		def items
			@models[:items]
		end
		


		
		def find_item(id)
			find_model(:items, id)
		end
		
		def spells
			@models[:spells]
		end
		
		def find_spell(id)
			find_model(:spells, id)
		end

		def tricks
			@models[:tricks]
		end
		
		def find_trick(id)
			find_model(:tricks, id)
		end
		
		def find_special_action(id)
			@special_action_model_table[id]
		end

		
		def enemies
			@models[:enemies]
		end
		
		def find_enemy(id)
			find_model(:enemies, id)
		end
		
		def pick_item_message(message_type, to_other)
			unless @item_message_table.include?(message_type) then
				message_type = :Common
			end
			
			list = @item_message_table[message_type][(to_other ? :other : :oneself)]
			return Util.random_pick(list)
		end

		
		def pick_enemy_sign_message(enemy_id)
			unless @enemy_sign_message_table.include?(enemy_id) then
				enemy_id = 'Common'
			end
			
			list = @enemy_sign_message_table[enemy_id]
			return Util.random_pick(list)
		end

		
		
		private
		def find_model(category, id)
			if @cache_table[category][id] then
				@cache_table[category][id]
			else
				re = @models[category].find{|x| x.id == id}
				@cache_table[category][id] = re
				
				re
			end
		end
		
		
		


		
		
		class ScriptContext
			def load_data_file(dir_path, feature_name)
				encoded_src_path = Pathname.new(dir_path) + (feature_name + '.dat')
				plain_src_path = Pathname.new(dir_path) + (feature_name + '.rb')
				
				if encoded_src_path.readable? then
					self.instance_eval(Util.decrypt(encoded_src_path.read), encoded_src_path.cleanpath.to_s)
				else
					self.instance_eval(plain_src_path.read, plain_src_path.cleanpath.to_s)
				end
				
				reset_state
				
				return self
			end
			
			def reset_state
			end
		end
		
		
		class ModelScriptContext < ScriptContext
			attr_reader :models
			
			def initialize
				@models = []
				@enemy_only = false
			end

			private
			def add(model_class, id, &proc)
				model = model_class.new(id.to_s)
				model.enemy_only = true if @enemy_only 
				if proc then
					model.instance_eval(&proc)
				end
				@models << model
			end
			
			def enemy_only
				@enemy_only = true
			end
			
			def reset_state
				super
				@enemy_only = false
			end
			

		end

		class SpellContext < ModelScriptContext
			private
			def spell(id, trick_ids, &proc)
				add(SpellModel, id){
					@trick_ids = trick_ids.map{|x| x.to_s}
					instance_eval(&proc)
				}
				
			end
		end
		
		class TrickContext < ModelScriptContext
			
			def trick(id, &proc)
				add(SpellTrickModel, id, &proc)
			end
		end
		
		class SpecialActionContext < ModelScriptContext			
			def special_action(id, &proc)
				add(SpecialActionModel, id, &proc)
			end
		end

		
		class ItemContext < ModelScriptContext
			
		end

		class EnemyContext < ModelScriptContext
			
			def enemy(id, &proc)
				add(EnemyModel, id, &proc)
			end
		end
		
		
		class MessageContext < ScriptContext
			include GetText
			attr_reader :message_table
		
			def initialize
				super
				@message_table = {}
			end
		
		end

		
		class AttackMessageContext < MessageContext
			def weapon(type, &proc)
				@current_type = type
				@message_table[type] ||= {:attack => [], :ranpage => [], :smash => [], :sneak_attack => [], :sharp_attack => []}
				proc.call
				@current_type = nil
			end
			
			def on_attack(msg)
				@message_table[@current_type][:attack] << msg
			end
			
			def on_ranpage(msg)
				@message_table[@current_type][:ranpage] << msg
			end
			
			def on_smash(msg)
				@message_table[@current_type][:smash] << msg
			end
			
			def on_sneak_attack(msg)
				@message_table[@current_type][:sneak_attack] << msg
			end
			def on_sharp_attack(msg)
				@message_table[@current_type][:sharp_attack] << msg
			end
		end
		
		class ItemMessageContext < MessageContext
			def item(type, &proc)
				@current_type = type
				@message_table[type] ||= {:oneself => [], :other => []}
				proc.call
				@current_type = nil
			end
			
			def on_use(msg)
				on_use_to_oneself(msg)
				on_use_to_other(msg)
			end
			
			def on_use_to_oneself(msg)
				@message_table[@current_type][:oneself] << msg
			end
			
			def on_use_to_other(msg)
				@message_table[@current_type][:other] << msg
			end
			
		end
		
	
		class EnemySignMessageContext < MessageContext
			def enemy(type, &proc)
				@current_type = type
				@message_table[type] ||= []
				proc.call
				@current_type = nil
			end
			
			def on_sign(msg)
				@message_table[@current_type] << msg
			end
			
		end
		
	end


	DB = Database.new

end