var getItemLocalStorage = global.getItemLocalStorage;
var setItemLocalStorage = global.setItemLocalStorage;
var p5WFS_GetFeature = global.p5WFS_GetFeature;
var DBG = DBG || 0;

function graphShowHide(nameSection) {
	var node = $('.smad-'+nameSection+'-graph-view')
	if (!node || !node.length) return;
	if ('block' !== node.css('display')) {
		node.show()
		graphFetchData(node.get(0), nameSection)
	} else {
		node.hide()
	}
}

function graphFetchData(node, nameSection) {
	var page = page || getItemLocalStorage('Bocian.biAuditForm.'+nameSection+'.pagination.page');
	if ( page === 1) {
		setItemLocalStorage('Bocian.biAuditForm.'+nameSection+'.pagination.page', 1);
	}

	var filterIdGroup = getItemLocalStorage('Bocian.biAuditForm.'+nameSection+'.filterIdGroup');

	var frm = document.getElementById('filtersFieldRemoveBtn-' + nameSection.toUpperCase()).form
	var fieldNameList = ('pracownicy' === nameSection) ? FIELD_LIST_PRACOWNICY : FIELD_LIST_KONTRAHENCI
	var filterFields = fieldNameList.filter(function (fieldName) {
		if (!frm[fieldName] && DBG) console.log('Err missing field: "'+fieldName+'"')
		return (frm[fieldName]) ? true : false
	}).map(function (fieldName) {
		return [ fieldName, frm[fieldName].value ]
	}).filter(function (filter) {
		return ( filter[1].length > 0 )
	})
	// filterFields = (filterFields.length > 0) ? '&' + filterFields : ''

	// <ogc:Filter>
	//     <ogc:Or>
	//       <ogc:PropertyIsLike wildCard="*" singleChar="%23" escapeChar="!">
	//         <ogc:PropertyName>A_STATUS</ogc:PropertyName>
	//         <ogc:Literal>*O%23MA*</ogc:Literal>
	//       </ogc:PropertyIsLike>
	//       <ogc:PropertyIsLike wildCard="*" singleChar="%23" escapeChar="!">
	//         <ogc:PropertyName>A_STATUS</ogc:PropertyName>
	//         <ogc:Literal>*ARNING</ogc:Literal>
	//       </ogc:PropertyIsLike>
	//     </ogc:Or>
	//   </ogc:Filter>
	var ogcFilterFields = (filterFields.length > 0)
	? '<ogc:Filter><ogc:And>' + filterFields.map(function(filter) {
		if ('ID' === filter[0].substr(2)) return '<ogc:PropertyIsEqualTo>' +
			'<ogc:PropertyName>' + filter[0].substr(2) + '</ogc:PropertyName>' +
			'<ogc:Literal>' + filter[1] + '</ogc:Literal>' +
		'</ogc:PropertyIsEqualTo>';

		return '<ogc:PropertyIsLike wildCard="*" singleChar="%23" escapeChar="!">' +
			'<ogc:PropertyName>' + filter[0].substr(2) + '</ogc:PropertyName>' +
			'<ogc:Literal>*' + filter[1] + '*</ogc:Literal>' +
		'</ogc:PropertyIsLike>';
	}) + '</ogc:And></ogc:Filter>'
	: ''

	var paginationLimit = 20;

	if(DBG)console.log('graphFetchData...', {
		paginationLimit: paginationLimit,
		page: page,
		filterIdGroup: filterIdGroup,
		filterFields: filterFields,
		ogcFilterFields: ogcFilterFields,
	});

	function refFieldsOnPathToList(typeName, fields) {
		var fields = fields || []
		var flatList = []
		var refPathBase = [
			'default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object',
			'default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row',
			'default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object'
		]
		flatList.push(refPathBase.concat(typeName).join('/'))
		fields.forEach(function (fieldName) {
			flatList.push(refPathBase.concat(typeName, fieldName).join('/'))
		})
		return flatList;
	}

	p5WFS_GetFeature('default_db__x3A__BI_audit_ENERGA_PRACOWNICY:BI_audit_ENERGA_PRACOWNICY',
		Object.assign({
			sortBy: 'ID+D',
			maxFeatures: paginationLimit,
			startIndex: (page - 1) * paginationLimit,
			// TODO: backRefNS, backRefPK, backRefField - TODO: from groups
			// resolve: 'all',
			// resolveDepth: 2
			'ogc:Filter': '<wfs:Query>' + '\n' + [
				'ID',
				'imiona',
				'nazwisko',
				'miejscowosc'
			].concat(refFieldsOnPathToList('default_db__x3A__BI_audit_KRS:BI_audit_KRS', [ 'ID', 'nazwa', 'krs', 'S_miejscowosc' ]))
			.concat(refFieldsOnPathToList('default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI:BI_audit_ENERGA_RUM_KONTRAHENCI', [ 'ID', 'Pelna_nazwa_kontrahenta' ]))
			.concat(refFieldsOnPathToList('default_db__x3A__BI_audit_MSIG:BI_audit_MSIG', [ 'ID', 'nazwa' ]))
			.concat(refFieldsOnPathToList('default_db__x3A__BI_audit_CEIDG:BI_audit_CEIDG', [ 'ID', 'nazwisko', 'firma' ]))
			.concat([
				// '*',
				// 'default_db__x3A__BI_audit_ENERGA_PRACOWNICY_adresy:BI_audit_ENERGA_PRACOWNICY_adresy/*',
				[
					'default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object',
					'ID'
				].join('/'),
				[
					'default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object',
					'default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row',
					'ID'
				].join('/'),
				[
					'default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object',
					'default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row',
					'default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object',
					'ID'
				].join('/')
			]).map(function (fieldName) {
				return '<wfs:PropertyName>' + fieldName + '</wfs:PropertyName>';
			}).join('\n') + '\n' +
			( ogcFilterFields ? ogcFilterFields : '' ) +
			'</wfs:Query>'
		}, (filterIdGroup > 0)
			? {
					backRefNS: 'default_db/BI_audit_ENERGA_PRACOWNICY_group/BI_audit_ENERGA_PRACOWNICY_group',
					backRefPK: filterIdGroup,
					backRefField: 'default_db__x3A__BI_audit_ENERGA_PRACOWNICY:BI_audit_ENERGA_PRACOWNICY',
				}
			: {}
		)
	).then(function (items) {
		if(DBG)console.log('p5WFS_GetFeature: items: ', items);
		graphRender(node, {
			msg: "Pobrano dane",
			typeName: ('pracownicy' === nameSection) ? 'default_db__x3A__BI_audit_ENERGA_PRACOWNICY:BI_audit_ENERGA_PRACOWNICY' : 'default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI:BI_audit_ENERGA_RUM_KONTRAHENCI',
			items: items
		})
	}).catch(function (err) {
		if(DBG)console.log('p5WFS_GetFeature: err: ', err);
		// graphRender(node, { msg: "Wystąpiły błędy podczas pobierania danych", nameSection: nameSection, err: err })
	})
}

