=begin
#-------------------------------------------------------------------------------------------------------------------------------------------------
#*************************************************************************************************
# Copyright 2009 - Designed September 2009 by Fredo6

# Permission to use this software for any purpose and without fee is hereby granted
# Distribution of this software for commercial purpose is subject to:
#  - the expressed, written consent of the author
#  - the inclusion of the present copyright notice in all copies.

# THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#-----------------------------------------------------------------------------
# Name			:   CurviloftTool.rb
# Original Date	:   05 Sep 2009 - version 1.0
# Type			:   Sketchup Tools
# Description	:   Manage the interactive GUI tool for Curviloft Loft
#-------------------------------------------------------------------------------------------------------------------------------------------------
#*************************************************************************************************
=end

module F6_Curviloft

T6[:TIT_SplineLoft] = "Loft by Spline"
T6[:TIT_PathLoft] = "Loft along Path"
T6[:TIT_Sweep] = "Sweep Rail"
T6[:TIT_Skinning] = "Skinning"
T6[:TIT_Transit] = "Curviloft: temporary"
T6[:TIT_RailSelection] = "RAIL selection"
T6[:TIT_ContourSelection] = "CONTOUR selection"

T6[:MSG_PleaseWait] = "PLEASE WAIT...."

T6[:TIP_ExecAlgo] = "Validate contours and go to preview mode"
T6[:TIP_ExecGeometry] = "Finish and Generate geometry"

T6[:TIP_BackAlgo] = "Go back to Contour selection"
T6[:TIP_BackGeometry] = "Go back to Preview mode"
	
#--------------------------------------------------------------------------------------------------------------
# Application Launcher: Call back called by Fredo6 for executing menus
#--------------------------------------------------------------------------------------------------------------			 				   
	
#Actual launching of menus	
def F6_Curviloft.action__mapping(action_code)
	case action_code
	when :loft_by_spline
		F6_Curviloft.launch_loft({ :method => :splineloft})
	when :loft_along_path
		F6_Curviloft.launch_loft({ :method => :along_path})
	when :loft_sweep
		F6_Curviloft.launch_loft({ :method => :sweep})
	when :skinning
		F6_Curviloft.launch_loft({ :method => :skinning})
	end		
end
	

def F6_Curviloft.launch_loft(*args)
	@tool_loft = CVL_LoftTool.new *args
	Sketchup.active_model.select_tool @tool_loft
end

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# SU Tool Class to manage GUI for Loft functions
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
					
class CVL_LoftTool < Traductor::PaletteSuperTool

def initialize(*args)
	#Parsing the arguments
	args.each { |arg| arg.each { |key, value|  parse_args(key, value) } if arg.class == Hash }

	@model = Sketchup.active_model

	#Loading strings
	init_text
	
	#Initializing the cursors
	hotx = 5
	hoty = 0
	@idcursor_default = Traductor.create_cursor "Cursor_Arrow_Default", 2, 2
	@id_cursor_arrow_exit = Traductor.create_cursor "Cursor_Arrow_Exit", 0, 0	
	@id_cursor_validate = Traductor.create_cursor "Cursor_Validate", 0, 0	
	@id_cursor_hourglass_red = Traductor.create_cursor "Cursor_hourGlass_Red", 16, 16		
	
	#Initializating the Edge Picker
	init_edge_picker
	
	#Creating the palette manager and texts
	init_palette
	
	#Creating the Loft Algo 
	hsh = { :method => @method, :palman => @palman, 
	        :proc_get_vertex => self.method('get_vertex_from_point'),
	        :proc_please_wait => self.method('please_wait') }
	@algo = CVL_LoftAlgo.new hsh
	@palman.set_algo @algo
end

#Parse the arguments of the initialize method
def parse_args(key, value)
	skey = key.to_s
	case skey
	when /method/i
		@method = value
	end	
end	

def make_proc(&proc) ; proc ; end

#Initialization of texts
def init_text
	case @method
	when :along_path
		@title = T6[:TIT_PathLoft]
	when :sweep
		@title = T6[:TIT_Sweep]
	when :splineloft	
		@title = T6[:TIT_SplineLoft]
	when :skinning
		@title = T6[:TIT_Skinning]
	end
	
	#@title_transit = T6[:TIT_Transit]
	@title_transit = "Curviloft: #{@title}"
	@msg_please_wait = T6[:MSG_PleaseWait]

	@mnu_exit = T6[:MNU_Exit]
	
	@tip_exec_algo = Traductor.encode_tip T6[:TIP_ExecAlgo], :enter
	@tip_exec_geometry = Traductor.encode_tip T6[:TIP_ExecGeometry], :enter
	@tip_back_algo = Traductor.encode_tip T6[:TIP_BackAlgo], :backspace
	@tip_back_geometry = Traductor.encode_tip T6[:TIP_BackGeometry], [:backspace, :escape]
	
	@txt_rail_selection = "[" + T6[:TIT_RailSelection] + "]"
	@txt_contour_selection = "[" + T6[:TIT_ContourSelection] + "]"
