/*
 * $Id$
 *
 * JSDAI(TM), a way to implement STEP, ISO 10303
 * Copyright (C) 1997-2008, LKSoftWare GmbH, Germany
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License
 * version 3 as published by the Free Software Foundation (AGPL v3).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * JSDAI is a registered trademark of LKSoftWare GmbH, Germany
 * This software is also available under commercial licenses.
 * See also http://www.jsdai.net/
 */

package jsdai.lang;

import jsdai.dictionary.*;
import java.io.*;
import java.util.HashSet;
import java.util.Set;

class PhFileWriter {

	private static final int INSTANCE_ID_LENGTH = (Long.toString(Long.MAX_VALUE)).length() + 2;
	DataOutputStream output_stream;
	int row_length;
	Print_instance print_inst;
	Get_instance get_inst;
	byte [] instance_id = new byte[INSTANCE_ID_LENGTH];
	int instance_id_length;
	private ComplexEntityValue entity_values = new ComplexEntityValue();
	SdaiModel model_dict;
	String dict_sch_name;
	Writer_error_table error_table;


/*         Writer's constants          */
	static final byte DOT2 = (byte)'.';
	static final byte SPACE2 = (byte)' ';
	static final byte [] LOG_TRUE = {DOT2, (byte)'T', DOT2};
	static final byte [] LOG_FALSE = {DOT2, (byte)'F', DOT2};
	static final byte [] LOG_UNKNOWN = {DOT2, (byte)'U', DOT2};
	static final byte [] INDENT = {SPACE2, SPACE2, SPACE2, SPACE2, SPACE2};
	static final byte RETURN = 10;
	static final byte [] DIGITS = {(byte)'0',(byte)'1',(byte)'2',(byte)'3',(byte)'4',(byte)'5',(byte)'6',(byte)'7',(byte)'8',(byte)'9'};
	static final  byte header_entities[][] = {
		{(byte)'F',(byte)'I',(byte)'L',(byte)'E',(byte)'_',(byte)'D',(byte)'E',(byte)'S',(byte)'C',(byte)'R',(byte)'I',(byte)'P',(byte)'T',(byte)'I',(byte)'O',(byte)'N'},
		{(byte)'F',(byte)'I',(byte)'L',(byte)'E',(byte)'_',(byte)'N',(byte)'A',(byte)'M',(byte)'E'},
		{(byte)'F',(byte)'I',(byte)'L',(byte)'E',(byte)'_',(byte)'S',(byte)'C',(byte)'H',(byte)'E',(byte)'M',(byte)'A'},
		{(byte)'S',(byte)'E',(byte)'C',(byte)'T',(byte)'I',(byte)'O',(byte)'N',(byte)'_',(byte)'L',(byte)'A',(byte)'N',(byte)'G',(byte)'U',(byte)'A',(byte)'G',(byte)'E'},
		{(byte)'S',(byte)'E',(byte)'C',(byte)'T',(byte)'I',(byte)'O',(byte)'N',(byte)'_',(byte)'C',(byte)'O',(byte)'N',(byte)'T',(byte)'E',(byte)'X',(byte)'T'},
		{(byte)'F',(byte)'I',(byte)'L',(byte)'E',(byte)'_',(byte)'P',(byte)'O',(byte)'P',(byte)'U',(byte)'L',(byte)'A',(byte)'T',(byte)'I',(byte)'O',(byte)'N'}
	};
	static final byte [] comment_start = {(byte)'/',(byte)'*',(byte)' '};
	static final byte [] comment_end = {(byte)' ',(byte)'*',(byte)'/'};
	static final byte [] separation = {(byte)',',(byte)' '};
	static final String default_determination_method = "SECTION_BOUNDARY";
	static final String native_schema_comment = "  /* schema_instance.native_schema */";
	static final String determination_method_comment =
		"  /* determination_method = SDAI schema_instance compatibility */";
	static final String governed_sections_comment = "  /* schema_instance.associated_models */";
	static final String HEADER_COMMENT_1 = "Generated by software containing";
	static final String HEADER_COMMENT_2 = "JSDAI (TM) from LKSoft (www.lksoft.com, www.jsdai.net)";
	static final String HEADER_COMMENT_3 = "JSDAI Runtime ";
	static final String TEMP_REPO_NAME = "&temp";

	static final int TEMP_REPO_NAME_LENGTH = TEMP_REPO_NAME.length();

	static final String FILE_DESCRIPTION_description = "/* description */ ";
	static final String FILE_DESCRIPTION_implementation_level = "/* implementation_level */ ";
	static final String FILE_NAME_name = "/* name */ ";
	static final String FILE_NAME_time_stamp = "/* time_stamp */ ";
	static final String FILE_NAME_author = "/* author */ ";
	static final String FILE_NAME_organization = "/* organization */ ";
	static final String FILE_NAME_preprocessor_version = "/* preprocessor_version */ ";
	static final String FILE_NAME_originating_system = "/* originating_system */ ";
	static final String FILE_NAME_authorization = "/* authorization */ ";

	static final int FILE_DESCRIPTION_description_LENGTH = FILE_DESCRIPTION_description.length();
	static final int FILE_DESCRIPTION_implementation_level_LENGTH = FILE_DESCRIPTION_implementation_level.length();
	static final int FILE_NAME_name_LENGTH = FILE_NAME_name.length();
	static final int FILE_NAME_time_stamp_LENGTH = FILE_NAME_time_stamp.length();
	static final int FILE_NAME_author_LENGTH = FILE_NAME_author.length();
	static final int FILE_NAME_organization_LENGTH = FILE_NAME_organization.length();
	static final int FILE_NAME_preprocessor_version_LENGTH = FILE_NAME_preprocessor_version.length();
	static final int FILE_NAME_originating_system_LENGTH = FILE_NAME_originating_system.length();
	static final int FILE_NAME_authorization_LENGTH = FILE_NAME_authorization.length();

	static final int PRINT_IN_WRITER										= 0;

	static final int ROW_LENGTH_LIMIT 									= 80;
	static final int INDENT_LENGTH 										= 5;
	static final int INITIAL_SIZE_OF_FOLDERS 							= 64;
	static final int NUMBER_OF_CHARACTERS_IN_STRING					= 256;
	static final int NUMBER_OF_CHARACTERS_IN_INSTANCE				= 512;

