#!/usr/bin/env ruby
# Manages locations

require 'optparse'
require 'json'
require 'fileutils'
require 'erb'

OPTIONS = {:builder => false, :port => 28000}

class LocationTpl
	def initialize(data)
		@data = data
	end

	def location
		@data
	end
	def get_binding
		binding()
	end
end

class LocationManager
	def initialize
		@data = JSON.parse(File.open("location_config.json").read)
		items = locations.sort{|a,b| a['id'] <=> b['id']}
		@last_id = items.empty? ? 10000 : items.last['id']
		@data['locations'] ||= []
	end

	def locations
		@data['locations']||[]
	end

	def get_terrain_name(ident, builder)
		return ("onverse/data/live_assets/engine/terrain/" + (builder ? "tempNewBuilder_ter_#{ident}.ter" : "ter_#{ident}.ter"))
	end

	def create_pref_data(location)
		lines = [
			"// Generated by location_tool",
			"$Pref::Server::MaxPlayers = 64;",
			"$Pref::Server::Port = 28000;",
			"$Pref::Server::RootMission = \"#{location['mis_file']}\";",
			"$Pref::Server::LocationId = \"#{location['id']}\";",
			"$Pref::Server::IsBuilder = #{location['builder'] ? 'true' : 'false'};"
		]
		return lines.join("\n")
	end

	def create_blank_mission(location)
		tpl = File.open('template/newMission.erb').read
		render = ERB.new(tpl)
		return render.result(LocationTpl.new(location).get_binding)
	end

	def create_location(name)
		pref_file = "onverse/server/cfg/#{name}.builder.cs"
		builder_pref_file = "onverse/server/cfg/#{name}.builder.cs"
		mis_file = "onverse/server/data/#{name}.builder.mis"
		if File.exist?(pref_file)
			raise Exception.new("Location of that name already exists. Choose a different name or delete #{pref_file}.")
		end

		@last_id += 1
		ter_file = get_terrain_name(@last_id, true)
		location = {'id' => @last_id, 
					'short_name' => name,
					'pref_file' => pref_file,
					'mis_file' => mis_file,
					'ter_file' => ter_file,
					'changekey' => 0,
					'builder' => true}

		File.open(pref_file, 'w') { |f| f.write(create_pref_data(location)) }

		FileUtils.cp("template/newMission.ter", ter_file)

		File.open(mis_file, 'w') do |f|
			f.write(create_blank_mission(location))
		end

		@data['locations'] << location
	end

	def commit_location(name)
		builder_location = get_location_by_name(name, true)
		if builder_location.nil?
			raise Exception.new("No builder location to commit!")
		end

		existing_location = get_location_by_name(name, false)
		if existing_location.nil?
			existing_location = builder_location.clone
			existing_location['builder'] = false
			@data['locations'] << existing_location
		end

		existing_location['builder'] = false

		# Advance id of location
		@last_id += 1
		existing_location['id'] = @last_id

		# Fixup mis
		obj_mapping = {
			'ClientEditTerrainBlock' => 'TerrainBlock',
			'ClientEditSky' => 'Sky',
			'ClientEditSun' => 'Sun',
			'ClientEditWaterBlock' => 'WaterBlock',
			'ClientEditAudioEmitter' => 'AudioEmitter',
			'ClientEditParticleEmitter' => 'ParticleEmitter',
			'ClientEditParticleEmitterNode' => 'ParticleEmitterNode',
			'ClientEditSunLight' => 'fxSunLight',
			'ClientEditLightObject' => 'sgLightObject',
			'ClientEditShapeReplicator' => 'fxShapeReplicator',
			'ClientEditFoliageReplicator' => 'fxFoliageReplicator',
			'ClientEditGameScriptObject' => 'GameScriptObject',
			'ClientEditTrigger' => 'Trigger',
			'ClientEditCamera' => 'Camera',
			'ClientEditInteriorInstance' => 'InteriorInstance',
			'ClientEditTSStatic' => 'TSStatic',
			'ClientEditSceneObject' => 'SceneObject',
			'ClientEditPoiPoint' => 'PoiPoint',
			'ClientEditInteractiveObject' => 'InteractiveObject',
			'ClientEditHomeArea' => 'HomeArea',
			'ClientEditHomePoint' => 'HomePoint',
			'ClientEditStoreArea' => 'StoreArea',
			'ClientEditGameArea' => 'GameArea',
			'ClientEditPrecipitation' => 'Precipitation',
			'ClientEditCameraBlocker' => 'CameraBlocker',
			'ClientEditPlayerBlocker' => 'PlayerBlocker',
			'ClientEditVehicleBlocker' => 'VehicleBlocker',
			'ClientEditLightning' => 'Lightning',
			'ClientEditAIPlayerObject' => 'AIPlayerObject',
			'PurchaseToolObject' => 'PurchaseToolObject',
			'SimGroup' => 'SimGroup',
			'Item' => 'Item',
			'GiftItem' => 'GiftItem',
			'ParticleEmitter' => 'ParticleEmitter',
			'SpawnSphere' => 'SpawnSphere'
		}

		mis_data = File.open(builder_location['mis_file']).read
		mis_data.gsub!(/new ([A-Za-z][^(]*)\(/) do |match|
			m = match[4...(match.length)-1]
			if obj_mapping.has_key?(m)
				#puts "Mapping #{m}"
				"new #{obj_mapping[m]}("
			else
				puts "Warning: no mapping for #{m}"
				"new #{m}("
			end
		end

		existing_location['mis_file'] = "onverse/server/data/#{name}.mis"

		terrainFile = "<%= location['ter_file'] %>";

		mis_data.gsub!(/terrainFile = \"[^\"]+\";/, "terrainFile = \"#{existing_location['ter_file']}\";")
		File.open(existing_location['mis_file'], 'w') { |f| f.write(mis_data) }

		# Fixup conf
		pref_file = "onverse/server/cfg/#{existing_location['short_name']}.cs"
		File.open(pref_file, 'w') { |f| f.write(create_pref_data(existing_location)) }

		# Copy terrain
		new_terrain_name = get_terrain_name(@last_id, false)
		FileUtils.cp(existing_location['ter_file'], new_terrain_name)
		puts "#{existing_location['ter_file']} -> #{new_terrain_name}"
		existing_location['ter_file'] = new_terrain_name
	end

	def get_location_by_id(ident, builder)
		@data['locations'].select{|a|a['id'] == ident && a['builder'] == builder}.first
	end

	def get_location_by_name(name, builder)
		@data['locations'].select{|a|a['short_name'] == name && a['builder'] == builder}.first
	end

	def launch_location(name, builder)
		location = get_location_by_name(name, builder)
		if location.nil?
			raise Exception.new("No known location with name #{name} and builder=#{builder}")
		end

		cfg_file = builder ? "#{name}.builder" : name
		puts "./Onverse_INTERNAL --entry onverse/server/serverMain.cs -config #{cfg_file} -port #{OPTIONS[:port]}"
                system "./Onverse_INTERNAL --entry onverse/server/serverMain.cs -config #{cfg_file} -port #{OPTIONS[:port]}"
	end

	def save
		File.open("location_config.json", 'w') do |f|
			f.write @data.to_json
		end 
	end
end

$manager = LocationManager.new

def entry(args)
	mode = args[0]

	case mode
	when 'create'
		raise Exception.new("Please specify name") if args.length < 2
		location = $manager.create_location(args[1])
		$manager.save

	when 'commit'
		raise Exception.new("Please specify name") if args.length < 2
		$manager.commit_location(args[1])
		$manager.save

	when 'launch'
		raise Exception.new("Please specify name") if args.length < 2
		$manager.launch_location(args[1], OPTIONS[:builder])

	when 'list'
		$manager.locations.each do |location|
			puts "#{location['builder'] ? 'Builder ' : ''}Location #{location['id']}: #{location['short_name']}"
		end
	else
		raise Exception.new("Unknown command #{mode}")
	end
end

OptionParser.new do |opts|
  opts.banner = "Usage: location_tool [create|commit|launch|list] [options]"
  opts.on( '-b', '--builder', 'Builder' ) { |b| OPTIONS[:builder] = ['true', '1', true].include?(b) ? true : false }
  opts.on( '-p port', '--port port', 'Port' ) { |p| OPTIONS[:port] = p }
end.parse!

entry(ARGV)