end

#Initializating the Edge Selection Picker
def init_edge_picker
	@anglemax = 40
	@modifier = 'F'
	
	hsh = {}
	hsh["notify_proc"] = self.method 'notify_edge_picked'
	hsh["modifier"] = @modifier if @modifier
	hsh["anglemax"] = @anglemax if @anglemax
	hsh["stop_at_crossing"] = true
	hsh["mesh"] = true if @method == :skinning
	hsh[:title] = @title
	hsh[:rail_display] = [0, 'red', 5] if @method == :along_path || @method == :sweep
	@selmode = Traductor::EdgePicker.new hsh
end

#Creating the palette manager
def init_palette
	proc_get_prop = self.method 'option_get_prop'
	proc_set_prop = self.method 'option_set_prop'
	proc_reset_prop = self.method 'option_reset_prop'
	notify_proc = self.method 'notify_from_palette'
	
	#List of options
	lst_options = []
	lst_options.push :geometry
	lst_options.push :option_simplify, :option_interpolate,  :option_match_method, :draw_zone
	
	if @method == :splineloft
		lst_options.push :option_numseg, :option_tension, :option_spline_master, 
		                 :option_spline_method, :option_global_loop, :option_twist
	
	elsif @method == :along_path || @method == :sweep
		lst_options.push :option_along_method, :option_twist, :option_sample

	elsif @method == :skinning
		lst_options.push :option_sample, :swap_rail
	
	end
	
	hsh = { 'title' => @title, 'list_options' => lst_options, 
			'loft_get_proc' => proc_get_prop, 'loft_set_proc' => proc_set_prop, 'loft_reset_proc' => proc_reset_prop,
 			'notify_proc' => notify_proc, :hidden_float_proc => self.method('hidden_float?'),
			'selmode' => @selmode }
			
	@palman = PaletteManager.new(self, @method, hsh) { refresh_view }
end

#--------------------------------------------------
# Callback methods from palette
#--------------------------------------------------

def option_get_prop(*args) ; @algo.option_get_prop(*args) ; end
def option_set_prop(*args) ; @algo.option_set_prop(*args) ; end
def option_reset_prop(*args) ; @algo.option_reset_prop(*args) ; end

#Notification method from palette for Back and Exec buttons
def notify_from_palette(action, code)
	case action
	when :tip
		compute_tooltip_for_palette code
	when :gray	
		compute_gray_for_palette code
	when :exec
		execute_from_palette code
	end	
end

#Tooltip for Back and Execute buttons
def compute_tooltip_for_palette(code)
	case code
	when :validate
		case @mode
		when :selection
			@tip_exec_algo
		when :algo
			@tip_exec_geometry
		end	
		
	when :back
		case @mode
		when :algo
			@tip_back_algo
		when :post_compute
			@tip_back_geometry
		end	

	end
end

#Gray status for Back and Execute buttons
def compute_gray_for_palette(code)
	case code
	when :validate
		case @mode
		when :post_compute
			true
		else
			false
		end	
		
	when :back
		case @mode
		when :selection
			true
		else
			false
		end	
		
	end	
end

#Execution for Back and Execute buttons
def execute_from_palette(code)
	case code
	when :validate
		execute_validate
	when :back
		execute_rollback
	end	
end

#Validate action, depending on the current mode
def execute_validate
	case @mode
	when :selection
		@selmode.terminate_current_selection
	when :algo
		#@palman.show_floating false
		execute_geometry
	end	
end

#Roll back action, depending on the current mode
def execute_rollback
	case @mode
	when :algo
		@suops.abort_operation
		set_state_mode :selection
	when :post_compute
		set_state_mode :algo
		@algo.roll_back
		onMouseMove_zero
	end	
end

#Status hide show for the floating palette
def hidden_float?
	@mode != :algo || @algo.preview_zone_hidden?
end

#--------------------------------------------------
# Standard SU Tool messages
#--------------------------------------------------