	static final int INTERNAL_ERROR_WR									= 101;
	static final int REDEFINE_IN_HEADER									= 102;
	static final int REDEFINE_IN_USER_DEFINED							= 103;
	static final int TYPED_PAR_IN_HEADER								= 104;
	static final int REFERENCE_IN_HEADER								= 105;
	static final int ENTITY_NOT_FOUND_FOR_WRITER						= 106;


	PhFileWriter() throws SdaiException
	{
		error_table = new Writer_error_table();
		print_inst = new Print_instance(error_table);
		get_inst = new Get_instance(error_table);
	}


	void output_to_physical_file(String output_file_name, SdaiRepository repository, String file_name)
			throws java.io.IOException, SdaiException {
		try {
			OutputStream handle = new FileOutputStream(output_file_name);
			output_to_physical_file(handle, repository, file_name);
		} finally {
			print_close();
		}
	}

	void output_to_physical_file(OutputStream handle, SdaiRepository repository, String file_name)
			throws java.io.IOException, SdaiException {
		int j;
		SdaiModel model;
		long time_before_writing = 0, time_after_writing;
		long time_in_sec;

		print_inst.repo2file = repository;
		output_stream = new DataOutputStream( new BufferedOutputStream(handle));
		if (PRINT_IN_WRITER >= 0) {
			time_before_writing = System.currentTimeMillis();
		}
		output_stream.write(PhFileReader.keywords[5]);
		output_stream.write(PhFileReader.SEMICOLON_b);
		output_stream.write(RETURN);
		FILE_SCHEMA fsch = repository.file_schema;
		dict_sch_name = null;
		for (j = 0; j < repository.models.myLength; j++) {
			model = (SdaiModel)repository.models.myData[j];
			if ((model.mode & SdaiModel.MODE_MODE_MASK) != SdaiModel.NO_ACCESS) {
				model.export = true;
			} else {
				model.export = false;
			}
		}
		fsch.schema_identifiers_temp = null;
		for (j = 0; j < repository.models.myLength; j++) {
			String sch_name = null;
			A_string sch_name_aggr = null;
			model = (SdaiModel)repository.models.myData[j];
/*			if (repository.models.myLength == 1) {
				if (fsch != null) {
					sch_name_aggr = fsch.schema_identifiers_short;
					if (sch_name_aggr != null && sch_name_aggr.myLength > 0) {
						Object [] myDataA;
						if (sch_name_aggr.myLength == 1) {
							sch_name = (String)sch_name_aggr.myData;
						} else if (sch_name_aggr.myLength == 2) {
							myDataA = (Object [])sch_name_aggr.myData;
							sch_name = (String)myDataA[0];
						} else {
							ListElement element;
							if (sch_name_aggr.myLength <= CAggregate.SHORT_AGGR) {
								element = (ListElement)sch_name_aggr.myData;
							} else {
								myDataA = (Object [])sch_name_aggr.myData;
								element = (ListElement)myDataA[0];
							}
							sch_name = (String)element.object;
						}
					}
				}
			} else {*/
				sch_name = model.schema_name;
//			}
			if (sch_name == null) {
				if (model.underlying_schema == null) {
					model.getUnderlyingSchema();
				}
				model_dict = model.underlying_schema.modelDictionary;
				sch_name = model_dict.name;
				int name_length = sch_name.length();
				if (name_length > 15) {
					sch_name = sch_name.substring(0, name_length - 16);
					if (model.repository == SdaiSession.systemRepository) {
						dict_sch_name = sch_name;
					}
				} else {
					sch_name = null;
				}
			}
			if (sch_name != null) {
/*				if (fsch.schema_identifiers_short != null) {
					if (check_schema_name(fsch.schema_identifiers_short, sch_name)) {
						fsch.schema_identifiers_short.addByIndex(fsch.schema_identifiers_short.myLength + 1, sch_name);
					}
				} else {
					sch_name_aggr = new A_string(SdaiSession.listTypeSpecial, repository);
					sch_name_aggr.myData = sch_name;
					sch_name_aggr.myLength = 1;
					fsch.schema_identifiers = fsch.schema_identifiers_short = sch_name_aggr;
				}*/
				if (fsch.schema_identifiers_temp != null) {
					if (check_schema_name(fsch.schema_identifiers_temp, sch_name)) {
						fsch.schema_identifiers_temp.addByIndex(fsch.schema_identifiers_temp.myLength + 1, sch_name);
					}
				} else {
					sch_name_aggr = new A_string(SdaiSession.listTypeSpecialU, repository);
					sch_name_aggr.myData = sch_name;
					sch_name_aggr.myLength = 1;
					fsch.schema_identifiers_temp = sch_name_aggr;
				}
				if (repository.models.myLength > 1) {
					model.schema_name = sch_name;
				}
			}
		}
		print_header(repository, file_name);
		print_header_user_defined(repository);
		output_stream.write(PhFileReader.keywords[2]);
		output_stream.write(PhFileReader.SEMICOLON_b);
		output_stream.write(RETURN);
		for (j = 0; j < repository.models.myLength; j++) {
			model = (SdaiModel)repository.models.myData[j];
			if (model.getMode() == SdaiModel.NO_ACCESS) {
//try {
				model.startReadOnlyAccess();
//} catch (SdaiException ex){
//continue;
//}
			}
			if (model.lengths != null) {
				get_inst.put_entity_types(model);
				print_data_section(model);
				print_data_section_user_defined();
				output_stream.write(PhFileReader.keywords[2]);
				output_stream.write(PhFileReader.SEMICOLON_b);
				output_stream.write(RETURN);
			}
			if ((model.mode & SdaiModel.MODE_MODE_MASK) != SdaiModel.NO_ACCESS && model.export == false &&
					model.repository != SdaiSession.systemRepository) {
				model.endReadOnlyAccess();
			} else {
				model.export = false;
			}
		}
		output_stream.write(PhFileReader.keywords[6]);
		output_stream.write(PhFileReader.SEMICOLON_b);
		output_stream.write(RETURN);
		entity_values.unset_ComplexEntityValue();
		if (PRINT_IN_WRITER >= 0) {
			time_after_writing = System.currentTimeMillis();
			time_in_sec = (time_after_writing-time_before_writing) / 1000;
			if (repository.session.logWriterSession != null) {
				repository.session.printlnSession("--- Writing time=" + time_in_sec + "sec");
			} else {
				SdaiSession.println("--- Writing time=" + time_in_sec + "sec");
			}
		}
		output_stream.flush();
	}

