#　セーブデータ
require 'anjson'
require 'pathname'
require 'benchmark'

require 'msgpack'
require 'yaml'

require 'darkhall/report'
	
module DarkHall


	class SaveData
		attr_reader :dir_path, :id		
		
		def initialize(dir_path, id)
			@dir_path = Pathname.new(dir_path)
			@id = id
		end
		
		def exist?
			file_path.exist?
		end
		
		def mtime
			file_path.mtime
		end
		
		def check_dir
			Dir.mkdir(@dir_path) unless @dir_path.exist?
		end
		
		def file_path
			@dir_path + "darkhall_save_#{@id}.dat"
		end
		
		def save(level = FQ_CHANGE, scene_title = '?')
			if level > GS.game_config.save_frequency_level then
				return false
			end
			
			check_dir
			
			LOGGER.puts "セーブデータ書き込み (#{level}:#{scene_title})"
			
			buf = {}
			
			
			
			dest_path = self.file_path
			
			# バックアップを取る
			if dest_path.exist? then
				FileUtils.mv(dest_path.to_s, dest_path.to_s + '.bak')
			end
			

			# MessagePack生成
			
			mpac = nil
			
			sec = Benchmark.realtime{
				mpac = GS.to_hash.to_msgpack
			}
			LOGGER.info(sprintf("\tserialize = %2.3g sec", sec))
			
			
			# 書き込み
			sec = Benchmark.realtime{
				open(dest_path, 'wb'){|f|
					f.write mpac
				}
			}
			LOGGER.info(sprintf("\twrite = %2.3g sec", sec))
			

			if Game.debug_mode? then
				
				# JSON書き込み
				sec = Benchmark.realtime{
					open(dest_path.to_s + '.json', 'w'){|f|
						f.write AnJSON.pretty_build(MessagePack.unpack(mpac))
					}
				}
				LOGGER.info(sprintf("\tjson serialize & write = %2.3g sec", sec))
			end
			
			
			LOGGER.info("\tcomplete.")
			LOGGER.puts
			
			Game.clear_event_pool
				
	
			return true
		end
		
		
		
		
		
		def load
			data = nil
			load_mpac_time = Benchmark.realtime{
				data = load_mpac_models
			}
			
			if data then
				# Rubyオブジェクトを生成
				objects = {}
				create_time = Benchmark.realtime{
					data.each_pair do |key, value|
						objects[key] = value.class.create_from_mpac_model(value)
					end
				}
				
				# ロード
				load_time = Benchmark.realtime{
					GS.load_from_hash(objects)
				}
				
				
				# ロード後の状態の検証
				check_time = Benchmark.realtime{
					begin
						GS.check_oneself
					rescue GameStateError
						dir_path = Pathname.new('./log/')
						dir_path.mkpath
						open(dir_path + "dumped_state_#{@id}.yml", 'w'){|f|
							mpac = GS.to_hash.to_msgpack
							f.write MessagePack.unpack(mpac).to_yaml
						}
						
						raise $!
					end
				}
				
	
				LOGGER.info "#{self.file_path} loaded."
				LOGGER.info sprintf(TIMELOG_FORMAT, "\tload mpac", load_mpac_time)
				LOGGER.info sprintf(TIMELOG_FORMAT, "\tcreate", create_time)
				LOGGER.info sprintf(TIMELOG_FORMAT, "\tload", load_time)
				LOGGER.info sprintf(TIMELOG_FORMAT, "\tcheck", check_time)
				
				Game.reporter = Reporter.new("./report/#{@id}/")
				Game.reporter.check_dir
			end

		end
		
		def load_mpac_models
			check_dir
			
			if self.file_path.exist? then
				mpac = nil
				open(self.file_path, 'rb'){|f|
					mpac = f.read
				}
				
				return MessagePack.unpack(mpac)
			else
				return nil
			end
		end
		


		
		
	end
	
	
	
	
	class StorableObject
		
		
		def dump
			{'_object_class' => self.class.name}
		end
		
		def to_msgpack(*args)
			self.dump.to_msgpack(*args)
		end
	end
	
	

end	
	
	

class Symbol

	def to_msgpack(*args)
		puts "[WARNING] #{self} - Symbol to MessagePack"
		self.to_s.to_msgpack(*args)
	end
end


primary_classes = [Numeric, NilClass, TrueClass, FalseClass, String]

primary_classes.each do |cls|
	def cls.create_from_mpac_model(base)
		base
	end
end



class Array
	def self.create_from_mpac_model(base)
		base.map{|x| x.class.create_from_mpac_model(x)}
	end
end
	
class Hash
	def self.create_from_mpac_model(base)
		oc = base.delete('_object_class')

		created_objects = {}
			
		base.each_pair do |key, value|
			created_objects[key] = value.class.create_from_mpac_model(value)
		end

		if oc then
			cls = eval(oc)
			re = cls.create_from_mpac_model(created_objects)
			
			re
		else
	
			created_objects
		end
	end
end