#Tool activation
def activate
	LibFredo6.register_ruby "Curviloft::#{@title}" if defined?(LibFredo6.register_ruby)
	@model = Sketchup.active_model
	@selection = @model.selection
	@entities = @model.active_entities
	@view = @model.active_view
		
	@button_down = false
	
	#Initiating the palette
	@palman.initiate
	set_state_mode :selection

	#Initializing the Executor
	hsh = { :title => @title_transit, :palette => @palman.palette, :end_proc => self.method('terminate_geometry'),
            :no_commit => true }
	@suops = Traductor::SUOperation.new hsh
	@suops.start_operation

	#initiating the Curl Manager and testing the current model selection
	@old_selection = @selection.to_a.clone
	@selmode.check_initial_selection
	transition_to_loft
		
	@view.invalidate
	info_show
end

def reset_selection

end

#Tool Deactivation
def deactivate(view)
	@suops.interrupt?(false)
	@palman.terminate
	@algo.deactivate
	@suops.abort_operation
	@selection.add @old_selection unless @mode == :post_compute
	G6.set_sun
	view.invalidate
end

#Cancel and undo methods
def onCancel(flag, view)
	case flag
	when 1, 2	#Undo or reselect the tool
		handle_undo
	when 0	#user pressed Escape	
		handle_escape
	end
end

def handle_undo
	@algo.zoom_terminate if @mode == :algo
	handle_escape
end

#Handle the escape key depending on current mode
def handle_escape
	return if @suops.interrupt?
	case @mode
	when :selection
		@selmode.handle_escape
	when :algo
		@algo.handle_escape
	when :post_compute
		execute_rollback
	end	
end

#VCB Inputs
def onUserText(text, view)
	#puts "VCB text = #{text} view = #{view}"
end

#Setting the cursor
def onSetCursor
	ic = @suops.onSetCursor
	return ic if ic
	
	ic = super
	return (ic != 0) if ic
	
	cursor = 0
	case @mode
	when :algo
		if @algo.mouse_in_void?
			cursor = @id_cursor_validate
		else
			cursor = @idcursor_default
		end	
	when :selection
		cursor = @selmode.onSetCursor
	when :post_compute
		cursor = @id_cursor_arrow_exit
	end
	UI::set_cursor cursor
end

#Contextual menu
def getMenu(menu)
	cxmenu = Traductor::ContextMenu.new
	
	if @mode == :algo
		@algo.contextual_menu_contribution(cxmenu)
	elsif @mode == :selection
		@selmode.contextual_menu_contribution(cxmenu)
	end
	
	#Other menus
	@palman.contextual_menu(cxmenu)
	
	#Menu Validate and Rollback
	cxmenu.add_sepa
	
	unless notify_from_palette :gray, :validate
		tip = notify_from_palette :tip, :validate
		cxmenu.add_item(tip) { notify_from_palette :exec, :validate }
	end
	unless notify_from_palette :gray, :back
		tip = notify_from_palette :tip, :back
		cxmenu.add_item(tip) { notify_from_palette :exec, :back }
	end

	#Exit
	cxmenu.add_sepa
	cxmenu.add_item(@mnu_exit) { exit_tool }
	
	#Showing the menu
	cxmenu.show menu
	true
end

#Return key pressed
def onReturn(view)
	execute_validate
end

#Exit the tool
def exit_tool
	@model.select_tool nil 
end

#Porcedure to refresh the view ehn options are changed
def refresh_view
	@view.invalidate
	info_show
end

#resume after view tools
def resume(view)
	if @mode == :algo
		@algo.refresh_when_view_changed
		#@algo.zoom_visibility true
	end	
	onMouseMove_zero
end	

def suspend(view)
	#@algo.zoom_visibility false if @mode == :algo
end

#Button Down
def onLButtonDown(flags, x, y, view)
	#Interrupting geometry construction if running
	return if @suops.interrupt?
	
	#Palette management
	return if super
	
	#Dispatching the event
	case @mode
	when :algo
		if @algo.mouse_in_void?
			execute_validate
		else	
			return @algo.onLButtonDown(flags, x, y, view)
		end	
	when :selection
		@selmode.onLButtonDown(flags, x, y, view)
	when :post_compute
		exit_tool
	end	
	onMouseMove_zero
end

#Button Up
def onLButtonUp(flags, x, y, view)
	return if super
	case @mode
	when :algo
		@algo.onLButtonUp(flags, x, y, view)
	when :selection	
		@selmode.onLButtonUp(flags, x, y, view)
	end	
	@button_down = false
	onMouseMove_zero
end

#Double Click received
def onLButtonDoubleClick(flags, x, y, view)
	return if super
	case @mode
	when :algo
		return @algo.onLButtonDoubleClick(flags, x, y, view)
	when :selection	
		@selmode.onLButtonDoubleClick(flags, x, y, view)
	end	
	view.invalidate
end