	private void writeString(String str) throws java.io.IOException {
		byte [] bytes = string_to_byte(str);
		output_stream.write(bytes);
	}

	private void print_header(SdaiRepository repository, String file_name)
			throws java.io.IOException, SdaiException {
		int i;
		int imp_level;
		String str;
		A_string lst = null;
		output_stream.write(PhFileReader.keywords[3]);
		output_stream.write(PhFileReader.SEMICOLON_b);
		output_stream.write(RETURN);

//		writeString("/* "
//			+ SdaiSession.session.getSdaiImplementation().getName()
//			+ "  "
//			+ SdaiSession.session.getSdaiImplementation().getLevel()
//			+ " */");
		writeString("/* " + HEADER_COMMENT_1);
		output_stream.write(RETURN);
		writeString(" * " + HEADER_COMMENT_2);
		output_stream.write(RETURN);
		writeString(" * " + HEADER_COMMENT_3 + Implementation.version);
		output_stream.write(RETURN);

		// Write library versions
		ClassLoader classLoader = SdaiClassLoaderProvider.getDefault().getClassLoader();
		Set usedSchemaVersions = new HashSet();
		for (int j = 0; j < repository.models.myLength; j++) {
			SdaiModel model = (SdaiModel)repository.models.myData[j];
			String schemaName = model.getUnderlyingSchemaString();
			schemaName =
				"jsdai.S" + Character.toUpperCase(schemaName.charAt(0)) +
				schemaName.substring(1).toLowerCase() +
				".S" +  Character.toUpperCase(schemaName.charAt(0)) +
				schemaName.substring(1).toLowerCase();
			try {
				Class schemaClass = Class.forName(schemaName, true, classLoader);
				Package schemaPackage = schemaClass.getPackage();
				if(schemaPackage != null) {
					String specTitle = schemaPackage.getSpecificationTitle();
					if(specTitle != null) {
						StringBuffer schemaVersionBuf = new StringBuffer(" * ");
						schemaVersionBuf.append(specTitle);
						String specVersion = schemaPackage.getSpecificationVersion();
						if(specVersion != null) {
							schemaVersionBuf.append(" Version ").append(specVersion);
						}
						String implVersion = schemaPackage.getImplementationVersion();
						if(implVersion != null) {
							schemaVersionBuf.append(specVersion != null ? " " : " Version ");
							schemaVersionBuf.append(implVersion);
						}
						String schemaVersion = schemaVersionBuf.toString();
						if(!usedSchemaVersions.contains(schemaVersion)) {
							usedSchemaVersions.add(schemaVersion);
							writeString(schemaVersion);
							output_stream.write(RETURN);
						}
					}
				}
			} catch (ClassNotFoundException e) {
			}
		}
		usedSchemaVersions.clear();

		writeString(" */");
		output_stream.write(RETURN);
		output_stream.write(header_entities[0]);
		output_stream.write(PhFileReader.LEFT_PARENTHESIS);
		output_stream.write(RETURN);
		writeString(FILE_DESCRIPTION_description);
		row_length = FILE_DESCRIPTION_description_LENGTH;
		A_string description = repository.description;
		if (description != null && description.myLength > 0) {
			expand_list(description, true, null);
		} else {
			expand_array(repository.session.description, true);
		}
		output_stream.write(PhFileReader.COMMA_b);
		output_stream.write(RETURN);
		writeString(FILE_DESCRIPTION_implementation_level);
		row_length = FILE_DESCRIPTION_implementation_level_LENGTH;
		if (repository.models.myLength <= 1) {
			str = "2;1";
			imp_level = 2;
		} else {
			str = "3;1";
			imp_level = 3;
		}
		write_string(str, true);
		output_stream.write(PhFileReader.RIGHT_PARENTHESIS);
		output_stream.write(PhFileReader.SEMICOLON_b);
		output_stream.write(RETURN);

		output_stream.write(header_entities[1]);
		output_stream.write(PhFileReader.LEFT_PARENTHESIS);
		output_stream.write(RETURN);
		writeString(FILE_NAME_name);
		row_length = FILE_NAME_name_LENGTH;
		if (file_name != null) {
			str = file_name;
		} else if (repository.file_name.name != null) {
			str = repository.file_name.name;
			if (str.length() >= TEMP_REPO_NAME_LENGTH && str.substring(0, TEMP_REPO_NAME_LENGTH).equals(TEMP_REPO_NAME)) {
				str = " ";
			}
		} else {
			str = repository.name;
			if (str.length() >= TEMP_REPO_NAME_LENGTH && str.substring(0, TEMP_REPO_NAME_LENGTH).equals(TEMP_REPO_NAME)) {
				str = " ";
			}
		}

/*		int count = 0;
		char sym;
		for (i = 0; i < str.length(); i++) {
			sym = str.charAt(i);
			if (sym == PhFileReader.BACKSLASH) {
				count++;
			}
		}
		int lng = str.length() + count;
		if (string_val.length < lng) {
			string_val = new byte[lng];
		}
		count = 0;
		for (i = 0; i < str.length(); i++) {
			sym = str.charAt(i);
			string_val[count] = (byte)sym;
			if (sym == PhFileReader.BACKSLASH) {
				count++;
				string_val[count] = PhFileReader.BACKSLASH;
			}
			count++;
		}
		write_string(string_val, lng, true);*/
		row_length = print_inst.write_byte_array(true, output_stream, str, row_length);
		output_stream.write(PhFileReader.COMMA_b);
		output_stream.write(RETURN);

		writeString(FILE_NAME_time_stamp);
		row_length = FILE_NAME_time_stamp_LENGTH;
		write_string(repository.file_name.time_stamp, true);
		output_stream.write(PhFileReader.COMMA_b);
		output_stream.write(RETURN);
		writeString(FILE_NAME_author);
		row_length = FILE_NAME_author_LENGTH;
		A_string author = repository.file_name.author;
		if (author != null && author.myLength > 0) {
			expand_list(author, true, null);
		} else {
			expand_array(repository.session.author, true);
		}
		output_stream.write(PhFileReader.COMMA_b);
		output_stream.write(RETURN);
		writeString(FILE_NAME_organization);
		row_length = FILE_NAME_organization_LENGTH;
		A_string organization = repository.file_name.organization;
		if (organization != null && organization.myLength > 0) {
			expand_list(organization, true, null);
		} else {
			expand_array(repository.session.organization, true);
		}
		output_stream.write(PhFileReader.COMMA_b);
		output_stream.write(RETURN);
		writeString(FILE_NAME_preprocessor_version);
		row_length = FILE_NAME_preprocessor_version_LENGTH;
		str = repository.file_name.preprocessor_version;
		if (str != null) {
			row_length = print_inst.write_byte_array(true, output_stream, str, row_length);
		}
		output_stream.write(PhFileReader.COMMA_b);
		output_stream.write(RETURN);
		writeString(FILE_NAME_originating_system);
		row_length = FILE_NAME_originating_system_LENGTH;
		str = repository.file_name.originating_system;
		if (str != null) {
			row_length = print_inst.write_byte_array(true, output_stream, str, row_length);
		}
		output_stream.write(PhFileReader.COMMA_b);
		output_stream.write(RETURN);
		writeString(FILE_NAME_authorization);
		row_length = FILE_NAME_authorization_LENGTH;
		str = repository.file_name.authorization;
		if (str != null) {
			row_length = print_inst.write_byte_array(true, output_stream, str, row_length);
		}
		output_stream.write(PhFileReader.RIGHT_PARENTHESIS);
		output_stream.write(PhFileReader.SEMICOLON_b);
		output_stream.write(RETURN);

		FILE_SCHEMA file_schema = repository.file_schema;
		boolean include_FILE_SCHEMA = false;
		String saved_schema = null;
		if (file_schema != null) {
			if (file_schema.schema != null) {
				if (verify_schema(file_schema)) {
					saved_schema = file_schema.schema;
					file_schema.schema = null;
					lst = (A_string)file_schema.schema_identifiers_temp;
				}
				include_FILE_SCHEMA = true;
			} else {
				lst = (A_string)file_schema.schema_identifiers_temp;
				if (lst != null && lst.myLength > 0) {
					include_FILE_SCHEMA = true;
				}
			}
		}
		if (include_FILE_SCHEMA) {
			output_stream.write(header_entities[2]);
			output_stream.write(PhFileReader.LEFT_PARENTHESIS);
			row_length = 12;
			String schema_name;
			if (file_schema.schema != null) {
				output_stream.write(PhFileReader.LEFT_PARENTHESIS);
				str = repository.session.getIdentification(file_schema.schema);
				if (str == null) {
					schema_name = file_schema.schema;
				} else {
					schema_name = file_schema.schema + " " + str;
				}
//				write_string(file_schema.schema, true);
				write_string(schema_name, true);
				output_stream.write(PhFileReader.RIGHT_PARENTHESIS);
			} else {
//				if (file_schema != null) {
//					lst = (A_string)file_schema.schema_identifiers_temp;
//				}
				expand_list(lst, true, repository);
				file_schema.schema = saved_schema;
			}
			output_stream.write(PhFileReader.RIGHT_PARENTHESIS);
			output_stream.write(PhFileReader.SEMICOLON_b);
			output_stream.write(RETURN);
		} else {
			if (repository.models.myLength > 0) {
				String base = SdaiSession.line_separator + AdditionalMessages.WR_NOSC;
				throw new SdaiException(SdaiException.SY_ERR, base);
			} else {
				printWarningToLogo(repository);
			}
		}

		for (i = 0; i < repository.languages_count; i++) {
			SECTION_LANGUAGE language = repository.languages[i];
			output_stream.write(header_entities[3]);
			output_stream.write(PhFileReader.LEFT_PARENTHESIS);
			row_length = 17;
			if (language != null) {
				str = language.section;
				if (str == null || repository.models.myLength <= 1) {
					output_stream.write(PhFileReader.DOLLAR_SIGN);
				} else {
					if(imp_level <= 2) {
						str = "default";
					}
					write_string(str, true);
				}
				str = language.default_language;
				if (str != null) {
//					write_string(str, false);
					row_length = print_inst.write_byte_array(false, output_stream, str, row_length);
				}
			}
			output_stream.write(PhFileReader.RIGHT_PARENTHESIS);
			output_stream.write(PhFileReader.SEMICOLON_b);
			output_stream.write(RETURN);
		}
		for (i = 0; i < repository.contexts_count; i++) {
			SECTION_CONTEXT context = repository.contexts[i];
			output_stream.write(header_entities[4]);
			output_stream.write(PhFileReader.LEFT_PARENTHESIS);
			row_length = 16;
			if (context != null) {
				str = context.section;
				if (str == null || repository.models.myLength <= 1) {
					output_stream.write(PhFileReader.DOLLAR_SIGN);
				} else {
					if(imp_level <= 2) {
						str = "default";
					}
					write_string(str, true);
				}
				lst = (A_string)context.context_identifiers;
			}
			expand_list(lst, false, null);
			output_stream.write(PhFileReader.RIGHT_PARENTHESIS);
			output_stream.write(PhFileReader.SEMICOLON_b);
			output_stream.write(RETURN);
		}
		if (imp_level <= 2) {
			return;
		}
		for (i = 0; i < repository.schemas.myLength; i++) {
			SchemaInstance sch_inst = (SchemaInstance)repository.schemas.myData[i];
			output_stream.write(header_entities[5]);
			output_stream.write(PhFileReader.LEFT_PARENTHESIS);

			write_string_comment(sch_inst.name, 0, PhFileReader.APOSTROPHE);
			print_inst.write_byte_array_simple(true, sch_inst.getNativeSchemaString().toUpperCase(),
				PhFileReader.APOSTROPHE, true, output_stream);
			print_inst.write_byte_array_simple(false, native_schema_comment, (byte)' ', false, output_stream);
			print_inst.write_byte_array_simple(true, default_determination_method, PhFileReader.APOSTROPHE,
				true, output_stream);
			print_inst.write_byte_array_simple(false, determination_method_comment, (byte)' ', false, output_stream);
			if (sch_inst.isRemote()) {
				sch_inst.getAssociatedModelsPrivate();
			}
			if (sch_inst.associated_models == null || repository.isComplete(sch_inst.associated_models)) {
				output_stream.write(PhFileWriter.RETURN);
				output_stream.write(PhFileWriter.INDENT);
				output_stream.write(PhFileReader.DOLLAR_SIGN);
			} else {
				expand_set_model_names(sch_inst.associated_models, repository, true, true);
			}
			print_inst.write_byte_array_simple(true, governed_sections_comment, (byte)' ', false, output_stream);
			if (sch_inst.included_schemas == null) {
				sch_inst.getIncludedSchemaInstances();
			}
			write_sch_inst_comment(sch_inst.included_schemas, 13);
			if (sch_inst.change_date > 0) {
				str = repository.session.cal.longToTimeStamp(sch_inst.change_date);
				write_string_comment(str, 1, PhFileReader.APOSTROPHE);
			}
			if (sch_inst.validation_date > 0) {
				str = repository.session.cal.longToTimeStamp(sch_inst.validation_date);
				write_string_comment(str, 2, PhFileReader.APOSTROPHE);
			}
			if (sch_inst.validation_result == 1) {
				str = ".F.";
			} else if (sch_inst.validation_result == 2) {
				str = ".T.";
			} else if (sch_inst.validation_result == 3) {
				str = ".U.";
			} else if (sch_inst.validation_result == 0) {
				str = null;
			} else {
// print warning
				str = null;
			}
			if (str != null) {
				write_string_comment(str, 3, (byte)' ');
			}
			str = Integer.toString(sch_inst.validation_level);
			write_string_comment(str, 4, (byte)' ');
			write_aggr_comment(sch_inst.description, 5);
			write_aggr_comment(sch_inst.author, 6);
			write_aggr_comment(sch_inst.organization, 7);
			if (sch_inst.preprocessor_version != null) {
				write_string_comment(sch_inst.preprocessor_version, 8, PhFileReader.APOSTROPHE);
			}
			if (sch_inst.originating_system != null) {
				write_string_comment(sch_inst.originating_system, 9, PhFileReader.APOSTROPHE);
			}
			if (sch_inst.authorization != null) {
				write_string_comment(sch_inst.authorization, 10, PhFileReader.APOSTROPHE);
			}
			if (sch_inst.language != null) {
				write_string_comment(sch_inst.language, 11, PhFileReader.APOSTROPHE);
			}
			write_aggr_comment(sch_inst.context_identifiers, 12);

			output_stream.write(PhFileReader.RIGHT_PARENTHESIS);
			output_stream.write(PhFileReader.SEMICOLON_b);
			output_stream.write(RETURN);
		}
	}


