# -*- coding: utf-8 -*-
#
# AWL simulator - project
#
# Copyright 2014 Michael Buesch <m@bues.ch>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#

from __future__ import division, absolute_import, print_function, unicode_literals
from awlsim.common.compat import *

from awlsim.common.cpuspecs import *
from awlsim.common.util import *

from awlsim.library.libselection import *

import base64, binascii
import datetime
import os
import hashlib

if isPy2Compat:
	from ConfigParser import SafeConfigParser as _ConfigParser
	from ConfigParser import Error as _ConfigParserError
else:
	from configparser import ConfigParser as _ConfigParser
	from configparser import Error as _ConfigParserError


class GenericSource(object):
	SRCTYPE		= "<generic>"
	IDENT_HASH	= "sha256"

	def __init__(self, name="", filepath="", sourceBytes=b""):
		self.name = name
		self.filepath = filepath
		self.sourceBytes = sourceBytes
		self.__identHash = None

	@property
	def name(self):
		return self.__name

	@name.setter
	def name(self, newName):
		self.__name = newName
		self.__identHash = None

	@property
	def filepath(self):
		return self.__filepath

	@filepath.setter
	def filepath(self, newFilepath):
		self.__filepath = newFilepath
		self.__identHash = None

	@property
	def sourceBytes(self):
		return self.__sourceBytes

	@sourceBytes.setter
	def sourceBytes(self, newSourceBytes):
		self.__sourceBytes = newSourceBytes
		self.__identHash = None

	@property
	def identHash(self):
		if not self.__identHash:
			# Calculate the ident hash
			h = hashlib.new(self.IDENT_HASH, self.SRCTYPE.encode("utf-8"))
			if self.name is not None:
				h.update(self.name.encode("utf-8"))
			if self.filepath is not None:
				h.update(self.filepath.encode("utf-8"))
			h.update(self.sourceBytes)
			self.__identHash = h.digest()
		return self.__identHash

	@identHash.setter
	def identHash(self, identHash):
		# Force the ident hash.
		self.__identHash = identHash

	@property
	def identHashStr(self):
		return binascii.b2a_hex(self.identHash).decode("ascii")

	def dup(self):
		raise NotImplementedError

	def isFileBacked(self):
		return bool(self.filepath)

	def writeFileBacking(self):
		"Write the backing file, if any."
		if not self.isFileBacked():
			return
		awlFileWrite(self.filepath, self.sourceBytes, encoding="binary")

	def forceNonFileBacked(self, newName):
		"Convert this source to a non-file-backed source."
		if self.isFileBacked():
			self.filepath = ""
			self.name = newName

	def toBase64(self):
		return base64.b64encode(self.sourceBytes).decode("ascii")

	@classmethod
	def fromFile(cls, name, filepath):
		try:
			data = awlFileRead(filepath, encoding="binary")
		except AwlSimError as e:
			raise AwlSimError("Project: Could not read %s "
				"source file '%s':\n%s" %\
				(cls.SRCTYPE, filepath, str(e)))
		return cls(name, filepath, data)

	@classmethod
	def fromBase64(cls, name, b64):
		try:
			data = base64.b64decode(b64.encode("ascii"))
		except (TypeError, binascii.Error, UnicodeError) as e:
			raise AwlSimError("Project: %s source '%s' "
				"has invalid base64 encoding." %\
				(cls.SRCTYPE, name))
		return cls(name, None, data)

	def __repr__(self):
		return "%s%s %s %s" % ("" if self.isFileBacked() else "project ",
				    self.SRCTYPE, self.name, self.identHashStr)

class AwlSource(GenericSource):
	SRCTYPE = "AWL/STL"

	def dup(self):
		return AwlSource(self.name, self.filepath,
				 self.sourceBytes[:])

class SymTabSource(GenericSource):
	SRCTYPE = "symbol table"

	def dup(self):
		return SymTabSource(self.name, self.filepath,
				    self.sourceBytes[:])

