begin
	require 'sketchup' # First we pull in the standard API hooks.
	require 'ShaDe//lib//utils.rb'
	require 'ShaDe//lib//geometry.rb'
	require 'ShaDe//lib//data-structures.rb'
  require 'ShaDe//lib//float-extra.rb'
rescue LoadError
	require "#{File.dirname(__FILE__)}/utils"
	require "#{File.dirname(__FILE__)}/geometry"
	require "#{File.dirname(__FILE__)}/data-structures"
  require "#{File.dirname(__FILE__)}/float-extra"
end

#Author:: Manuela Ruiz  (mailto:mruiz@lcc.uma.es)
#Abstract representation of a Constraint
class Constraint
	#Name of the Constraint
	attr_reader :name
	
	#Returns true iff the design complies the Constraint
	def satisfied?()
		#The empty Constraint always returns true
		return true
	end
	
	#Deletes the Constraint
	def delete()
	end
end


#Author:: Manuela Ruiz  (mailto:mruiz@lcc.uma.es)
#A sort of Constraint: the produced shapes must be distinct to the last produced one
class DistinctShapeConstraint < Constraint
	
	#Initializing
	def initialize()
		@name = Constants::DISTINCT_SHAPE_CONSTRAINT_NAME
	end
	#Returns true iff the design complies the Constraint
	def satisfied?()
		equal_to_last = false
		if Shade.project.execution.execution_history.size > 0
			last_shape = Shade.project.execution.execution_history.last[2]
			equal_to_last = (Shade.project.execution.current_shape == last_shape)
		end
		return !equal_to_last
	end
	
end

#Author:: Manuela Ruiz  (mailto:mruiz@lcc.uma.es)
#A sort of Constraint: the applied rule must not involve scales
class NoScalesConstraint < Constraint
	#Initializing
	def initialize()
		@name = Constants::NO_SCALES_CONSTRAINT_NAME
	end
	
	#Returns true iff the design complies the Constraint
	def satisfied?()
		result = true
		if Shade.project.execution.execution_history.size > 0
			last_t = Shade.project.execution.execution_history.last[1]

			sx = Math.sqrt(last_t[0]**2 + last_t[1]**2)
			if ((sx - 1).abs > 0.001) #If there is a scale...
				result = false
			end
		end
		return result
	end
	
end

#Author:: Manuela Ruiz  (mailto:mruiz@lcc.uma.es)
#An abstract representation of an area
class Area

	#points:: array of points limitting the area
	#color_name (optional): name of the color of the area to present
	#
	#Initializing
	def initialize(points, color_name=Constants::COLOR_AREA)
		mesh = Geom::PolygonMesh.new
		
		mesh.add_polygon points
		
		entities = Sketchup.active_model.entities
		
		materials = Sketchup.active_model.materials
		materials.add "area_material"
		material = materials[materials.length-1]
		material.color = color_name
		material.alpha = Constants::ALPHA
		@group = entities.add_group
		@group.entities.add_faces_from_mesh(mesh, 0, material, material)
		@group.locked = true
		
		# The face is the last entity added
		# Trick to circumvent a SU error (add_faces_from_mesh returns 0 instead of the added faces)
		size_entities = @group.entities.length	
		@face = @group.entities[size_entities-1]
	end
	
	#point:: a point
	#
	#Returns trye iff the area covers the point
	def cover_point?(point)
		result = false
		if (@face.classify_point point) <= Sketchup::Face::PointOnEdge
			result = true
		end
		return result
	end
	
	#Deletes the area
	def erase()
		entities = Sketchup.active_model.entities
		@group.locked = false
		entities.erase_entities @group
	end
end