	private boolean verify_schema(FILE_SCHEMA file_schema) throws SdaiException {
		A_string lst = (A_string)file_schema.schema_identifiers_temp;
		if (lst == null || lst.myLength <= 0) {
			return false;
		}
		String sch = file_schema.schema;
		boolean dif = false;
		for (int i = 1; i <= lst.myLength; i++) {
			String str = lst.getByIndex(i);
			if (!str.equals(sch)) {
				dif = true;
				break;
			}
		}
		return dif;
	}


	private void write_string_comment(String str, int index, byte sym) throws java.io.IOException, SdaiException {
		print_inst.write_byte_array_simple(true, comment_start, (byte)' ', true, output_stream);
		print_inst.write_byte_array_simple(true, str, sym, false, output_stream);
		print_inst.write_byte_array_simple(true, separation, (byte)' ', false, output_stream);
		print_inst.write_byte_array_simple(true, PhFileReader.SCH_INST_HIDDEN_FIELDS[index], (byte)' ',
			false, output_stream);
		print_inst.write_byte_array_simple(true, comment_end, (byte)' ', false, output_stream);
	}

	private void write_aggr_comment(A_string aggr, int index) throws java.io.IOException, SdaiException {
		print_inst.write_byte_array_simple(true, comment_start, (byte)' ', true, output_stream);
		if (aggr != null && aggr.myLength > 0) {
			expand_list_simple(aggr, true, false);
		} else {
			output_stream.write(PhFileReader.LEFT_PARENTHESIS);
			write_string(" ", true);
			output_stream.write(PhFileReader.RIGHT_PARENTHESIS);
		}
		print_inst.write_byte_array_simple(true, separation, (byte)' ', false, output_stream);
		print_inst.write_byte_array_simple(true, PhFileReader.SCH_INST_HIDDEN_FIELDS[index], (byte)' ',
			false, output_stream);
		print_inst.write_byte_array_simple(true, comment_end, (byte)' ', false, output_stream);
	}