class GuiSettings(object):
	def __init__(self,
		     editorAutoIndentEn=True,
		     editorPasteIndentEn=True,
		     editorValidationEn=True,
		     editorFont=""):
		self.setEditorAutoIndentEn(editorAutoIndentEn)
		self.setEditorPasteIndentEn(editorPasteIndentEn)
		self.setEditorValidationEn(editorValidationEn)
		self.setEditorFont(editorFont)

	def setEditorAutoIndentEn(self, editorAutoIndentEn):
		self.editorAutoIndentEn = editorAutoIndentEn

	def getEditorAutoIndentEn(self):
		return self.editorAutoIndentEn

	def setEditorPasteIndentEn(self, editorPasteIndentEn):
		self.editorPasteIndentEn = editorPasteIndentEn

	def getEditorPasteIndentEn(self):
		return self.editorPasteIndentEn

	def setEditorValidationEn(self, editorValidationEn):
		self.editorValidationEn = editorValidationEn

	def getEditorValidationEn(self):
		return self.editorValidationEn

	def setEditorFont(self, editorFont):
		self.editorFont = editorFont

	def getEditorFont(self):
		return self.editorFont

class CoreLinkSettings(object):
	def __init__(self,
		     spawnLocalEn=True,
		     spawnLocalPortRange=range(4151, 4151 + 4095 + 1),
		     spawnLocalInterpreters="pypy3; pypy; $CURRENT; "\
					    "python3; python2; python; py",
		     connectHost="localhost",
		     connectPort=4151,
		     connectTimeoutMs=3000):
		self.setSpawnLocalEn(spawnLocalEn)
		self.setSpawnLocalPortRange(spawnLocalPortRange)
		self.setSpawnLocalInterpreters(spawnLocalInterpreters),
		self.setConnectHost(connectHost)
		self.setConnectPort(connectPort)
		self.setConnectTimeoutMs(connectTimeoutMs)

	def setSpawnLocalEn(self, spawnLocalEn):
		self.spawnLocalEn = spawnLocalEn

	def getSpawnLocalEn(self):
		return self.spawnLocalEn

	def setSpawnLocalPortRange(self, spawnLocalPortRange):
		self.spawnLocalPortRange = spawnLocalPortRange

	def getSpawnLocalPortRange(self):
		return self.spawnLocalPortRange

	def setSpawnLocalInterpreters(self, spawnLocalInterpreters):
		self.spawnLocalInterpreters = spawnLocalInterpreters

	def getSpawnLocalInterpreters(self):
		return self.spawnLocalInterpreters

	def getSpawnLocalInterpreterList(self, replace=True):
		ret = []
		for interp in self.getSpawnLocalInterpreters().split(';'):
			interp = interp.strip()
			if replace:
				if interp == "$CURRENT":
					interp = sys.executable
			ret.append(interp)
		return ret

	def setConnectHost(self, connectHost):
		self.connectHost = connectHost

	def getConnectHost(self):
		return self.connectHost

	def setConnectPort(self, connectPort):
		self.connectPort = connectPort

	def getConnectPort(self):
		return self.connectPort

	def setConnectTimeoutMs(self, connectTimeoutMs):
		self.connectTimeoutMs = connectTimeoutMs

	def getConnectTimeoutMs(self):
		return self.connectTimeoutMs

