class BaseMigration extends RootMenuApp

	@click: ->
		ez5.rootMenu.closeMenu()
		baseMigration = new BaseMigration()
		baseMigration.showDownloadUploadModal()

	@label: ->
		"base.migration.button"

	@is_allowed: ->
		ez5.session.hasSystemRight("root", "plugin.basemigration.migration")

	@group: ->
		"za_other"

	@submenu: ->
		"plugins"

	log: (txt) ->
		console.debug.apply(console, arguments)
		args = []
		for arg in arguments
			args.push(arg)
		@__console.log(args.join(" "))

	showDownloadUploadModal: ->

		@__console = new CUI.Console()
		fileReader = new CUI.FileReader
			onAdd: =>
				uploadBtn.startSpinner()
			onFail: =>
				uploadBtn.stopSpinner()
				CUI.alert(text: $$("base.migration.error.file_upload_failed"))
			onDone: (frf) =>
				uploadBtn.stopSpinner()
				# console.debug "uploaded_data", frf.getDataAsUtf8String()
				try
					uploaded_data = JSON.parse(frf.getResult())
					if uploaded_data.dump_version != BaseMigration.dumpVersion
						CUI.alert(text: $$("base.migration.dump_version_not_matching",
							version_found: uploaded_data.dump_version or "unknown"
							version_expected: BaseMigration.dumpVersion
						))
						return
				catch e
					CUI.alert(text: $$("base.migration.unable_to_parse_file"))
					return

				# console.debug "file done utf8", frf, uploaded_data
				@log($$("base.migration.console.log.filename.md", {
						filename: frf.getFile().name
						filesize: ez5.format_filesize(frf.getFile().size)
					}
				))
				diag.destroy()
				@presentUploadedData(uploaded_data)
				return

		downloadBtn = new LocaButton
			loca_key: "base.migration.choice.download"
			disabled: true
			onClick: (ev, btn) =>
				@export()
				diag.destroy()

		if ez5.session.isReadOnly()
			uploadBtn = new CUI.Button
				text: $$("base.migration.choice.upload|text")
				icon: $$("base.migration.choice.upload|icon")
				disabled: true
				onClick: =>
					console.debug("Upload button clicked in read-only mode")
					CUI.problem(text: $$("base.error.user.read_only_mode"))
		else
			uploadBtn = new CUI.FileUploadButton
				fileUpload: fileReader
				text: $$("base.migration.choice.upload|text")
				disabled: true
				multiple: false
				icon: $$("base.migration.choice.upload|icon")

		diag = new CUI.ConfirmationDialog
			cancel: true
			title: $$("base.migration.choice.title")
			size: "xs"
			class: "ez5-base-migration-download-upload-modal"
			content: new LocaLabel(loca_key: "base.migration.choice.loading")
			buttons: [	downloadBtn, uploadBtn ]
		.show()

		@loadBaseData()
		.done =>
			@log($$("base.migration.console.log.base-data-loaded"))
			if diag.isDestroyed()
				return
			# console.debug("base data loaded", @, @getUniqueId(), @getBaseData())
			downloadBtn.enable()
			uploadBtn.enable()
			diag.setText($$("base.migration.choice.info"))
			diag.autoSize()

	getBaseData: ->
		@__baseData

	# finds "who" in data and migrates it
	migrateWhoInData: (data, where = "", level = 0) ->
		# console.debug "check migrate who", data, where

		if data?._basetype in ["group", "user"]
			try
				return @migrateWho(data)
			catch e
				if e instanceof MigrateSkipException
					@log($$("base.migration.console.log.where-skipping-who",
						where: where,
						error: e.getError())
					)
				throw(e)
			return null

		if CUI.isArray(data)
			idx = 0
			while data.length > idx
				item = data[idx]
				try
					ret = @migrateWhoInData(item, where, level+1)
					if ret != null
						data[idx] = ret
				catch e
					if e instanceof MigrateSkipException
						data.splice(idx, 1)
						continue
					throw(e)
				idx++
			return null

		if CUI.isPlainObject(data)
			for key, value of data
				try
					ret = @migrateWhoInData(value, where, level+1)
					if ret != null
						data[key] = ret
				catch e
					if e instanceof MigrateSkipException
						delete(data[key])
						continue
					throw(e)
			return null


		return null

	migrateTransition: (transition) ->
		copied_transition = who: []

		for k in [
			"actions"
			"comment"
			"confirm"
			"objecttype_ids"
			"operations"
			"sticky"
			"type"
		]
			copied_transition[k] = CUI.util.copyObject(transition[k], true)

		for k in [
			"tagfilter:before"
			"tagfilter:after"
		]
			if not transition[k]
				continue

			try
				new_tagfilter = @migrateTagFilter(transition[k])
			catch e
				if e instanceof MigrateSkipException
					@log($$("base.migration.console.log.skipping-tag-filter"), error: e.getError())
					new_tagfilter = null
				else
					throw(e)

			if new_tagfilter
				copied_transition[k] = new_tagfilter


		for who in transition.who
			try
				new_who = @migrateWho(who)
			catch e
				if e instanceof MigrateSkipException
					@log($$("base.migration.console.log.skipping-who", error: e.getError()))
					new_who = null
				else
					throw(e)

			if new_who
				copied_transition.who.push(new_who)

		if ez5.version("6")
			copied_transition.who_not = transition.who_not or false


		if copied_transition.actions
			@migrateWhoInData(copied_transition.actions, "Transition: Actions")

		if copied_transition.who.length == 0
			@log($$("base.migration.console.log.transition.no-migratable-found.md"))
			console.warn($$("base.migration.console.log.transition.no-migratable-found.md"), transition)
			return undefined

		# console.debug "migrateTransition:", transition, copied_transition
		return copied_transition


	migrateACL: (acl) ->
		copied_rights = []
		for acl_row in acl
			try
				copied_rights.push(@migrateACLRow(acl_row))
			catch e
				if e instanceof MigrateSkipException
					@log($$("base.migration.console.log.skipping-right", error: e.getError()))
				else
					throw(e)

		# console.debug "migrateACL", acl, ">", copied_rights
		copied_rights

	migrateACLRow: (right) ->
		new_right =
			active: right.active
			rights: @migrateRights(right.rights)
			who: @migrateWho(right.who)

		if right.tagfilter
			new_right.tagfilter = @migrateTagFilter(right.tagfilter)

		# console.debug "migrateRight", right, ">", new_right
		new_right

	migrateTagFilter: (tagfilter) ->
		new_tagfilter = {}
		for k in ["all", "any", "not", "changed"]
			if not tagfilter[k]
				continue

			if not new_tagfilter[k]
				new_tagfilter[k] = []

			for tag_id in tagfilter[k]
				new_tagfilter[k].push(@migrateTagID(tag_id))

		if CUI.isEmptyObject(new_tagfilter)
			undefined
		else
			new_tagfilter
		#

	migrateTagID: (tag_id) ->
		source_tag = @__sourceData._tag_by_id[tag_id]

		for tag in @__baseData._tags
			if tag._tag_identifier == source_tag._tag_identifier
				# console.debug "Tag found in target:", source_tag._tag_identifier
				return tag.tag._id
		throw(new MigrateSkipException(text: $$("base.migration.exception.tag-not-found.text.md", target: source_tag._tag_identifier)))

	migrateRights: (rights) ->
		CUI.util.copyObject(rights, true)

	migrateWho: (who) ->
		#In fylr the acl right who information doesn't contain all the group info so we have to inject it from source data.
		if ez5.version("6")
			if who.group
				group = @__sourceData.group.find((sourceGroup) => sourceGroup.group._id == who.group._id)
				if group
					who.group = group.group

		_who = AclWho.newInstance(who)

		baseType = who._basetype
		dn = _who.getDisplayText()

		if baseType == "group"
			groups = @findGroupsByGroup(who)
			if groups.length == 0
				throw(new MigrateSkipException(text: $$("base.migration.exception.group-not-found.text.md", group: dn)))
			else if groups.length > 1
				console.warn("Who:", who, groups)
				throw(new MigrateSkipException(text: $$("base.migration.exception.group-repeat.text.md", group: dn)))

			new_who =
				_basetype: "group"
				group:
					_id: groups[0].group._id
					name: groups[0].group.name
					displayname: groups[0].group.displayname
		else if baseType == "user" and ez5.version("6")
			# In fylr we can find users by reference, if an user has no reference, we cannot migrate it
			# SourceData and BaseDAta will have a list of users with reference and id
			sourceUserReference = @getSourceData()["user"]?.find((source_user) => source_user.user._id == who.user._id)?.user?.reference
			# We have the refernece of the user. we can try to find the same reference in the base data of this instance
			if not sourceUserReference
				throw(new MigrateSkipException(text: $$("base.migration.exception.user-not-found.text.md", user: dn)))
			baseUser = @getBaseData()["user"]?.find((base_user) => base_user.user.reference == sourceUserReference)
			if not baseUser
				throw(new MigrateSkipException(text: $$("base.migration.exception.user-not-found.text.md", user: dn)))
			new_who =
				_basetype: "user"
				user:
					_id: baseUser.user._id
					reference: baseUser.user.reference
		else
			errorText = $$("base.migration.exception.basetype-not-group.text.md",
				baseType: baseType
				displayText: CUI.MarkdownInput.escape(dn)
			)
			throw(new MigrateSkipException(text: errorText))

		# console.debug "migrateWho", who, ">", new_who
		new_who


	findGroupsByGroup: (group_data) ->
		found_groups = []

		for group in @__baseData.group
			if group.group.type != group_data.group.type
				continue

			switch group.group.type
				when "system"
					if (not ez5.version("6") and group.group.name == group_data.group.name) or
						(ez5.version("6") and group.group.reference == group_data.group.reference)
							found_groups.push(group)

				when "easydb"
					if ez5.loca.getBestFrontendValue(group.group.displayname) ==
						ez5.loca.getBestFrontendValue(group_data.group.displayname)
							found_groups.push(group)

		found_groups

	enhanceBaseData: (baseData) ->
		baseData._tags = []
		baseData._utf8_test = "äöüÄÖÜß"
		baseData._tag_by_id = {}

		for taggroup in baseData.tags
			tag_group_name = ez5.loca.getBestFrontendValue(taggroup.taggroup.displayname)
			for tag in taggroup._tags
				tag._tag_identifier = tag_group_name+"."+ez5.loca.getBestFrontendValue(tag.tag.displayname)
				baseData._tags.push(tag)
				baseData._tag_by_id[tag.tag._id] = tag

		# To get the presets it is necessary to use the api /right
		if baseData.right and not baseData.preset
			rights = {}
			contexts = baseData.right
			for context, info of contexts
				if not info.capabilities.preset or not info.presets
					continue
				rights[context] = []
				for preset in info.presets
					rights[context].push(preset)
			baseData.preset = rights
			delete baseData.right

		if baseData["user"]?
			# We only want the reference and id of users, we dont want the full user data (DATA PROTECTION!)
			userList = []
			for user in baseData.user
				# If the user does not have a reference, we skip it, we cant migrate it as we cannot find it in the target system
				if user.user.reference?
					userList.push {
						basetype: "user"
						user:
							_id: user.user._id
							reference: user.user.reference
					}
			baseData.user = userList
			console.log "BaseMigration: Enhanced user data, only reference and id are kept for migration purposes.", baseData.user
		@

	loadBaseData: ->
		base_data = dump_version: BaseMigration.dumpVersion
		promises = []
		types = [
			"tags"
			"transitions"
			"pool"
			"config"
			"objecttype"
			"group"
			"right"
			"user"
		]
		for type in types
			do (type) =>
				promises.push(ez5.api[type]().done (data) =>
					base_data[type] = data
				)

		dfr = new CUI.Deferred()
		CUI.when(promises)
		.done =>
			@enhanceBaseData(base_data)
			# console.debug "base data loaded:", base_data
			@__baseData = base_data
			dfr.resolve()
		.fail =>
			CUI.alert(text: $$("base.migration.error.load_base_data_failed"))
		dfr.promise()

	export: ->
		name = []
		inst = ez5.session.getInstance()
		for k in ["name"]
			name.push(inst[k])
		baseData = @getBaseData()
		CUI.downloadData(JSON.stringify(baseData), ez5.session.getEasydbName() + "_Permissions_" + CUI.util.moment().format('YYYY-MM-DD') + ".json")

	getSourceData: ->
		@__sourceData

	getConsole: ->
		new CUI.SimplePane
			header_left: new CUI.Label(text: $$("base.migration.console.text"))
			header_right:
				icon: $$("base.migration.console.icon")
				onClick: =>
					@__console.clear()
			content: @__console

	presentUploadedData: (@__sourceData) ->
		@enhanceBaseData(@__sourceData)

		source_nodes = []

		migrate_keys = [
			"config"
			"group"
			"tags"
			"transitions"
			"objecttype"
			"pool"
			"dump_version"
			"_utf8_test"
			"preset"
		]

		for k, v of @__sourceData
			if not k.startsWith("_") and k not in migrate_keys
				@log($$("base.migration.console.log.skipping-unsupported-data"), k, v.length, v)

		for migrate_key in migrate_keys
			for k, v of @__sourceData
				if k != migrate_key
					continue

				switch k
					when "pool"
						source_nodes.push.apply(source_nodes, BaseMigrationNodePool.getSourceNodesFromData(@, v))
					when "objecttype"
						source_nodes.push.apply(source_nodes, BaseMigrationNodeObjecttype.getSourceNodesFromData(@, v))
					when "tags"
						source_nodes.push.apply(source_nodes, BaseMigrationNodeTagGroup.getSourceNodesFromData(@, v))
					when "group"
						source_nodes.push.apply(source_nodes, BaseMigrationNodeGroup.getSourceNodesFromData(@, v))
					when "transitions"
						source_nodes.push.apply(source_nodes, BaseMigrationNodeTransition.getSourceNodesFromData(@, v))
					when "preset"
						source_nodes.push.apply(source_nodes, BaseMigrationNodePreset.getSourceNodesFromData(@, v))
					when "dump_version"
						console.info("Dump Version:", v)
					when "_utf8_test"
						console.info("Utf8-Test:", v)

		source_tree = new CUI.ListViewTree
			cols: ["maximize"]
			selectableRows: true
			header_left: new CUI.Label(text: $$("base.migration.source-tree.header.text"))
			onSelect: (ev, info) =>
				actionPane = info.node.getActionPane()
				@__layout.replace(actionPane, "center")
				# console.debug "Selecting:", info.node.getDisplayText()
			onDeselect: (ev, info) =>
				@__layout.replace(@getEmptyPane(), "center")
			children: source_nodes


		@__layout = new CUI.HorizontalLayout
			left:
				class: "ez5-base-migration-tree"
				content: source_tree.render()
				flexHandle:
					hidden: false
					state_name: "base-migration-tree"
			center:
				class: "ez5-base-migration-content"
				content: @getEmptyPane()
			right:
				class: "ez5-base-migration-console"
				flexHandle:
					hidden: false
					state_name: "base-migration-console"
				content: @getConsole()

		mod = new CUI.Modal
			class: "ez5-base-migration-modal"
			cancel: true
			fill_space: "both"
			onHide: =>
				delete(@__baseData)
			pane:
				header_left: new CUI.Label(text: $$("base.migration.choice.title"))
				content: @__layout
				footer_right: new LocaButton
					loca_key: "base.migration.migration.button.done"
					secondary: true
					onClick: =>
						mod.destroy()
		.show()

		source_tree.root.open()

	getEmptyPane: ->
		new CUI.EmptyLabel(centered: true, multiline: true, markdown: true, text: $$("base.migration.empty-pane.text.md"))

	@dumpVersion: 1


class MigrateSkipException extends UserError


ez5.session_ready ->
	ez5.rootMenu.registerApp(BaseMigration)