	private void write_sch_inst_comment(ASchemaInstance incl_schemas, int index) throws java.io.IOException, SdaiException {
		if (incl_schemas == null || incl_schemas.myLength == 0) {
			return;
		}
		print_inst.write_byte_array_simple(true, comment_start, (byte)' ', true, output_stream);
		boolean first_inside = true;
		for (int i = 0; i < incl_schemas.myLength; i++) {
			SchemaInstance sch_inst = (SchemaInstance)incl_schemas.myData[i];
			if (first_inside) {
				output_stream.write(PhFileReader.LEFT_PARENTHESIS);
				print_inst.write_byte_array_simple(true, sch_inst.name, PhFileReader.APOSTROPHE, false, output_stream);
				first_inside = false;
			} else {
				print_inst.write_byte_array_simple(false, sch_inst.name, PhFileReader.APOSTROPHE, false, output_stream);
			}
		}
		output_stream.write(PhFileReader.RIGHT_PARENTHESIS);
		print_inst.write_byte_array_simple(true, separation, (byte)' ', false, output_stream);
		print_inst.write_byte_array_simple(true, PhFileReader.SCH_INST_HIDDEN_FIELDS[index], (byte)' ',
			false, output_stream);
		print_inst.write_byte_array_simple(true, comment_end, (byte)' ', false, output_stream);
	}

	private void print_header_user_defined(SdaiRepository repository)
			throws java.io.IOException, SdaiException {
		$HEADER_USER_DEFINED_ENTITY [] user_def_insts = repository.user_defined_instances;
		for (int i = 0; i < repository.user_defined_instances_count; i++) {
			EntityValue inst_in_header = user_def_insts[i].read();
			byte [] name = user_def_insts[i].get_name();
			output_stream.write(name, 0, name.length);
			row_length = name.length;
			print_inst.print_instance(inst_in_header, output_stream, row_length, true);
		}
	}