function graphRender(wrapNode, props) {
	var svgNode = jQuery(wrapNode).children('svg')
	svgNode = (svgNode.length) ? d3.select(svgNode.get(0)) : d3.select(wrapNode).append("svg")
	// svg.append("rect").attr("x",0).attr("y",0).attr("width","100%").attr("height","100%").attr("fill","white").attr('class','background').on('mouseup', () => redraw())
	// svgNode.attr("width", '100%')
	// 	.attr("height", '300px')
		// .append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");

	// var data = parseGraphRec(props.items, props.nameSection)
	var _todoGraphData = [];
	parseResponseRec(_todoGraphData, props.items, props.typeName)
	if(DBG)console.log('_todoGraphData', _todoGraphData)
	var _nodes = [];
	var _links = [];
	var mapNodeIdToIdx = {};
	_todoGraphData.forEach(function (levelData, levelIdx) {
		if (levelData.nodes && levelData.nodes.length) {
			levelData.nodes.forEach(function (node) {
				try {
					if (node.id in mapNodeIdToIdx) return;
					_nodes.push(node) // _nodes.add(node)
					mapNodeIdToIdx[node.id] = _nodes.length - 1
				} catch (e) {
					if(DBG)console.log('_graphData.nodes.add [level='+levelIdx+'] error:', e);
				}
			})
		}
	})

	var linkIdsAdded = []
	_todoGraphData.forEach(function (levelData, levelIdx) {
		if (levelData.edges && levelData.edges.length) {
			levelData.edges.forEach(function (edge) {
				if(DBG)console.log('_graphData.edges.add [level='+levelIdx+']:', {edge, source:mapNodeIdToIdx[edge.source], target:mapNodeIdToIdx[edge.target], mapNodeIdToIdx});
				// try {
					// var source = mapNodeIdToIdx[edge.source]
					// var target = mapNodeIdToIdx[edge.target]
					// if (!source || !target) { // && !== 0
					// 	console.warn('(!source || !target)', {source, target, edge, mapNodeIdToIdx})
					// }
					if (-1 !== linkIdsAdded.indexOf(edge.id)) return
					// _links.push({
					// 	id: edge.id,
					// 	source: mapNodeIdToIdx[edge.source],
					// 	target: mapNodeIdToIdx[edge.target],
					// 	value: 1
					// })
					_links.push({
						id: edge.id,
						source: edge.source,
						target: edge.target,
						value: 1
					}) // _edges.add(edge)
					linkIdsAdded.push(edge.id)
				// } catch (e) {
				// 	if(DBG)console.log('_graphData.edges.add [level='+levelIdx+'] error:', e);
				// }
			})
		}
	})

	var totalLinks = _links.length
	// console.log('DBG:0: _links', _links)
	// console.log('DBG:1: _nodes', _nodes)
	_nodes = _nodes.filter(function (node, idx) {
		if (node.typeName !== props.typeName) return true
		for (i=0; i<totalLinks; i++) {
			var link = _links[i]
			if (_links[i].source === node.id) return true
			if (_links[i].target === node.id) return true
		}
		return false
	})
	// console.log('DBG:2: _nodes', _nodes)
	jQuery(wrapNode).find('p').remove()
	if (!_nodes || !_nodes.length) {
		jQuery(wrapNode).find('svg').remove()
		jQuery(wrapNode).append('<p>Brak powiązań</p>')
		return;
	}

	var graphData = { nodes: _nodes, links: _links };

	var graf = renderGraph(svgNode, graphData, {
		width: jQuery(wrapNode).width()
	})
	graf.on('click', (event) => {
	  if(DBG)console.log('event', event)
	  // event = {
	  //   nativeEvent: d3.event,
	  //   node: a,
	  //   element: d3.event.srcElement
	  // }

	  // TODO: !a.expanded && redraw({type: a.name, criteria: 'ID'})
	})
}

