------------------------------------------------------------------------------
--                                                                          --
--                              SYSTEM ET                                   --
--                                                                          --
--                           PROJECT MODULES                                --
--                                                                          --
--                               B o d y                                    --
--                                                                          --
-- Copyright (C) 2017 - 2024                                                --
-- Mario Blunk / Blunk electronic                                           --
-- Buchfinkenweg 3 / 99097 Erfurt / Germany                                 --
--                                                                          --
-- This library is free software;  you can redistribute it and/or modify it --
-- under terms of the  GNU General Public License  as published by the Free --
-- Software  Foundation;  either version 3,  or (at your  option) any later --
-- version. This library is distributed in the hope that it will be useful, --
-- but WITHOUT ANY WARRANTY;  without even the implied warranty of MERCHAN- --
-- TABILITY or FITNESS FOR A PARTICULAR PURPOSE.                            --
--                                                                          --
-- You should have received a copy of the GNU General Public License and    --
-- a copy of the GCC Runtime Library Exception along with this program;     --
-- see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    --
-- <http://www.gnu.org/licenses/>.                                          --
--                                                                          --
------------------------------------------------------------------------------

--   For correct displaying set tab with in your edtior to 4.

--   The two letters "CS" indicate a "construction site" where things are not
--   finished yet or intended for the future.

--   Please send your questions and comments to:
--
--   info@blunk-electronic.de
--   or visit <http://www.blunk-electronic.de> for more contact data
--
--   history of changes:
--

with ada.characters;			use ada.characters;
with ada.characters.latin_1;
with ada.characters.handling;	use ada.characters.handling;
with ada.strings; 				use ada.strings;
with ada.strings.fixed; 		use ada.strings.fixed;

with ada.exceptions;
with ada.directories;
with gnat.directory_operations;

with ada.containers;            use ada.containers;
with ada.containers.ordered_maps;
with ada.containers.ordered_sets;

with et_export;
with et_text;					use et_text;
with et_general_rw;				use et_general_rw;
with et_meta;

with et_pcb_coordinates_2;
with et_geometry;				use et_geometry;
with et_symbols;

with et_drills;

with et_symbol_rw;				--use et_symbol_rw;
with et_device_rw;				use et_device_rw;
with et_pcb_rw;					use et_pcb_rw;
with et_schematic_rw;			use et_schematic_rw;
with et_schematic_ops;			use et_schematic_ops;
with et_material;

with et_conventions;
with et_project.rigs;
with et_exceptions;				use et_exceptions;