	private void print_data_section(SdaiModel model) throws java.io.IOException, SdaiException {
		long time_a = 0, time_b, total =0;
		long time_in_sec;
		CEntity app_instance;
		CEntity_definition def;
		int index;
		SdaiRepository rep = model.repository;
		SdaiModel derived_model_dict = null;

		boolean short_names = !model.repository.suppress_short_names;
		A_string list;
		ListElement new_element;
		if (model.underlying_schema == null) {
			model.getUnderlyingSchema();
		}
		model_dict = model.underlying_schema.modelDictionary;
		output_stream.write(PhFileReader.keywords[0]);
		if (rep.models.myLength > 1) {
			output_stream.write(PhFileReader.LEFT_PARENTHESIS);
//			row_length = 5;
			row_length = print_inst.write_byte_array(true, output_stream, model.name, 5);
//			write_string(model.name, true);
			if (model.schema_name == null) {
				if (model.repository == SdaiSession.systemRepository) {
					if (dict_sch_name == null) {
						dict_sch_name = model_dict.name;
						int name_length = dict_sch_name.length();
						if (name_length > 15) {
							dict_sch_name = dict_sch_name.substring(0, name_length - 16);
						} else {
							dict_sch_name = null;
						}
					}
					if (dict_sch_name != null) {
						list = new A_string(SdaiSession.listTypeSpecial, null);
						list.myData = dict_sch_name;
						list.myLength = 1;
						expand_list(list, false, null);
					} else {
						output_stream.write(PhFileReader.COMMA_b);
						output_stream.write(PhFileReader.DOLLAR_SIGN);
					}
				} else {
					output_stream.write(PhFileReader.COMMA_b);
					output_stream.write(PhFileReader.DOLLAR_SIGN);
				}
			} else {
				list = new A_string(SdaiSession.listTypeSpecial, null);
				list.myData = model.schema_name;
				list.myLength = 1;
				expand_list(list, false, null);
			}
			output_stream.write(PhFileReader.RIGHT_PARENTHESIS);
		}
		output_stream.write(PhFileReader.SEMICOLON_b);
		output_stream.write(RETURN);
		app_instance = get_inst.get_next();
		while (app_instance != null) {
			def = (CEntity_definition)app_instance.getInstanceType();
			take_instance_id(app_instance.instance_identifier);
			output_stream.write(instance_id, 0, instance_id_length);
			output_stream.write(PhFileReader.EQUAL);
			row_length = instance_id_length + 1;
			time_a = System.currentTimeMillis();
			SchemaData schemaData = null;
			if (app_instance instanceof $USER_DEFINED_ENTITY) {
// do nothing
			} else {
				if (app_instance instanceof CEntity) {
					app_instance.owning_model.prepareAll(entity_values, def);
					derived_model_dict = ((CEntity)def).owning_model.underlying_schema.modelDictionary;
				} else {
					derived_model_dict = model_dict;
				}
				schemaData = ((CEntity)def).owning_model.schemaData;
			}
			app_instance.getAll(entity_values);
if (SdaiSession.debug2) print_entity_values(app_instance.instance_identifier);
			time_b = System.currentTimeMillis();total+=(time_b - time_a);
			byte [] name;
			int name_length;
			if (app_instance instanceof $USER_DEFINED_ENTITY) {
				$USER_DEFINED_ENTITY user_inst = ($USER_DEFINED_ENTITY)app_instance;
				name_length = user_inst.name.length();
				name = user_inst.get_name();
//				name = new byte[name_length];
//				for (int k = 0; k < name_length; k++) {
//					name[k] = (byte)str.charAt(k);
//				}
				print_inst.print_instance(entity_values, name, name_length, output_stream, row_length);
			} else if (def.complex == 2) {
				print_inst.print_instance(entity_values, short_names, schemaData, output_stream, row_length);
			} else {
if (SdaiSession.debug2) System.out.println("  WRITER instance: #" +  app_instance.instance_identifier);
if (SdaiSession.debug2) System.out.println("  WRITER instance name: " + def.getCorrectName());
if (SdaiSession.debug2) System.out.println("  WRITER owning model: " + derived_model_dict.name);
				index = schemaData.find_entity(0, schemaData.bNames.length - 1,
//					def.getNameUpperCase());
					(CEntityDefinition)def);
if (SdaiSession.debug2) System.out.println("  WRITER index = " + index);
				if (index < 0 || index >= schemaData.bNames.length) {
					String base = SdaiSession.line_separator + AdditionalMessages.WR_OUT +
						SdaiSession.line_separator + "     " +
						(String)error_table.messages.get(new Integer(ENTITY_NOT_FOUND_FOR_WRITER)
						+ " Name: " +
							((CEntityDefinition)app_instance.getInstanceType()).name);
					throw new SdaiException(SdaiException.SY_ERR, base);
				}
				if (short_names) {
					int index_short = schemaData.toShort[index];
					if (index_short >= 0) {
						name =  schemaData.bShortNames[index_short];
						name_length = schemaData.bShortNames[index_short].length;
					} else {
						name =  schemaData.bNames[index];
						name_length = schemaData.bNames[index].length;
					}
				} else {
					name =  schemaData.bNames[index];
					name_length = schemaData.bNames[index].length;
				}
				print_inst.print_instance(entity_values, name, name_length, output_stream, row_length);
			}
			app_instance = get_inst.get_next();
		}
		time_in_sec = total / 1000;
//		SdaiSession.println("--- time for getting values=" + time_in_sec + "sec");
	}

	private void print_data_section_user_defined() throws java.io.IOException, SdaiException
	{
		CEntity app_instance;

		app_instance = get_inst.get_next_user_defined();
		while (app_instance != null) {
			take_instance_id(app_instance.instance_identifier);
			output_stream.write(instance_id, 0, instance_id_length);
			output_stream.write(PhFileReader.EQUAL);
			row_length = instance_id_length + 1;
			$USER_DEFINED_ENTITY user_inst = ($USER_DEFINED_ENTITY)app_instance;
			byte [] name = user_inst.get_name();
			output_stream.write(name, 0, name.length);
			row_length += name.length;
			user_inst.getAll(entity_values);
			print_inst.print_instance(entity_values.entityValues[0], output_stream, row_length, false);
			app_instance = get_inst.get_next_user_defined();
		}
	}