function parseResponseRec(_todoGraphData, json, typeName, parentNodeId, level) {
	var level = level || 0
	var parentNodeId = parentNodeId || null
	if(DBG)console.log('DBG::parseResponseRec', {json:json, typeName:typeName, parentNodeId:parentNodeId, isString: p5Utils__isString(json), isArray: p5Utils__isArray(json), isObject: p5Utils__isObject(json)});
	if (p5Utils__isArray(json)) {
		// TODO: create named group
		var isXlinkList = (json.length > 0 && p5Utils__isString(json[0]))
		json.forEach(function (subJson) {
			if (isXlinkList) {
				parseResponseXlinkListRec(_todoGraphData, subJson, typeName, parentNodeId, level)
			} else {
				parseResponseRec(_todoGraphData, subJson, typeName, parentNodeId, level)
			}
		})
	} else if (p5Utils__isObject(json) && isP5LinkObject(json)) {
		if(DBG)console.log('DBG::parseResponseRec isP5LinkObject');
		parseResponseP5Link(_todoGraphData, json, typeName, parentNodeId, level)
	} else if (p5Utils__isObject(json)) {
		// _todoGraphData.nodes.add({ id: nodeId, label: nodeId })
		if (!_todoGraphData[level]) _todoGraphData[level] = { nodes: [], edges: [] }
		var nodeObject = dataMakeNode({
			typeName: typeName,
			primaryKey: (json['ID']) ? json['ID'] : null, // TODO: get primaryKey from object
			row: json,
		})
		var nodeId = nodeObject.id

		if ("default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row" === typeName) {
			parseResponseRec__row(_todoGraphData, json, typeName, parentNodeId, level)
		}
		else if ("default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object" === typeName) {
			// parseResponseRec__row_object(_todoGraphData, json, typeName, parentNodeId, level)
			Object.keys(json).filter(function (fieldName) {
				return (fieldName.indexOf(':') > -1)
			})
			.forEach(function (fieldName) {
				var value = json[fieldName]
				parseResponseRec(_todoGraphData, value, fieldName, parentNodeId, level + 1)
			})
		}
		else {
			_todoGraphData[level].nodes.push(nodeObject)
			if (parentNodeId) {
				_todoGraphData[level].edges.push(dataMakeEdge(parentNodeId, nodeObject))
			}
			Object.keys(json).filter(function (fieldName) {
				return (fieldName.indexOf(':') > -1)
			})
			.forEach(function (fieldName) {
				var value = json[fieldName]
				parseResponseRec(_todoGraphData, value, fieldName, nodeId, level + 1)
			})
		}
	} else if (p5Utils__isString(json)) {
		if(DBG)console.log('TODO: Not implemented - parseResponseRec isString');
	} else {
		if(DBG)console.log('TODO: Not implemented - parseResponseRec is not string, not array and not object');
	}
}
function parseResponseXlinkListRec(_todoGraphData, json, typeName, parentNodeId, level) {
	if(DBG)console.log('DBG::parseResponseRec:XlinkList', {json:json, typeName:typeName, parentNodeId:parentNodeId, isString: p5Utils__isString(json), isArray: p5Utils__isArray(json), isObject: p5Utils__isObject(json)});
	if (p5Utils__isString(json)) { // xlink "https://biuro.biall-net.pl/wfs/default_db/BI_audit_ENERGA_RUM_KONTRAHENCI#BI_audit_ENERGA_RUM_KONTRAHENCI.9233",
		var nodeId = json.substr(json.indexOf('#') + 1)
		{
			// _graphData.nodes.add({ id: nodeId, label: nodeId })
			if (!_todoGraphData[level]) _todoGraphData[level] = { nodes: [], edges: [] }
			var nodeObject = dataMakeXlinkNode({
				xlink: json,
				typeName: typeName,
			})
			_todoGraphData[level].nodes.push(nodeObject)
			if (parentNodeId) {
				_todoGraphData[level].edges.push(dataMakeEdge(parentNodeId, nodeObject))
			}
		}
	} else if (p5Utils__isObject(json) && isP5LinkObject(json)) {
		parseResponseP5Link(_todoGraphData, json, typeName, parentNodeId, level)
	} else {
		if(DBG)console.log('TODO: Not implemented - parseResponseRec:XlinkList is not string and not object');
	}
}
function parseResponseP5Link(_todoGraphData, json, typeName, parentNodeId, level) {
	if(DBG)console.log('parseResponseRec isObject and P5Link - fetch more xlink object');
	// example json: { type: "next",
	//  @backRefNS: "default_db/BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA/BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA",
	//  @backRefPK: "42",
	//  @typeName: "default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_P…ow:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row",
	//  @startIndex: "10" }
	if (!parentNodeId) throw "Missing parentNodeId for ref link object";
	var objectName = typeName.substr(typeName.indexOf(':') + 1)
	{
		// _graphData.nodes.add({ id: nodeId, label: nodeId })
		if (!_todoGraphData[level]) _todoGraphData[level] = { nodes: [], edges: [] }
		switch (json.type) {
			case 'next': {
        // TODO: add fetch next node
				// var nodeObject = dataMakeFetchMoreNode({
				// 	parentNodeId: parentNodeId,
				// 	type: json.type,
				// 	objectName: objectName,
				// 	ref: json,
				// })
				// _todoGraphData[level].nodes.push(nodeObject)
				// _todoGraphData[level].edges.push(dataMakeFetchMoreEdge(parentNodeId, nodeObject))
			} break;
			default: {
				if(DBG)console.log('TODO: Not implemented - parseResponseRec isObject with type "'+json.type+'" - fetch more xlink object');
			}
		}
	}
}

