async = require 'async'
events = require 'events'
logsmight = require 'logsmith'

# ---

exports.escape = (input) ->
	return input.replace(/\t/g, '\\t').replace(/\n/g, '\\n').replace(/(["`$\\])/g, '\\$1')
	
# ---

exports.quote = (input) ->
	return '"' + exports.escape(input) + '"'
	
# ---

exports.Stream = class Stream extends events.EventEmitter
	emit_data_for_stdout: (data) -> @emit 'data', data, 'stdout'
	emit_data_for_stderr: (data) -> @emit 'data', data, 'stderr'
	emit_error: (error) -> @emit 'error', error
	emit_exit: (exit) -> @emit 'exit', exit
	
# ---

exports.create_task = (desc, run) ->
	return task: desc: desc, run: run
	
# ---

exports.create_exec_handler = (callback, failproof_or_handler) ->
	(err, stream) ->
		return callback err if err
		
		stream.on 'data', (data, extended) ->
			switch
				when extended == 'stdout' then out = process.stdout
				when extended == 'stderr' then out = process.stderr
				else out = process.stdout
				
			out.write data
			
		switch
			when failproof_or_handler == undefined
				failproof = false
				handler = null
			when typeof(failproof_or_handler) == 'boolean' || failproof_or_handler instanceof Boolean
				failproof = failproof_or_handler
				handler = null
			else
				failproof = false
				handler = failproof_or_handler
				
		if handler
			try
				handler.call this, stream, callback
			catch e
				return callback e if callback
		else
			stream.on 'exit', (code) ->
				return callback new Error "command exited with code #{code}" if code and !failproof
				return callback null
				
# ---

exports.Target = class Target
	constructor: () ->
		@steps = []
		@recoveries = []
		@ignited = false
		
	next: (task, callback) ->
		@steps.unshift [task, callback]
		
	step: (task, callback) ->
		@steps.push [task, callback]
		
	recover: (task, callback) ->
		@recoveries.push [task, callback]
		
	ignite: (dry, callback) ->
		@ignited = true
		exec_err = null
		exec_pointer = 'steps'
		
		test = () =>
			return true if @[exec_pointer].length > 0
			return false if exec_pointer == 'recoveries'
			
			exec_pointer = 'recoveries'
			
			return do arguments.callee
			
		fn = (callback) =>
			entry = @[exec_pointer].shift()
			task = entry[0]
			task_callback = entry[1]
			
			if dry
				logsmight.info task.desc if task.desc?
				
				return callback null
			else
				super_callback = (err) =>
					task_callback err if task_callback
					
					if err
						if exec_pointer == 'recoveries'
							logsmith.error err
						else
							@steps = []
							exec_err = err
							exec_pointer = 'recoveries'
							
					return callback null
					
				logsmight.info task.desc if task.desc?
				
				return task.run super_callback if task.run?
				return task super_callback if task
				
		async.whilst test, fn, (err) ->
			return callback err if err
			return callback exec_err if exec_err
			return callback null
			
	copy: () -> throw new Error "copy not implemented"
	exec: () -> throw new Error "exec not implemented"
	spawn: () -> throw new Error "spawn not implemented"
	