class Project(object):
	DATETIME_FMT	= "%Y-%m-%d %H:%M:%S.%f"

	def __init__(self, projectFile,
		     createDate=None,
		     modifyDate=None,
		     awlSources=[],
		     symTabSources=[],
		     libSelections=[],
		     cpuSpecs=None,
		     obTempPresetsEn=False,
		     extInsnsEn=False,
		     guiSettings=None,
		     coreLinkSettings=None):
		self.setProjectFile(projectFile)
		self.setCreateDate(createDate)
		self.setModifyDate(modifyDate)
		self.setAwlSources(awlSources)
		self.setSymTabSources(symTabSources)
		self.setLibSelections(libSelections)
		if not cpuSpecs:
			cpuSpecs = S7CPUSpecs()
		self.setCpuSpecs(cpuSpecs)
		self.setObTempPresetsEn(obTempPresetsEn)
		self.setExtInsnsEn(extInsnsEn)
		if not guiSettings:
			guiSettings = GuiSettings()
		self.setGuiSettings(guiSettings)
		if not coreLinkSettings:
			coreLinkSettings = CoreLinkSettings()
		self.setCoreLinkSettings(coreLinkSettings)

	def setProjectFile(self, filename):
		self.projectFile = filename

	def getProjectFile(self):
		return self.projectFile

	def setCreateDate(self, createDate):
		if createDate is None:
			createDate = datetime.datetime.utcnow()
		self.createDate = createDate

	def getCreateDate(self):
		return self.createDate

	def setModifyDate(self, modifyDate):
		if modifyDate is None:
			modifyDate = self.getCreateDate()
		if modifyDate is None:
			modifyDate = datetime.datetime.utcnow()
		self.modifyDate = modifyDate

	def getModifyDate(self):
		return self.modifyDate

	def setAwlSources(self, awlSources):
		self.awlSources = awlSources

	def getAwlSources(self):
		return self.awlSources

	def setSymTabSources(self, symTabSources):
		self.symTabSources = symTabSources

	def getSymTabSources(self):
		return self.symTabSources

	def setLibSelections(self, libSelections):
		self.libSelections = libSelections

	def getLibSelections(self):
		return self.libSelections

	def setCpuSpecs(self, cpuSpecs):
		self.cpuSpecs = cpuSpecs

	def getCpuSpecs(self):
		return self.cpuSpecs

	def setObTempPresetsEn(self, obTempPresetsEn):
		self.obTempPresetsEn = obTempPresetsEn

	def getObTempPresetsEn(self):
		return self.obTempPresetsEn

	def setExtInsnsEn(self, extInsnsEn):
		self.extInsnsEn = extInsnsEn

	def getExtInsnsEn(self):
		return self.extInsnsEn

	def setGuiSettings(self, guiSettings):
		self.guiSettings = guiSettings

	def getGuiSettings(self):
		return self.guiSettings

	def setCoreLinkSettings(self, coreLinkSettings):
		self.coreLinkSettings = coreLinkSettings

	def getCoreLinkSettings(self):
		return self.coreLinkSettings

	@classmethod
	def dataIsProject(cls, data):
		magic = b"[AWLSIM_PROJECT]"
		if isIronPython and isinstance(data, str):
			try:
				"a".startswith(b"a") # Test for ipy byte conversion bug
			except TypeError:
				# XXX: Workaround for IronPython byte conversion bug
				printInfo("Applying workaround for IronPython byte conversion bug")
				magic = magic.decode("UTF-8")
		return data.lstrip().startswith(magic)

	@classmethod
	def fileIsProject(cls, filename):
		return cls.dataIsProject(awlFileRead(filename, encoding="binary"))

	@classmethod
	def fromText(cls, text, projectFile):
		if not cls.dataIsProject(text.encode("utf-8")):
			raise AwlSimError("Project file: The data is "\
				"not an awlsim project.")
		projectDir = os.path.dirname(projectFile)
		createDate = None
		modifyDate = None
		awlSources = []
		symTabSources = []
		libSelections = []
		cpuSpecs = S7CPUSpecs()
		obTempPresetsEn = False
		extInsnsEn = False
		guiSettings = GuiSettings()
		linkSettings = CoreLinkSettings()
		try:
			p = _ConfigParser()
			p.readfp(StringIO(text), projectFile)

			# AWLSIM_PROJECT section
			version = p.getint("AWLSIM_PROJECT", "file_version")
			expectedVersion = 0
			if version != expectedVersion:
				raise AwlSimError("Project file: Unsupported version. "\
					"File version is %d, but expected %d." %\
					(version, expectedVersion))
			if p.has_option("AWLSIM_PROJECT", "date"):
				# Compatibility only. "date" is deprecated.
				dStr = p.get("AWLSIM_PROJECT", "date")
				createDate = datetime.datetime.strptime(dStr,
							cls.DATETIME_FMT)
				modifyDate = createDate
			if p.has_option("AWLSIM_PROJECT", "create_date"):
				dStr = p.get("AWLSIM_PROJECT", "create_date")
				createDate = datetime.datetime.strptime(dStr,
							cls.DATETIME_FMT)
			if p.has_option("AWLSIM_PROJECT", "modify_date"):
				dStr = p.get("AWLSIM_PROJECT", "modify_date")
				modifyDate = datetime.datetime.strptime(dStr,
							cls.DATETIME_FMT)

			# CPU section
			for i in range(0xFFFF):
				option = "awl_file_%d" % i
				if not p.has_option("CPU", option):
					break
				path = p.get("CPU", option)
				src = AwlSource.fromFile(path, cls.__generic2path(path, projectDir))
				awlSources.append(src)
			for i in range(0xFFFF):
				srcOption = "awl_%d" % i
				nameOption = "awl_name_%d" % i
				if not p.has_option("CPU", srcOption):
					break
				awlBase64 = p.get("CPU", srcOption)
				name = None
				if p.has_option("CPU", nameOption):
					try:
						name = base64.b64decode(p.get("CPU", nameOption))
						name = name.decode("utf-8", "ignore")
					except (TypeError, binascii.Error) as e:
						pass
				if name is None:
					name = "AWL/STL #%d" % i
				src = AwlSource.fromBase64(name, awlBase64)
				awlSources.append(src)
			if p.has_option("CPU", "mnemonics"):
				mnemonics = p.getint("CPU", "mnemonics")
				cpuSpecs.setConfiguredMnemonics(mnemonics)
			if p.has_option("CPU", "nr_accus"):
				nrAccus = p.getint("CPU", "nr_accus")
				cpuSpecs.setNrAccus(nrAccus)
			if p.has_option("CPU", "clock_memory_byte"):
				clockMemByte = p.getint("CPU", "clock_memory_byte")
				cpuSpecs.setClockMemByte(clockMemByte)
			if p.has_option("CPU", "ob_startinfo_enable"):
				obTempPresetsEn = p.getboolean("CPU", "ob_startinfo_enable")
			if p.has_option("CPU", "ext_insns_enable"):
				extInsnsEn = p.getboolean("CPU", "ext_insns_enable")

			# SYMBOLS section
			for i in range(0xFFFF):
				option = "sym_tab_file_%d" % i
				if not p.has_option("SYMBOLS", option):
					break
				path = p.get("SYMBOLS", option)
				src = SymTabSource.fromFile(path, cls.__generic2path(path, projectDir))
				symTabSources.append(src)
			for i in range(0xFFFF):
				srcOption = "sym_tab_%d" % i
				nameOption = "sym_tab_name_%d" % i
				if not p.has_option("SYMBOLS", srcOption):
					break
				symTabBase64 = p.get("SYMBOLS", srcOption)
				name = None
				if p.has_option("SYMBOLS", nameOption):
					try:
						name = base64.b64decode(p.get("SYMBOLS", nameOption))
						name = name.decode("utf-8", "ignore")
					except (TypeError, binascii.Error) as e:
						pass
				if name is None:
					name = "Symbol table #%d" % i
				src = SymTabSource.fromBase64(name, symTabBase64)
				symTabSources.append(src)

			# LIBS section
			for i in range(0xFFFF):
				nameOption = "lib_name_%d" % i
				blockOption = "lib_block_%d" % i
				effOption = "lib_block_effective_%d" % i
				if not p.has_option("LIBS", nameOption):
					break
				try:
					libName = base64.b64decode(p.get("LIBS", nameOption))
					libName = libName.decode("utf-8", "ignore")
				except (TypeError, binascii.Error) as e:
					continue
				block = p.get("LIBS", blockOption).upper().strip()
				effBlock = p.getint("LIBS", effOption)
				try:
					if block.startswith("FC"):
						entryType = AwlLibEntrySelection.TYPE_FC
						entryIndex = int(block[2:].strip())
					elif block.startswith("FB"):
						entryType = AwlLibEntrySelection.TYPE_FB
						entryIndex = int(block[2:].strip())
					elif block.startswith("UNKNOWN"):
						entryType = AwlLibEntrySelection.TYPE_UNKNOWN
						entryIndex = int(block[7:].strip())
					else:
						raise ValueError
					if entryIndex < -1 or entryIndex > 0xFFFF:
						raise ValueError
				except ValueError:
					raise AwlSimError("Project file: Invalid "
						"library block: %s" % block)
				libSelections.append(
					AwlLibEntrySelection(libName = libName,
							     entryType = entryType,
							     entryIndex = entryIndex,
							     effectiveEntryIndex = effBlock)
				)

			# CORE_LINK section
			if p.has_option("CORE_LINK", "spawn_local"):
				linkSettings.setSpawnLocalEn(
					p.getboolean("CORE_LINK", "spawn_local"))
			if p.has_option("CORE_LINK", "spawn_local_port_range"):
				pRange = p.get("CORE_LINK", "spawn_local_port_range")
				try:
					pRange = pRange.split(":")
					begin = int(pRange[0])
					end = int(pRange[1])
					if end < begin:
						raise ValueError
					pRange = range(begin, end + 1)
				except (IndexError, ValueError) as e:
					raise AwlSimError("Project file: Invalid port range")
				linkSettings.setSpawnLocalPortRange(pRange)
			if p.has_option("CORE_LINK", "spawn_local_interpreters"):
				interp = p.get("CORE_LINK", "spawn_local_interpreters")
				try:
					interp = base64.b64decode(interp)
					interp = interp.decode("utf-8")
				except (TypeError, binascii.Error, UnicodeError) as e:
					raise AwlSimError("Project file: "
						"Invalid interpreter list")
				linkSettings.setSpawnLocalInterpreters(interp)
			if p.has_option("CORE_LINK", "connect_host"):
				host = p.get("CORE_LINK", "connect_host")
				try:
					host = base64.b64decode(host)
					host = host.decode("utf-8")
				except (TypeError, binascii.Error, UnicodeError) as e:
					raise AwlSimError("Project file: "
						"Invalid host name")
				linkSettings.setConnectHost(host)
			if p.has_option("CORE_LINK", "connect_port"):
				port = p.getint("CORE_LINK", "connect_port")
				linkSettings.setConnectPort(port)
			if p.has_option("CORE_LINK", "connect_timeout_ms"):
				timeout = p.getint("CORE_LINK", "connect_timeout_ms")
				linkSettings.setConnectTimeoutMs(timeout)

			# GUI section
			if p.has_option("GUI", "editor_autoindent"):
				guiSettings.setEditorAutoIndentEn(
					p.getboolean("GUI", "editor_autoindent"))
			if p.has_option("GUI", "editor_paste_autoindent"):
				guiSettings.setEditorPasteIndentEn(
					p.getboolean("GUI", "editor_paste_autoindent"))
			if p.has_option("GUI", "editor_validation"):
				guiSettings.setEditorValidationEn(
					p.getboolean("GUI", "editor_validation"))
			if p.has_option("GUI", "editor_font"):
				guiSettings.setEditorFont(p.get("GUI", "editor_font").strip())

		except _ConfigParserError as e:
			raise AwlSimError("Project parser error: " + str(e))

		return cls(projectFile = projectFile,
			   createDate = createDate,
			   modifyDate = modifyDate,
			   awlSources = awlSources,
			   symTabSources = symTabSources,
			   libSelections = libSelections,
			   cpuSpecs = cpuSpecs,
			   obTempPresetsEn = obTempPresetsEn,
			   extInsnsEn = extInsnsEn,
			   guiSettings = guiSettings,
			   coreLinkSettings = linkSettings)

	@classmethod
	def fromFile(cls, filename):
		return cls.fromText(awlFileRead(filename, encoding="utf8"), filename)

	@classmethod
	def __path2generic(cls, path, relativeToDir):
		"""Generate an OS-independent string from a path."""
		if "\r" in path or "\n" in path:
			# The project file format cannot handle these
			raise AwlSimError("Project file: Path '%s' contains invalid "\
				"characters (line breaks)." % path)
		path = os.path.relpath(path, relativeToDir)
		if os.path.splitdrive(path)[0]:
			raise AwlSimError("Project file: Failed to strip drive letter. "\
				"Please make sure the project file, AWL code files and "\
				"symbol tables files all reside on the same drive.")
		path = path.replace(os.path.sep, "/")
		return path

	@classmethod
	def __generic2path(cls, path, relativeToDir):
		"""Generate a path from an OS-independent string."""
		path = path.replace("/", os.path.sep)
		path = os.path.join(relativeToDir, path)
		return path

	def toText(self, projectFile=None):
		if not projectFile:
			projectFile = self.projectFile
		projectDir = os.path.dirname(projectFile)

		self.setModifyDate(datetime.datetime.utcnow())

		lines = []
		lines.append("[AWLSIM_PROJECT]")
		lines.append("file_version=0")
		lines.append("create_date=%s" %\
			     self.getCreateDate().strftime(self.DATETIME_FMT))
		lines.append("modify_date=%s" %\
			     self.getModifyDate().strftime(self.DATETIME_FMT))
		lines.append("")

		lines.append("[CPU]")
		fileBackedSources = (src for src in self.awlSources if src.isFileBacked())
		embeddedSources = (src for src in self.awlSources if not src.isFileBacked())
		for i, awlSrc in enumerate(fileBackedSources):
			path = self.__path2generic(awlSrc.filepath, projectDir)
			lines.append("awl_file_%d=%s" % (i, path))
		for i, awlSrc in enumerate(embeddedSources):
			lines.append("awl_%d=%s" % (i, awlSrc.toBase64()))
			name = awlSrc.name.encode("utf-8", "ignore")
			name = base64.b64encode(name).decode("ascii")
			lines.append("awl_name_%d=%s" % (i, name))
		lines.append("mnemonics=%d" % self.cpuSpecs.getConfiguredMnemonics())
		lines.append("nr_accus=%d" % self.cpuSpecs.nrAccus)
		lines.append("clock_memory_byte=%d" % self.cpuSpecs.clockMemByte)
		lines.append("ob_startinfo_enable=%d" % int(bool(self.obTempPresetsEn)))
		lines.append("ext_insns_enable=%d" % int(bool(self.extInsnsEn)))
		lines.append("")

		lines.append("[SYMBOLS]")
		fileBackedSources = (src for src in self.symTabSources if src.isFileBacked())
		embeddedSources = (src for src in self.symTabSources if not src.isFileBacked())
		for i, symSrc in enumerate(fileBackedSources):
			path = self.__path2generic(symSrc.filepath, projectDir)
			lines.append("sym_tab_file_%d=%s" % (i, path))
		for i, symSrc in enumerate(embeddedSources):
			lines.append("sym_tab_%d=%s" % (i, symSrc.toBase64()))
			name = symSrc.name.encode("utf-8", "ignore")
			name = base64.b64encode(name).decode("ascii")
			lines.append("sym_tab_name_%d=%s" % (i, name))
		lines.append("")

		lines.append("[LIBS]")
		for i, libSel in enumerate(self.libSelections):
			libName = libSel.getLibName().encode("utf-8", "ignore")
			libName = base64.b64encode(libName).decode("ascii")
			lines.append("lib_name_%d=%s" % (i, libName))
			lines.append("lib_block_%d=%s %d" % (
				i, libSel.getEntryTypeStr(),
				libSel.getEntryIndex()))
			lines.append("lib_block_effective_%d=%d" % (
				i, libSel.getEffectiveEntryIndex()))
		lines.append("")

		lines.append("[CORE_LINK]")
		linkSettings = self.getCoreLinkSettings()
		lines.append("spawn_local=%d" %\
			     int(bool(linkSettings.getSpawnLocalEn())))
		lines.append("spawn_local_port_range=%d:%d" %(\
			     linkSettings.getSpawnLocalPortRange()[0],
			     linkSettings.getSpawnLocalPortRange()[-1]))
		interp = linkSettings.getSpawnLocalInterpreters()
		interp = interp.encode("utf-8", "ignore")
		interp = base64.b64encode(interp).decode("ascii")
		lines.append("spawn_local_interpreters=%s" % interp)
		host = linkSettings.getConnectHost()
		host = host.encode("utf-8", "ignore")
		host = base64.b64encode(host).decode("ascii")
		lines.append("connect_host=%s" % host)
		lines.append("connect_port=%d" %\
			     int(linkSettings.getConnectPort()))
		lines.append("connect_timeout_ms=%d" %\
			     int(linkSettings.getConnectTimeoutMs()))
		lines.append("")

		lines.append("[GUI]")
		guiSettings = self.getGuiSettings()
		lines.append("editor_autoindent=%d" %\
			     int(bool(guiSettings.getEditorAutoIndentEn())))
		lines.append("editor_paste_autoindent=%d" %\
			     int(bool(guiSettings.getEditorPasteIndentEn())))
		lines.append("editor_validation=%d" %\
			     int(bool(guiSettings.getEditorValidationEn())))
		lines.append("editor_font=%s" % guiSettings.getEditorFont())
		lines.append("")

		return "\r\n".join(lines)

	def toFile(self, projectFile=None):
		if not projectFile:
			projectFile = self.projectFile
		if not projectFile:
			raise AwlSimError("Project file: Cannot generate project file. "
				"No file name specified.")
		text = self.toText(projectFile)
		awlFileWrite(projectFile, text, encoding="utf8")
		for awlSrc in self.awlSources:
			awlSrc.writeFileBacking()
		for symSrc in self.symTabSources:
			symSrc.writeFileBacking()