function parseResponseRec__row(_todoGraphData, json, typeName, parentNodeId, level) {
	var nodeObject = dataMakeNode({
		typeName: typeName,
		primaryKey: (json['ID']) ? json['ID'] : null, // TODO: get primaryKey from object
	})
	var nodeId = nodeObject.id

	// 'default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row',
	// 'default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object',
	// 'default_db__x3A__BI_audit_KRS:BI_audit_KRS' --> Kontrahenci
	var rowObjectChildrens = []
	var rowTN = 'default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row'
	var rowObjTN = 'default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object'
	// { row: row_obj: [ { ID, kontr: [ "http...kontr.ID" ] }, { ID, kontr: [ "http...krs.ID" ] } ], ... }
	var rowJson = json
	if (rowJson[rowObjTN] && rowJson[rowObjTN].length > 1) {
		var pathItems = []
		rowJson[rowObjTN].forEach(function (rowObjJson) {
			Object.keys(rowObjJson)
				.filter(function (rowObjFld) { return (-1 !== rowObjFld.indexOf(':')); })
				.filter(function (rowObjRefFld) { return (rowObjJson[rowObjRefFld] && rowObjJson[rowObjRefFld].length); })
				.forEach(function (rowObjRefFld) {
					pathItems.push([ rowObjRefFld, rowObjJson[rowObjRefFld][0] ])
				})
		})
	}
	// console.log('node('+nodeId+') pathItems:', pathItems);
	var lastParentFid = null
	pathItems.reverse().forEach(function (pathItem) {
		var nodeObject = ('string' === typeof pathItem[1])
		? dataMakeXlinkNode({
				xlink: pathItem[1],
				typeName: pathItem[0]
			})
		:	 dataMakeNode({
				typeName: pathItem[0],
				primaryKey: (pathItem[1]['ID']) ? pathItem[1]['ID'] : null,
				row: pathItem[1]
			})
		;
		// _todoGraphData[todoLevel].nodes.push(nodeObject) // already added below
		if (lastParentFid) _todoGraphData[level].edges.push(dataMakeEdge(lastParentFid, nodeObject))
		lastParentFid = nodeObject.id
	})

	Object.keys(json).filter(function (fieldName) {
		return (fieldName.indexOf(':') > -1)
	})
	.forEach(function (fieldName) {
		var value = json[fieldName]
		parseResponseRec(_todoGraphData, value, fieldName, parentNodeId, level + 1)
	})
}