	private void print_close() throws java.io.IOException
	{
		if(output_stream != null) {
			output_stream.flush();
			output_stream.close();
		}
	}


	private void write_string(String str, boolean first) throws java.io.IOException {
		int next_row;
		byte [] bytes = string_to_byte(str);
		if (!first) {
			output_stream.write(PhFileReader.COMMA_b);
			row_length++;
		}
		if (row_length >= ROW_LENGTH_LIMIT) {
			next_row = 1;
		} else {
			next_row = 0;
		}
		switch (next_row) {
			case 0:
				output_stream.write(PhFileReader.APOSTROPHE);
				output_stream.write(bytes);
				output_stream.write(PhFileReader.APOSTROPHE);
				row_length += bytes.length+2;
				break;
			case 1:
				output_stream.write(RETURN);
				output_stream.write(INDENT);
				output_stream.write(PhFileReader.APOSTROPHE);
				output_stream.write(bytes);
				output_stream.write(PhFileReader.APOSTROPHE);
				row_length = bytes.length + INDENT_LENGTH + 2;
				break;
		}
	}


	private void write_string(byte [] bytes, int string_length, boolean first) throws java.io.IOException {
		int i;
		int next_row;
		if (!first) {
			output_stream.write(PhFileReader.COMMA_b);
			row_length++;
		}
		if (row_length >= ROW_LENGTH_LIMIT) {
			next_row = 1;
		} else {
			next_row = 0;
		}
		switch (next_row) {
			case 0:
				output_stream.write(PhFileReader.APOSTROPHE);
				for (i = 0; i < string_length; i++) {
					output_stream.write(bytes[i]);
				}
				output_stream.write(PhFileReader.APOSTROPHE);
				row_length += string_length+2;
				break;
			case 1:
				output_stream.write(RETURN);
				output_stream.write(INDENT);
				output_stream.write(PhFileReader.APOSTROPHE);
				for (i = 0; i < string_length; i++) {
					output_stream.write(bytes[i]);
				}
				output_stream.write(PhFileReader.APOSTROPHE);
				row_length = string_length + INDENT_LENGTH + 2;
				break;
		}
	}


	private void expand_list(A_string list, boolean first, SdaiRepository repo)
			throws java.io.IOException, SdaiException {
		boolean first_inside;
		int next_row;
		String str;

		if (!first) {
			output_stream.write(PhFileReader.COMMA_b);
			row_length++;
		}
		if (list != null && list.myLength > 0) {
			first_inside = true;
			for (int i = 1; i <= list.myLength; i++) {
				str = list.getByIndex(i);
				if (repo != null) {
					String id = repo.session.getIdentification(str);
					if (id != null) {
						str = str + " " + id;
					}
				}
				if (row_length >= ROW_LENGTH_LIMIT) {
					next_row = 1;
				}	else {
					next_row = 0;
				}
				if (first_inside) {
					switch (next_row) {
						case 0:
							output_stream.write(PhFileReader.LEFT_PARENTHESIS);
							row_length++;
							row_length = print_inst.write_byte_array(true, output_stream, str, row_length);
							break;
						case 1:
							output_stream.write(RETURN);
							output_stream.write(INDENT);
							output_stream.write(PhFileReader.LEFT_PARENTHESIS);
							row_length = print_inst.write_byte_array(true, output_stream, str, INDENT_LENGTH + 1);
							break;
					}
				first_inside = false;
				} else {
					switch (next_row) {
						case 0:
							row_length = print_inst.write_byte_array(false, output_stream, str, row_length);
							break;
						case 1:
							output_stream.write(PhFileReader.COMMA_b);
							output_stream.write(RETURN);
							output_stream.write(INDENT);
							row_length = print_inst.write_byte_array(true, output_stream, str, INDENT_LENGTH);
							break;
					}
				}
			}
			output_stream.write(PhFileReader.RIGHT_PARENTHESIS);
		} else {
			output_stream.write(PhFileReader.LEFT_PARENTHESIS);
			output_stream.write(PhFileReader.RIGHT_PARENTHESIS);
		}
	}


	private void expand_array(String [] arr, boolean first) throws java.io.IOException, SdaiException {
		boolean first_inside;
		int next_row;
		String str;

		if (!first) {
			output_stream.write(PhFileReader.COMMA_b);
			row_length++;
		}
		if (arr != null && arr.length > 0) {
			first_inside = true;
			for (int i = 0; i < arr.length; i++) {
				str = arr[i];
				if (row_length >= ROW_LENGTH_LIMIT) {
					next_row = 1;
				}	else {
					next_row = 0;
				}
				if (first_inside) {
					switch (next_row) {
						case 0:
							output_stream.write(PhFileReader.LEFT_PARENTHESIS);
							row_length++;
							row_length = print_inst.write_byte_array(true, output_stream, str, row_length);
							break;
						case 1:
							output_stream.write(RETURN);
							output_stream.write(INDENT);
							output_stream.write(PhFileReader.LEFT_PARENTHESIS);
							row_length = print_inst.write_byte_array(true, output_stream, str, INDENT_LENGTH + 1);
							break;
					}
				first_inside = false;
				} else {
					switch (next_row) {
						case 0:
							row_length = print_inst.write_byte_array(false, output_stream, str, row_length);
							break;
						case 1:
							output_stream.write(PhFileReader.COMMA_b);
							output_stream.write(RETURN);
							output_stream.write(INDENT);
							row_length = print_inst.write_byte_array(true, output_stream, str, INDENT_LENGTH);
							break;
					}
				}
			}
			output_stream.write(PhFileReader.RIGHT_PARENTHESIS);
		} else {
			output_stream.write(PhFileReader.LEFT_PARENTHESIS);
			output_stream.write(PhFileReader.RIGHT_PARENTHESIS);
		}
	}