#Key Up
def onKeyUp(key, rpt, flags, view)
	key = Traductor.check_key key, flags, true
	
	#Dispatching the event
	return execute_rollback if key == 8
	
	case @mode
	when :algo
		return if @algo.onKeyUp(key, rpt, flags, view)
	when :selection
		return onMouseMove_zero if @selmode.onKeyUp(key, rpt, flags, view)
	end
		
	@control_down = false
end

#Key down
def onKeyDown(key, rpt, flags, view)
	key = Traductor.check_key key, flags, false

	#Interrupting geometry construction if running
	return if @suops.interrupt?
	
	#Dispatching the event
	case @mode
	when :algo	
		return onMouseMove_zero if @algo.onKeyDown(key, rpt, flags, view)
	when :selection
		return view.invalidate if @selmode.onKeyDown(key, rpt, flags, view)
	end
	
	case key			
	#Calling options
	when CONSTRAIN_MODIFIER_KEY
		onMouseMove_zero

	when COPY_MODIFIER_KEY
		@control_down = true
		return

	else
		@control_down = false
		return
	end	
	@control_down = false
	
	onMouseMove_zero
	info_show
end

#Mouse Move method
def onMouseMove_zero ; onMouseMove(@flags, @xmove, @ymove, @view) if @xmove ; end

def onMouseMove(flags, x, y, view)
	#Event for the palette
	return if super
	
	@xmove = x
	@ymove = y

	#Synchronize draw and move
	return if @moving
	@moving = true

	#Dispatching the event
	case @mode
	when :algo
		@algo.onMouseMove flags, x, y, view
	when :selection	
		@selmode.onMouseMove(flags, x, y, view)
	end
	
	#Refreshing the view
	view.invalidate
	info_show
end	

#----------------------------------------------------------------
# Entity Picker Management
#----------------------------------------------------------------

#add or remove entity from selection
def notify_edge_picked(action, ledges)
	if action == :finish
		transition_to_loft
	elsif action == :curl_accept
		#do nothing
	end	
	ledges
end

#Transition to the Loft state
def transition_to_loft
	@algo.reset_all
	lst_contours = (@method == :skinning) ? @selmode.get_cells : @selmode.get_contours
	return if lst_contours.length == 0
	if @algo.check_contours(lst_contours, !@selmode.from_user_selection?)
		lorder = @algo.generic_actual_order lst_contours
		@selmode.set_contours_order lorder
		@algo.proceed
		set_state_mode :algo
	else
	end
	onSetCursor
	onMouseMove_zero
end

def get_vertex_from_point(pt)
	@selmode.get_vertex pt
end

#----------------------------------------------------------------
# Drawing Methods
#----------------------------------------------------------------

#Draw method for Polyline tool
def draw(view)	
	#Drawing the curves
	case @mode
	when :algo
		@algo.draw view
	when :selection
		@selmode.draw view
	end	
	@moving = false
	
	#Drawing the palette
	super
end

#display information in the Sketchup status bar
def info_show
	return if @mode == :geometry
	if @method == :along_path 
		contours = @selmode.get_contours 
		if contours && contours.length == 0
			@selmode.set_status_complement @txt_rail_selection
		else	
			@selmode.set_status_complement @txt_contour_selection
		end
	else
		@selmode.set_status_complement nil
	end
	return @selmode.info_show if @mode == :selection
	
	msg = @title
	label = ""
	txval = ""

	Sketchup.set_status_text msg
	Sketchup.set_status_text label, SB_VCB_LABEL
	Sketchup.set_status_text txval, SB_VCB_VALUE

end

#Method to indicate waiting while calculation of wireframe
def please_wait(finished=false)
	if finished
		info_show
		onSetCursor
	else
		UI::set_cursor @id_cursor_hourglass_red
		Sketchup.set_status_text @title + ': ' + @msg_please_wait
		Sketchup.set_status_text @msg_please_wait, SB_VCB_VALUE
	end	
end

#-----------------------------------------------------------------
# Interface to Algo for Execution / Termination
#-----------------------------------------------------------------

#Set the current mode
def set_state_mode(mode)
	@mode = mode
	case @mode
	when :selection, :algo
		visi = @mode
	else
		visi = nil
	end
	G6.set_sun(@mode == :algo)
	@palman.set_visible_families visi
	@algo.zoom_terminate 
	@view.invalidate
end

#Launch the execution of geometry
def execute_geometry
	set_state_mode :geometry
	@algo.zoom_terminate
	@algo.execute_geometry(@suops)
end

#Notification of the Termination of the geometry
def terminate_geometry(time)
	if time < 0
		set_state_mode :algo
	else
		set_state_mode :post_compute
	end
	@view.invalidate
	onSetCursor
end
	
end	#End Class CVL_LoftTool
	
end	#End Module F6_Curviloft