function parseGraphRec(items, featureType, parentFeatureId) { // TODO: not used
	var parentFeatureId = parentFeatureId || null
	var nodesArray = []
	var linksArray = []
	if (!items) return;
	items.forEach(function (item) {
		if ('string' === typeof item) {
			if(DBG)console.log('TODO: xlink "'+item+'": ', {item:item, parentFeatureId:parentFeatureId})
			return;
		}
		if (!item['ID']) {
			if(DBG)console.log('TODO: SKIP item('+featureType+') - missing ID: ', {item:item, parentFeatureId:parentFeatureId})
			return;
		}
		var id = item['ID']
		console.log('item('+featureType+'): ', {item:item, parentFeatureId:parentFeatureId})
		var featureId = featureType + '.' + id
		Object.keys(item).filter(function (fieldName) { return ('ID' !== fieldName); })
		.forEach(function (fieldName) {
			parseGraphRec(item[fieldName], fieldName, featureId)
		})
	})
  // items.forEach(element => {
  //   const rowObject = hasTargetArray(element);
  //   if (rowObject)
  //     rowObject.array.forEach(objectElement => {
  //       const trackNode = hasTargetArray(objectElement);
  //       if (trackNode) {
  //         let group = trackNode.name;
  //         let expanded = false;
  //         if (expansion && expansion.type && group === expansion.type) {
  //           group = trackNode.array[0][expansion.criteria];
  //           expanded = true;
  //         }
  //         nodes[group] = {expanded: expanded, type: trackNode.name};
  //         instanceTypes[trackNode.array[0].ID] = group;
  //       }
  //     });
  // });

	return {
		nodes: nodesArray,
		links: linksArray
	}
}