	private void expand_list_simple(A_string list, boolean first, boolean next_row)
			throws java.io.IOException, SdaiException {
		boolean first_inside;
		String str;

		if (!first) {
			output_stream.write(PhFileReader.COMMA_b);
		}
		if (next_row) {
			output_stream.write(RETURN);
			output_stream.write(INDENT);
		}
		if (list != null && list.myLength > 0) {
			first_inside = true;
			for (int i = 1; i <= list.myLength; i++) {
				str = list.getByIndex(i);
				if (first_inside) {
					output_stream.write(PhFileReader.LEFT_PARENTHESIS);
					print_inst.write_byte_array_simple(true, str, PhFileReader.APOSTROPHE, false, output_stream);
					first_inside = false;
				} else {
					print_inst.write_byte_array_simple(false, str, PhFileReader.APOSTROPHE, false, output_stream);
				}
			}
			output_stream.write(PhFileReader.RIGHT_PARENTHESIS);
		} else {
			output_stream.write(PhFileReader.LEFT_PARENTHESIS);
			output_stream.write(PhFileReader.RIGHT_PARENTHESIS);
		}
	}


	private void expand_set_model_names(ASdaiModel aggr, SdaiRepository repository, boolean first,
			boolean next_row) throws java.io.IOException, SdaiException {
		if (!first) {
			output_stream.write(PhFileReader.COMMA_b);
		}
		if (next_row) {
			output_stream.write(RETURN);
			output_stream.write(INDENT);
		}
		if (aggr != null && aggr.myLength > 0) {
			boolean first_inside = true;
			for (int i = 0; i < aggr.myLength; i++) {
				SdaiModel mod = (SdaiModel)aggr.myData[i];
				if (mod.repository != repository) {
					continue;
				}
				if (first_inside) {
					output_stream.write(PhFileReader.LEFT_PARENTHESIS);
					print_inst.write_byte_array_simple(true, mod.name, PhFileReader.APOSTROPHE, false, output_stream);
					first_inside = false;
				} else {
					print_inst.write_byte_array_simple(false, mod.name, PhFileReader.APOSTROPHE, false, output_stream);
				}
			}
			output_stream.write(PhFileReader.RIGHT_PARENTHESIS);
		} else {
			output_stream.write(PhFileReader.LEFT_PARENTHESIS);
			output_stream.write(PhFileReader.RIGHT_PARENTHESIS);
		}
	}


	private void take_instance_id(long lo) {
		byte sym;
		int digit_index = -1;
		long next_number;
		while (lo != 0) {
			next_number = lo / 10;
			digit_index++;
			instance_id[digit_index] = DIGITS[(int)(lo - next_number * 10)];
			lo = next_number;
		}
		instance_id[++digit_index] = PhFileReader.SPECIAL;
		for (int i = 0; i <= (digit_index - 1) / 2; i++) {
			sym = instance_id[i];
			instance_id[i] = instance_id[digit_index - i];
			instance_id[digit_index - i] = sym;
		}
		instance_id_length = digit_index + 1;
	}

	private boolean check_schema_name(A_string sch_name_aggr, String sch_name)
			throws SdaiException {
		for (int i = 1; i <= sch_name_aggr.myLength; i++) {
			String str = (String)sch_name_aggr.getByIndexObject(i);
			if (str.equals(sch_name)) {
				return false;
			}
		}
		return true;
	}

	private byte [] string_to_byte(String str) {
		byte [] bytes = new byte[str.length()];

		for (int l = 0; l < str.length(); l++)
		{
			bytes[l] = (byte)str.charAt(l);
		}
		return bytes;
	}

	static void printWarningToLogo(SdaiRepository repository) throws SdaiException {
		String base = AdditionalMessages.WR_OSC1 + repository.name + AdditionalMessages.WR_OSC2;
		SdaiSession ss = repository.session;
		if (ss.logWriterSession != null) {
			ss.printlnSession(base);
		} else {
			SdaiSession.println(base);
		}
	}

	private void print_entity_values(long instance_identifier) {
		int count = entity_values.def.noOfPartialEntityTypes;
		System.out.println("INSTANCE: #" + instance_identifier);
		for (int i = 0; i < count; i++) {
			EntityValue partval = entity_values.entityValues[i];
			System.out.println("****** partial entity no. " + i);
			for (int j = 0; j < partval.count; j++) {
				print_value(partval.values[j]);
				System.out.println("");
			}
		}
	}
	private void print_value(Value val) {
		Value value_next;
		String str;
		switch (val.tag) {
			case PhFileReader.MISSING:
				System.out.print("$  ");
				break;
			case PhFileReader.REDEFINE:
				System.out.print("*  ");
				break;
			case PhFileReader.INTEGER:
				System.out.print(val.integer + "  ");
				break;
			case PhFileReader.REAL:
				System.out.print(val.real + "  ");
				break;
			case PhFileReader.LOGICAL:
				if (val.integer == 0) {
					System.out.print(".F.  ");
				} else if (val.integer == 1) {
					System.out.print(".T.  ");
				} else {
					System.out.print(".U.  ");
				}
				break;
			case PhFileReader.ENUM:
				System.out.print("." + val.string + ".  ");
				break;
			case PhFileReader.STRING:
				System.out.print("'" + val.string + "'  ");
				break;
			case PhFileReader.BINARY:
				System.out.print("\"" + ((Binary)val.reference).toString() + "\"  ");
				break;
			case PhFileReader.TYPED_PARAMETER:
				System.out.print(val.string + "(");
				value_next = val.nested_values[0];
				print_value(value_next);
				System.out.print(")  ");
				break;
			case PhFileReader.ENTITY_REFERENCE:
				Object ref = val.reference;
				System.out.print(ref + "  ");
//				String ref_class = ref.getClass().getName();
//				System.out.print(ref_class + "  ");
//				System.out.print("REF  ");
				break;
			case PhFileReader.EMBEDDED_LIST:
				Aggregate agg = (Aggregate)val.reference;
				if (agg instanceof CAggregate) {
					System.out.println("Aggregate: " + ((CAggregate)agg).toString());
				} else if (agg instanceof A_double3) {
					System.out.println("Aggregate: " + ((A_double3)agg).toString());
				} else if (agg instanceof A_double) {
					System.out.println("Aggregate: " + ((A_double)agg).toString());
				} else if (agg instanceof A_integerPrimitive) {
					System.out.println("Aggregate: " + ((A_integerPrimitive)agg).toString());
				}
/*				System.out.print("(");
				for (int i = 0; i < val.length; i++) {
					value_next = val.nested_values[i];
					print_value(value_next);
				}
				System.out.print(")  ");*/
				break;
			}
	}

}