package body et_project.modules is

	use pac_generic_modules;
	
	function to_string (project_name : in pac_project_name.bounded_string) return string is begin
		return pac_project_name.to_string (project_name);
	end to_string;
	
	function to_project_name (name : in string) return pac_project_name.bounded_string is begin
		return pac_project_name.to_bounded_string (name);
	end to_project_name;

	function to_string (path : in type_et_project_path.bounded_string) return string is begin
		return type_et_project_path.to_string (path);
	end to_string;

	function to_project_path (path : in string) return type_et_project_path.bounded_string is begin
		return type_et_project_path.to_bounded_string (path);
	end to_project_path;
	
	function exists (module : in pac_module_name.bounded_string) return boolean is begin
		return pac_generic_modules.contains (generic_modules, module);
	end;

	function locate_module (name : in pac_module_name.bounded_string) -- motor_driver (without extension *.mod)
		return pac_generic_modules.cursor 
	is
		cursor : pac_generic_modules.cursor := find (generic_modules, name);
	begin
		if cursor = pac_generic_modules.no_element then
			raise semantic_error_1 with
				"ERROR: Module " & enclose_in_quotes (to_string (name)) 
				& " does not exist !";
		else
			return find (generic_modules, name);
		end if;
	end locate_module;


	
	-- Returns the list of preferred schematic libraries:
	function get_preferred_libraries_schematic (module : in pac_generic_modules.cursor)
		return et_meta.pac_preferred_libraries_schematic.list
	is begin
		return element (module).meta.schematic.preferred_libs;
	end get_preferred_libraries_schematic;

	-- Returns the list of preferred board libraries (non-electrical packages):
	function get_preferred_libraries_board (module : in pac_generic_modules.cursor)
		return et_meta.pac_preferred_libraries_board.list
	is begin
		return element (module).meta.board.preferred_libs;
	end get_preferred_libraries_board;


	

	function sheet_description (
		module	: in pac_generic_modules.cursor;
		sheet	: in et_coordinates_2.type_sheet)
		return et_frames.type_schematic_description 
	is
		use et_frames;

		use pac_schematic_descriptions;
		cursor : pac_schematic_descriptions.cursor;

		use et_schematic;

		procedure query_descriptions (
			module_name	: in pac_module_name.bounded_string;
			module		: in type_module) 
		is begin
			cursor := find (module.frames.descriptions, sheet);
		end query_descriptions;
										 
	begin -- sheet_description
		query_element (
			position	=> module,
			process		=> query_descriptions'access);
		
		if cursor /= pac_schematic_descriptions.no_element then
			return element (cursor);
		else
		-- If the sheet has no description, then return the defaults.
			return (others => <>);
		end if;
	end sheet_description;
	
	procedure port_not_at_edge (name : in pac_net_name.bounded_string) is 
		use et_string_processing;
	begin
		log (ERROR, "port " & enclose_in_quotes (to_string (name)) &
			" must be at the edge of the submodule !", console => true);
		raise constraint_error;
	end;

	
	function port_connected (
	-- Returns true if given port of netchanger is connected with any net.
		module	: in pac_generic_modules.cursor;
		port	: in et_netlists.type_port_netchanger)
		return boolean 
	is
		result : boolean := false; -- to be returned. goes true on the first (and only) match.

		use et_nets;
		use et_schematic;
		

		procedure query_nets (
			module_name	: in pac_module_name.bounded_string;
			module		: in type_module) 
		is
			use pac_nets;
			net_cursor : pac_nets.cursor := module.nets.first;

			procedure query_strands (
				net_name	: in pac_net_name.bounded_string;
				net			: in type_net) 
			is
				use pac_strands;
				strand_cursor : pac_strands.cursor := net.strands.first;

				procedure query_segments (strand : in type_strand) is
					use pac_net_segments;
					segment_cursor : pac_net_segments.cursor := strand.segments.first;

					procedure query_ports (segment : in type_net_segment) is 
						use et_submodules;

						use et_netlists;
						use pac_netchanger_ports;
						port_cursor : pac_netchanger_ports.cursor := segment.ports.netchangers.first;
					begin
						while port_cursor /= pac_netchanger_ports.no_element loop
							if element (port_cursor) = port then
								result := true;
								exit; -- no more searching for netchanger ports required
							end if;
							next (port_cursor);
						end loop;
					end query_ports;
					
				begin -- query_segments
					while result = false and segment_cursor /= pac_net_segments.no_element loop
						
						query_element (
							position	=> segment_cursor,
							process		=> query_ports'access);
						
						next (segment_cursor);
					end loop;
				end query_segments;
				
			begin -- query_strands
				while result = false and strand_cursor /= pac_strands.no_element loop

					query_element (
						position	=> strand_cursor,
						process		=> query_segments'access);
					
					next (strand_cursor);
				end loop;
			end query_strands;
			
		begin -- query_nets
			while result = false and net_cursor /= pac_nets.no_element loop

				pac_nets.query_element (
					position	=> net_cursor,
					process		=> query_strands'access);

				next (net_cursor);
			end loop;
			
		end query_nets;
		
	begin -- port_not_connected

		pac_generic_modules.query_element (
			position	=> module,
			process		=> query_nets'access);
		
		return result;
	end port_connected;

	
	function netchanger_as_port_available (
		module		: in pac_generic_modules.cursor;
		net			: in et_nets.pac_nets.cursor;
		direction	: in et_submodules.type_netchanger_port_name) -- master/slave 
		return boolean 
	is
		
		result : boolean := false; -- to be returned. goes true on the first
		-- suitable netchanger found.

		use et_nets;
		use et_schematic;
		
		procedure query_strands (
			net_name	: in pac_net_name.bounded_string;
			net			: in type_net)
		is
			use pac_strands;
			strand_cursor : pac_strands.cursor := net.strands.first;

			procedure query_segments (strand : in type_strand) is
				use pac_net_segments;
				segment_cursor : pac_net_segments.cursor := strand.segments.first;

				procedure query_ports (segment : in type_net_segment) is 
					use et_submodules;

					use et_netlists;
					use pac_netchanger_ports;
					port_cursor : pac_netchanger_ports.cursor := segment.ports.netchangers.first;
				begin
					while port_cursor /= pac_netchanger_ports.no_element loop

						-- If the given direction is MASTER, then we must look for a SLAVE netchanger
						-- port (and vice versa) in the net segment.
						if element (port_cursor).port = opposide_port (direction) then 

							-- The opposide port must be not connected. In that case 
							-- suitable netchanger has been found:
							if not port_connected (
								module	=> module,
								port	=> (index	=> element (port_cursor).index,
											port	=> direction)) then
								
								result := true;
								exit; -- no more searching for netchanger ports required
							end if;

						end if;
						
						next (port_cursor);
					end loop;
				end query_ports;
				
			begin -- query_segments
				while result = false and segment_cursor /= pac_net_segments.no_element loop
					
					query_element (
						position	=> segment_cursor,
						process		=> query_ports'access);
					
					next (segment_cursor);
				end loop;
			end query_segments;
			
		begin -- query_strands
			while result = false and strand_cursor /= pac_strands.no_element loop

				query_element (
					position	=> strand_cursor,
					process		=> query_segments'access);
				
				next (strand_cursor);
			end loop;
		end query_strands;
		
	begin -- netchanger_as_port_available
		pac_nets.query_element (
			position	=> net,
			process		=> query_strands'access);
		
		return result;
	end netchanger_as_port_available;


	
	procedure save_module (
		module_cursor	: in pac_generic_modules.cursor;
		save_as_name	: in pac_module_name.bounded_string := to_module_name (""); -- motor_driver, templates/clock_generator							  
		log_threshold	: in type_log_level) 
	is separate;


	
	function to_string (section : in type_section) return string is
	-- Converts a section like SEC_NET to a string "net".
		len : positive := type_section'image (section)'length;
	begin
		return to_lower (type_section'image (section) (5..len));
	end to_string;


	
	procedure read_module (
	-- Reads a module file and stores its content as generic module in container modules.
	-- The file name may contain environment variables.
	-- The file must exist, must be visible from the current working directory.
		file_name 		: in string; -- motor_driver.mod, templates/clock_generator.mod
		log_threshold	: in type_log_level) 
		is separate;


		
	procedure create_module (
		module_name		: in pac_module_name.bounded_string; -- motor_driver, templates/clock_generator
		log_threshold	: in type_log_level) is

		module_cursor : pac_generic_modules.cursor;
		inserted : boolean;
		use et_string_processing;
	begin
		log (
			text	=> "creating module " & enclose_in_quotes (to_string (module_name)) & " ...",
			level	=> log_threshold);

		-- We create the new module only if does not exist already:

		-- Create an empty module named after the given module name.
		-- So the module names are things like "motor_driver", "templates/clock_generator".
	
		-- CS: make sure the module is inside the current project directory.
		
		pac_generic_modules.insert (
			container	=> generic_modules,
			key			=> module_name,
			position	=> module_cursor,
			inserted	=> inserted);

		if to_string (module_name) /= untitled then
			if inserted then
				save_module (module_name, log_threshold + 1);
			else
				raise semantic_error_1 with
					"ERROR: Module " & enclose_in_quotes (to_string (module_name)) 
					& " already exists -> not created.";
			end if;
		end if;
		
	end create_module;

	procedure save_module (
		module_name		: in pac_module_name.bounded_string; -- motor_driver, templates/clock_generator
		log_threshold	: in type_log_level) 
	is
		module_cursor : pac_generic_modules.cursor := locate_module (module_name);

		use et_string_processing;
		use ada.directories;

		file_name : constant string := to_string (module_name) &
			latin_1.full_stop & module_file_name_extension;
		-- motor_driver.mod or templates/clock_generator.mod
	begin
		log (
			text	=> "saving module " & enclose_in_quotes (to_string (module_name)) & " ...",
			level	=> log_threshold);

		-- We save the module if it exists:
		if module_cursor /= pac_generic_modules.no_element then
	
			-- Make sure the module file is inside the current project directory.
			-- If the file is outside the project, issue a warning and do not save.
			if inside_project_directory (to_string (module_name)) then
			
				save_module (
					module_cursor	=> module_cursor,
					log_threshold 	=> log_threshold + 1);

			else
				log (WARNING, "module " & enclose_in_quotes (to_string (module_name)) &
					 " is outside the project and will not be saved !");
			end if;
			
		else
			log (WARNING, "module " & enclose_in_quotes (to_string (module_name)) &
					" does not exist !");
		end if;
		
	end save_module;
	
	procedure delete_module (
		module_name		: in pac_module_name.bounded_string; -- motor_driver, templates/clock_generator
		log_threshold	: in type_log_level) is

		module_cursor : pac_generic_modules.cursor := locate_module (module_name);

		use et_string_processing;
		use ada.directories;

		file_name : constant string := append_extension (to_string (module_name));
		-- motor_driver.mod or templates/clock_generator.mod

		file_found : boolean := true;
		module_found : boolean := true;
	begin
		log (
			text	=> "deleting module " & enclose_in_quotes (to_string (module_name)) & " ...",
			level	=> log_threshold);

		-- Delete the module file in case it exists already:
		if exists (file_name) then
			delete_file (file_name);
		else
			file_found := false;
		end if;
		
		-- Remove the module from collection of generic modules:
		if module_cursor /= pac_generic_modules.no_element then
	
			---- Make sure the module file is inside the current project directory.
			---- If the file is outside the project, issue a warning and do not delete.
			--if inside_project_directory (to_string (module_name)) then
				
			pac_generic_modules.delete (
				container	=> generic_modules,
				position	=> module_cursor);

			--else
				--log (WARNING, "module " & enclose_in_quotes (to_string (module_name)) &
					 --" is outside the project and will not be deleted !");
			--end if;
		else
			module_found := false;
		end if;

		-- If neither the module nor the module file have been found, raise error:
		if not file_found and not module_found then
			raise semantic_error_1 with
				"ERROR: Neither module " & enclose_in_quotes (to_string (module_name)) 
				& " nor the file " & enclose_in_quotes (file_name) & " exist !";
		end if;
	end delete_module;



	function exists (
	-- Returns true if the given module provides the given port.
	-- The module being searched in must be in the rig already.
		module			: in et_submodules.pac_submodules.cursor;
		port			: in pac_net_name.bounded_string; -- clock_output
		direction		: in et_submodules.type_netchanger_port_name) -- master/slave
		return boolean 
	is
		result : boolean := false; -- to be returned
		
		use et_string_processing;
		use et_submodules;
		use et_schematic;
		
		submodule_file : pac_submodule_path.bounded_string; -- $ET_TEMPLATES/motor_driver.mod
		module_name : pac_module_name.bounded_string; 
		module_cursor : pac_generic_modules.cursor;

		procedure query_nets (
			module_name	: in pac_module_name.bounded_string;
			module		: in type_module) 
		is
			use et_nets;
			net_cursor : pac_nets.cursor;

			-- The port being inquired is a net inside the submodule.
			net : constant string := to_string (port);
			use pac_nets;
		begin
			-- locate the net in the submodule
			net_cursor := find (module.nets, to_net_name (net));

			-- If net found, test its scope. If it is global,
			-- then all requirements are met -> result true.
			-- If net is local, then a netchanger is required.
			if net_cursor /= pac_nets.no_element then -- net found

				case element (net_cursor).scope is
					when et_netlists.GLOBAL => 
						result := true;

					when et_netlists.LOCAL =>
						if netchanger_as_port_available (module_cursor, net_cursor, direction) then
							result := true;
						else
							result := false;
						end if;
				end case;
				
			else -- net not found: result is false
				result := false;
			end if;

		end query_nets;
		
	begin -- exists
		submodule_file := pac_submodules.element (module).file;

		module_name := to_module_name (remove_extension (to_string (submodule_file)));
		module_cursor := locate_module (module_name);

		pac_generic_modules.query_element (
			position	=> module_cursor,
			process		=> query_nets'access);
		
		return result;

		exception
			when event: others =>
				log_indentation_reset;
				log (text => ada.exceptions.exception_information (event), console => true);
				raise;
		
	end exists;

	
	function exists (
	-- Returns true if the given module provides the given submodule instance.
	-- The module being searched in must be in the rig already.						
		module		: in pac_generic_modules.cursor; -- the parent module that contains the submodule instance
		instance	: in et_general.pac_module_instance_name.bounded_string) -- OSC1
		return boolean is

		instance_found : boolean := false; -- to be returned

		procedure query_submodules (
			module_name	: in pac_module_name.bounded_string;
			module		: in et_schematic.type_module) is
			use et_submodules.pac_submodules;
		begin
			if contains (module.submods, instance) then
				instance_found := true;
			end if;
		end query_submodules;

	begin -- exists
		-- search in the parent module for the given submodule instance
		pac_generic_modules.query_element (
			position	=> module,
			process		=> query_submodules'access);

		return instance_found;
	end exists;

	function exists (
	-- Returns true if the given submodule instance provides the
	-- given assembly variant. The submodule instance is searched for
	-- in the parent module indicated by cursor "module".
	-- The module being searched in must be in the rig already.												
		module		: in pac_generic_modules.cursor; -- the parent module that contains the submodule instance
		instance	: in et_general.pac_module_instance_name.bounded_string; -- OSC1
		variant		: in pac_assembly_variant_name.bounded_string) -- low_cost				
		return boolean is

		variant_found : boolean := false; -- to be returned

		procedure query_submodules (
			module_name	: in pac_module_name.bounded_string;
			module		: in et_schematic.type_module) is
			use et_submodules;
			use et_submodules.pac_submodules;
			submod_instance_cursor : et_submodules.pac_submodules.cursor;
			submod_path : pac_submodule_path.bounded_string;
			submod_name	: pac_module_name.bounded_string;
			submod_cursor : pac_generic_modules.cursor;

			procedure query_variants (
			-- Locates the given assembly variant in the submodule.
			-- Sets flag variant_found.
				submodule_name	: in pac_module_name.bounded_string;
				submodule		: in et_schematic.type_module) is
				use et_assembly_variants;
			begin
				if pac_assembly_variants.contains (submodule.variants, variant) then
					variant_found := true;
				end if;
			end query_variants;
				
		begin -- query_submodules
			-- locate the submodule instance by the given instance name
			submod_instance_cursor := find (module.submods, instance);

			-- get the file name of the submodule like $ET_TEMPLATES/motor_driver.mod
			submod_path :=  element (submod_instance_cursor).file;

			-- convert the submodule path to a submodule name
			submod_name := to_module_name (remove_extension (to_string (submod_path)));

			--log (text => "submod name " & to_string (submod_name));

			-- get a cursor to the submodule file
			submod_cursor := locate_module (submod_name);

			-- locate the given variant in the submodule
			pac_generic_modules.query_element (
				position	=> submod_cursor,
				process		=> query_variants'access);

		end query_submodules;
		
	begin -- exists
		-- search in the parent module for the given submodule instance
		pac_generic_modules.query_element (
			position	=> module,
			process		=> query_submodules'access);

		return variant_found;
	end exists;

	function exists (
	-- Returns true if the given module provides the given assembly variant.
	-- If the variant is an empty string then it is about the default variant
	-- which is always provided. The return is true in that case.
		module		: in pac_generic_modules.cursor;
		variant		: in pac_assembly_variant_name.bounded_string) -- low_cost
		return boolean is

		use pac_assembly_variants;

		result : boolean := false; -- to be returned

		procedure query_variants (
			module_name	: in pac_module_name.bounded_string;
			module		: in et_schematic.type_module) is
		begin
			result := contains (module.variants, variant);
		end;
		
	begin -- exists
		if pac_assembly_variant_name.length (variant) = 0 then
			result := true;
		else
			
			pac_generic_modules.query_element (
				position	=> module,
				process		=> query_variants'access);

		end if;
					
		return result;
	end exists;

	function exists (
	-- Returns true if the given module and variant provides the given device.
	-- Assumptions: 
	-- - The module being searched in must be in the rig already.
	-- - The assembly variant must exist in the module.
	-- - The device must exist in the module.
		module	: in pac_generic_modules.cursor; -- the module like motor_driver
		variant	: in pac_assembly_variant_name.bounded_string; -- low_cost				
		device	: in type_device_name)
		return boolean is

		result : boolean := false; -- to be returned

		procedure query_variants (
			module_name	: in pac_module_name.bounded_string;
			module		: in et_schematic.type_module) is
			use pac_assembly_variants;
			variant_cursor : pac_assembly_variants.cursor;

			procedure query_devices (
				variant_name	: in pac_assembly_variant_name.bounded_string;
				variant			: in type_assembly_variant) is
				use et_assembly_variants;
				use pac_device_variants;
				device_cursor : pac_device_variants.cursor;
			begin
				device_cursor := find (variant.devices, device);

				-- The device may be listed in the assembly variant:
				if device_cursor /= pac_device_variants.no_element then
					case element (device_cursor).mounted is
						when YES => result := true; -- mounted with alternative value, partcode or purpose
						when NO  => result := false; -- not mounted
					end case;
				else
				-- The device may be NOT listed in the assembly variant. Means it is mounted always.
					result := true;
				end if;
					
			end query_devices;
				
		begin -- query_variants
			variant_cursor := find (module.variants, variant);

			query_element (
				position	=> variant_cursor,
				process		=> query_devices'access);
		end;
		
	begin -- exists
-- 		log (text => "module " & enclose_in_quotes (to_string (module_name)) &
-- 			" variant " & enclose_in_quotes (to_variant (variant)) &
-- 			" querying device " & to_string (device),
-- 			level => log_threshold);

		pac_generic_modules.query_element (
			position	=> module,
			process		=> query_variants'access);
		
		return result;
	end exists;

	function alternative_device (
	-- Returns a cursor to the alternative device in the given module
	-- and given assembly variant.
	-- Assumptions: 
	-- - The module being searched in must be in the rig already.
	-- - The assembly variant must exist in the module.
	-- - The device must exist in the module.
	-- - The device must have an entry in the given assembly variant,
	--   otherwise the return is no_element.
		module	: in pac_generic_modules.cursor; -- the module like motor_driver
		variant	: in pac_assembly_variant_name.bounded_string; -- low_cost				
		device	: in type_device_name)
		return pac_device_variants.cursor is

		cursor : pac_device_variants.cursor; -- to be returned;
		
		procedure query_variants (
			module_name	: in pac_module_name.bounded_string;
			module		: in et_schematic.type_module) is
			use pac_assembly_variants;
			
			variant_cursor : pac_assembly_variants.cursor;

			procedure query_devices (
				variant_name	: in pac_assembly_variant_name.bounded_string;
				variant			: in type_assembly_variant) is
				use pac_device_variants;
			begin
				cursor := find (variant.devices, device);
			end query_devices;
				
		begin -- query_variants
			variant_cursor := find (module.variants, variant);

			query_element (
				position	=> variant_cursor,
				process		=> query_devices'access);
		end;
		
	begin -- alternative_device

		pac_generic_modules.query_element (
			position	=> module,
			process		=> query_variants'access);
		
		return cursor;
	end alternative_device;

	function alternative_submodule (
	-- Returns a cursor to the alternative submodule variant in the given module
	-- and given assembly variant.
	-- Assumptions: 
	-- - The module being searched in must be in the rig already.
	-- - The assembly variant must exist in the module.
	-- - The suubmodule must have been instantiated in the module.
	-- - The submodule must have an entry in the given assembly variant,
	--   otherwise the return is no_element.
	-- If the given variant is an emtpy string (means default variant) the return
	-- is no_element.
		module	: in pac_generic_modules.cursor; -- the module like motor_driver
		variant	: in pac_assembly_variant_name.bounded_string; -- low_cost				
		submod	: in et_general.pac_module_instance_name.bounded_string) -- OSC1
		return pac_submodule_variants.cursor is

		cursor : pac_submodule_variants.cursor; -- to be returned;
		
		procedure query_variants (
			module_name	: in pac_module_name.bounded_string;
			module		: in et_schematic.type_module) is
			use pac_assembly_variants;

			variant_cursor : pac_assembly_variants.cursor;

			procedure query_submodules (
				variant_name	: in pac_assembly_variant_name.bounded_string;
				variant			: in type_assembly_variant) is
				use pac_submodule_variants;
			begin
				cursor := find (variant.submodules, submod);
			end query_submodules;
				
		begin -- query_variants
			variant_cursor := find (module.variants, variant);

			query_element (
				position	=> variant_cursor,
				process		=> query_submodules'access);
		end;
		
	begin -- alternative_submodule
		if is_default (variant) then
			cursor := pac_submodule_variants.no_element;
		else
			pac_generic_modules.query_element (
				position	=> module,
				process		=> query_variants'access);
		end if;
		
		return cursor;
	end alternative_submodule;
	
	function deepest_conductor_layer (
		module	: in pac_generic_modules.cursor) -- the module like motor_driver
		return et_pcb_stack.type_signal_layer is
	begin
		return et_pcb_stack.deepest_layer (pac_generic_modules.element (module).board.stack);
	end deepest_conductor_layer;

	function layout_rules_assigned (
		module	: in pac_generic_modules.cursor) -- the module like motor_driver
		return boolean is

		use et_design_rules.pac_file_name;
	begin
		if length (pac_generic_modules.element (module).rules.layout) > 0 then
			return true;
		else
			return false;
		end if;
	end layout_rules_assigned;

	function get_pcb_design_rules (
		module	: in pac_generic_modules.cursor) -- the module like motor_driver
		return et_design_rules.type_design_rules -- JLP_ML4_standard.dru
	is
		use et_design_rules;
	begin
		return get_rules (element (module).rules.layout); 
	end get_pcb_design_rules;

	function get_user_settings (
		module	: in pac_generic_modules.cursor) -- the module like motor_driver
		return et_pcb.type_user_settings
	is
		settings : et_pcb.type_user_settings; -- to be returned

		procedure query_module (
			module_name	: in pac_module_name.bounded_string;
			module		: in et_schematic.type_module)
		is begin
			settings := module.board.user_settings;
		end;
	begin
		pac_generic_modules.query_element (module, query_module'access);
		
		return settings;
	end get_user_settings;

	
	function get_net_class (
		module	: in pac_generic_modules.cursor; -- the module like motor_driver
		class	: in et_pcb.pac_net_class_name.bounded_string) -- hi-voltage, si-critical
		return et_pcb.type_net_class
	is
		use et_pcb;
		
		result : type_net_class;

		procedure query_module (
			module_name	: in pac_module_name.bounded_string;
			module		: in et_schematic.type_module)
		is
			use pac_net_classes;
			use pac_net_class_name;
		begin
			if class = net_class_name_default then
				null;
				-- CS load result with DRU settings (min track clearance, min track width, 
				-- min via drill size)
			else
				result := element (find (module.net_classes, class));
			end if;
		end query_module;
		
	begin
		query_element (module, query_module'access);
		
		return result;
	end get_net_class;


	function get_net_class (
		module	: in pac_generic_modules.cursor; -- the module like motor_driver
		net		: in et_nets.pac_nets.cursor) -- GND, RESET_N, ...
		return et_pcb.type_net_class
	is
		use et_pcb;
		result : type_net_class;

		procedure query_module (
			module_name	: in pac_module_name.bounded_string;
			module		: in et_schematic.type_module)
		is
			use pac_net_classes;
			use pac_net_class_name;

			use et_schematic;
			use et_nets;
			use pac_nets;
		begin
			if net = et_nets.pac_nets.no_element then -- freetrack
				null;
				-- CS load result with DRU settings (min track clearance, min track width, 
				-- min via drill size)
			else
				if element (net).class = net_class_name_default then
					null;
					-- CS load result with DRU settings (min track clearance, min track width, 
					-- min via drill size)
				else
					result := element (find (module.net_classes, element (net).class));
				end if;
			end if;
		end query_module;
		
	begin
		query_element (module, query_module'access);
		return result;
	end get_net_class;
	
	
end et_project.modules;
	
-- Soli Deo Gloria

-- For God so loved the world that he gave 
-- his one and only Son, that whoever believes in him 
-- shall not perish but have eternal life.
-- The Bible, John 3.16