#Author:: Manuela Ruiz  (mailto:mruiz@lcc.uma.es)
#A sort of Constraint: the shapes in the design cannot be outside an area
class AreaConstraint < Constraint
	
	#Reader the involved area
	attr_reader :area
	
	#points:: array of points limitting the area
	#
	#Initializing
	def initialize(points = nil)
		if points
			@area = Area.new points
		else
			@area = Area.new Constants::PTS_AREA.clone
		end
		@name = Constants::AREA_CONSTRAINT_NAME
	end
	
	#Returns true iff the shapes in the design are inside the area
	def satisfied?()
		execution = Shade.project.execution
		
		satisfied = true
		j = 0
		n_layers = Sketchup.active_model.layers.length
		layer_name = "Layer0"
		while (satisfied and (j < n_layers))
			layer_name = Sketchup.active_model.layers[j].name
			
			segments = execution.current_shape.s[layer_name]
			segments.reset_iterator
			
			while satisfied and (s_node = segments.get_next)
				s_list = s_node.list
				s_list.reset_iterator
				while satisfied and (s = s_list.get_next)
					satisfied = ((@area.cover_point? s.key.tail.point.transform(Constants::AXIOM_T)) && (@area.cover_point? s.key.head.point.transform(Constants::AXIOM_T)))
				end
			end
			
			if satisfied
				points = execution.current_shape.p[layer_name]
				points.reset_iterator
				while satisfied and (p_node = points.get_next)
					if !(p_node.key.value == Constants::INTERSECTION_LABEL)
						p_list = p_node.list
						p_list.reset_iterator
						while satisfied and (p = p_list.get_next)
							satisfied = (@area.cover_point? p.key.point.transform(Constants::AXIOM_T))
						end
					end
				end
			end
			
			j += 1
		end
		return satisfied
	end
	
	#Deletes the area
	def delete()
		@area.erase
	end
  
	#Writer for the involved area
	def area=(new_area)
		@area.erase
		@area = new_area
	end
end


class OverlapConstraint < Constraint
  
  #Initializing
  def initialize()
    @name = Constants::OVERLAP_CONSTRAINT_NAME
  end
  #Returns true iff the design complies the Constraint
  def satisfied?()
    overlap = false
    #puts"paso"
    if Shade.project.execution.execution_history.size > 0
      last = Shade.project.execution.execution_history.last[2]
      transformation = Shade.project.execution.execution_history.last[1]
      rule_id = Shade.project.execution.execution_history.last[0]
      overlap = overlap?(rule_id, transformation, last, Shade.project.execution.current_shape)
    end
    #puts"overlap = #{overlap}"
    return overlap
  end
  
  def overlap?(id, transformation, last, current)
        j = 0
        n_layers = 0 
        layer_names = Array.new()
        current.p.each_key{|layer|
          n_layers += 1
          layer_names.push layer
        }
        layer_name = "Layer0"
        contiene = true
        while ((j < n_layers) and contiene)
          layer_name = layer_names[j]
          
          #puts"n_layer = #{n_layers}"
          
          #puts"layer_name = #{layer_name}"          
         #puts"id = #{id}"
          
         #puts"transf = #{transformation}"
         
         rule = ShadeUtils.get_rule(id-1)
         
         #puts"rule = #{rule}"
         
          beta_points = Array.new()
          p_list = rule.beta.transform(transformation).p[layer_name]
          p_list.reset_iterator
          p = p_list.get_node(Label.new("Intersection_Label"))
            #puts"tipo beta = #{p.key.value}"
            puntos = p.list
            puntos.reset_iterator
            while (pt = puntos.get_next)
              #puts"punto beta = #{pt.key.point.x.to_f.round_to(2)} #{pt.key.point.y.to_f.round_to(2)}"
            beta_points.push [pt.key.point.x.to_f.round_to(2), pt.key.point.y.to_f.round_to(2)] 
            end
         
          
          current_points = Array.new()
          p_list = current.p[layer_name]
          p_list.reset_iterator
          p = p_list.get_node(Label.new("Intersection_Label"))
           #puts"tipo current = #{p.key.value}"
           puntos = p.list
           puntos.reset_iterator
           while (pt = puntos.get_next)
            #puts"punto current = #{pt.key.point.x.to_f.round_to(2)} #{pt.key.point.y.to_f.round_to(2)}"
             current_points.push [pt.key.point.x.to_f.round_to(2), pt.key.point.y.to_f.round_to(2)]
           end
         
           
           
         last_points = Array.new()
         p_list = last.p[layer_name]
         p_list.reset_iterator
         p = p_list.get_node(Label.new("Intersection_Label"))
          #puts"tipo last = #{p.key.value}"
          puntos = p.list
          puntos.reset_iterator
          while (pt = puntos.get_next)
            #puts"punto last = #{pt.key.point.x.to_f.round_to(2)} #{pt.key.point.y.to_f.round_to(2)}"
            last_points.push [pt.key.point.x.to_f.round_to(2), pt.key.point.y.to_f.round_to(2)]
          end
          
          
          last_points.each{|lp|
            current_points.delete lp
          }
          
          
          current_points.each{|cp|
           #puts"c = #{cp[0]} #{cp[1]}"
          }
          
          
          i=0
          while ((i<current_points.size) and contiene)
            if !(beta_points.include? current_points[i])
              contiene = false
            #puts"current de i = #{current_points[i]}"
            end
            i += 1 
          end 
          
          
          j += 1
        end
       #puts"overlap = #{contiene}"
        return contiene
  end
  
end