function isP5LinkObject(json) {
	if ( !('type' in json) || !json['type'] ) return false;
	if ( !('value' in json) || !json['value'] ) return false;
	if ( !('@typeName' in json) || !json['@typeName'] ) return false;
	if ( !('@startIndex' in json) || !json['@startIndex'] ) return false;
	if ( !('@backRefPK' in json) || !json['@backRefPK'] ) return false;
	if ( !('@backRefNS' in json) || !json['@backRefNS'] ) return false;
	return true;
}

function dataMakeNode(params) {
	var objectName = params.typeName.substr(params.typeName.indexOf(':') + 1)
	var nodeId = objectName + '.' + params.primaryKey // TODO: primaryKey?
	var graphNode = {
		id: nodeId,
		name: nodeId,
		group: objectName,
		_loaded: true,
		typeName: params.typeName,
		primaryKey: params.primaryKey // TODO: _primaryKey
	}
	if (params.row) graphNode.row = params.row
	return graphAddNodeLabel(graphNode)
}
function dataMakeEdge(parentNodeId, nodeObject) {
	return {
		id: parentNodeId + nodeObject.id,
		source: parentNodeId,
		target: nodeObject.id,
	}
}
function dataMakeXlinkNode(params) {
	var objectName = params.typeName.substr(params.typeName.indexOf(':') + 1)
	var nodeId = params.xlink.substr(params.xlink.indexOf('#') + 1)
	var primaryKey = nodeId.substr(nodeId.lastIndexOf('.') + 1)
	var graphNode = {
		id: nodeId,
		name: nodeId,
		group: objectName,
		_loaded: false,
		typeName: params.typeName,
		primaryKey: primaryKey
	}
	return graphAddNodeLabel(graphNode)
}

function dataMakeFetchMoreNode(params) {
	// params = {
	// 	parentNodeId: parentNodeId,
	// 	type: json.type,
	// 	objectName: objectName,
	// 	ref: json,
	// }
	if(DBG)console.log('DBG dataMakeFetchMoreNode(params)', params)
	var nodeId = params.parentNodeId+'fetch-more-features-'+params.type+'-on-' + params.objectName;
	return {
		id: nodeId,
		label: 'Pobierz więcej (+)',
		group: 'fetch-more-data', // params.objectName,
		_loaded: false,
		_type: 'ref',
		parentNodeId: params.parentNodeId,
		ref: params.ref,
		// typeName: typeName,
		// primaryKey: nodeId.substr(nodeId.lastIndexOf('.') + 1)
	};
}
function dataMakeFetchMoreEdge(parentNodeId, fetchMoreNode) {
	// @param parentNodeId - from node id
	// @param fetchMoreNode - from dataMakeFetchMoreNode
	if(DBG)console.log('DBG dataMakeFetchMoreEdge(parentNodeId, fetchMoreNode)', {parentNodeId:parentNodeId, fetchMoreNode:fetchMoreNode})
	return {
		id: fetchMoreNode.id,
		from: parentNodeId,
		to: fetchMoreNode.id
	}
}
function makeShortLabel(label) { // TODO: shorter name
	// BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object.1234567

	if (label.length < 30) return label;
	var exLabel = label.split('.')
	if (exLabel.length !== 2) return label;
	return exLabel[0].substr(0, 24) + '...' + exLabel[0].substr(-10) + '.' + exLabel[1];
}
function graphAddNodeLabel(graphNode) {
	// var graphNode = {
	// 	id: nodeId,
	// 	name: nodeId,
	// 	group: objectName,
	// 	_loaded: true,
	// 	typeName: params.typeName,
	// 	primaryKey: params.primaryKey // TODO: _primaryKey
	// }
	var label = graphNode.id // TODO: get from schema assert @label attribute
	label = makeShortLabel(graphNode.id)
	switch (graphNode.typeName) {
		case 'default_db__x3A__BI_audit_KRS:BI_audit_KRS': {
			// <xs:assert test="@default_db__x3A__BI_audit_KRS:label = concat(nazwa, ' ', krs, ' ', S_miejscowosc)" id="I_audit_KRS___d6e76977-1">
			if (graphNode.row) label = [graphNode.row.nazwa, graphNode.row.krs, graphNode.row.S_miejscowosc].filter(function (val) { return val; }).join(' ');
			label = (label) ? label : makeShortLabel(graphNode.id)
			label = label + ' (krs)'
		} break;
		case 'default_db__x3A__BI_audit_ENERGA_PRACOWNICY:BI_audit_ENERGA_PRACOWNICY': {
			// <xs:assert id="_PRACOWNICY___d6e76324-1" test="@label = concat(imiona, ' ', nazwisko, ' ', miejscowosc)"/>
			if (graphNode.row) label = [graphNode.row.imiona, graphNode.row.nazwisko, graphNode.row.miejscowosc].filter(function (val) { return val; }).join(' ');
			label = (label) ? label : makeShortLabel(graphNode.id)
			label = label + ' (pracownicy)'
		} break;
		case 'default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI:BI_audit_ENERGA_RUM_KONTRAHENCI': {
			// <xs:assert id="KONTRAHENCI___d6e76526-1" test="@label = concat(substr(Pelna_nazwa_kontrahenta, 0, 20, '\n umów na kwotę ', @sum))"/>
			if (graphNode.row) label = [graphNode.row.Pelna_nazwa_kontrahenta].filter(function (val) { return val; }).join(' ');
			label = (label) ? label : makeShortLabel(graphNode.id)
			label = label + ' (kontrahent)'
		} break;
		case 'default_db__x3A__BI_audit_CEIDG:BI_audit_CEIDG': {
			// <xs:assert test="@label = concat(nazwisko, substring(firma, 1, 20))"/>
			if (graphNode.row) label = [graphNode.row.nazwisko, graphNode.row.firma].filter(function (val) { return val; }).join(' ');
			label = (label) ? label : makeShortLabel(graphNode.id)
			label = label + ' (ceidg)'
		} break;
		case 'default_db__x3A__BI_audit_MSIG:BI_audit_MSIG': {
			// <xs:assert test="@label = substring(nazwa, 1, 20)"/>
			if (graphNode.row) label = [graphNode.row.nazwa].filter(function (val) { return val; }).join(' ');
			label = (label) ? label : makeShortLabel(graphNode.id)
			label = label + ' (msig)'
		} break;
	}
	return Object.assign(graphNode, {
		label: label,
	})
}


global.graphShowHide = graphShowHide;