/*
 *
 * K2HR3 Web Application
 *
 * Copyright 2017 Yahoo Japan Corporation.
 *
 * K2HR3 is K2hdkc based Resource and Roles and policy Rules, gathers
 * common management information for the cloud.
 * K2HR3 can dynamically manage information as "who", "what", "operate".
 * These are stored as roles, resources, policies in K2hdkc, and the
 * client system can dynamically read and modify these information.
 *
 * For the full copyright and license information, please view
 * the license file that was distributed with this source code.
 *
 * AUTHOR:   Takeshi Nakatani
 * CREATE:   Tue Aug 22 2017
 * REVISION:
 *
 */

import React						from 'react';
import ReactDOM						from 'react-dom';						// eslint-disable-line no-unused-vars
import PropTypes					from 'prop-types';

import TextField					from '@mui/material/TextField';
import Typography					from '@mui/material/Typography';
import IconButton					from '@mui/material/IconButton';
import FormControl					from '@mui/material/FormControl';	// For Radio Button
import FormControlLabel				from '@mui/material/FormControlLabel';
import RadioGroup					from '@mui/material/RadioGroup';
import Radio						from '@mui/material/Radio';
import Tooltip						from '@mui/material/Tooltip';
import Box							from '@mui/material/Box';
import DeleteIcon					from '@mui/icons-material/ClearRounded';
import AddIcon						from '@mui/icons-material/AddRounded';
import UpIcon						from '@mui/icons-material/ArrowUpwardRounded';
import DownIcon						from '@mui/icons-material/ArrowDownwardRounded';

import { r3Resource }				from './r3styles';
import R3FormButtons				from './r3formbuttons';					// Buttons
import R3PopupMsgDialog				from './r3popupmsgdialog';
import R3Message					from '../util/r3message';
import { regYrnAnyResourcePath }	from '../util/r3define';
import { errorType, resourceTypeString, resourceTypeObject, actionTypeName, actionTypeValue, actionTypeNewKey, actionTypeNewValue, actionTypeDelete, actionTypeAdd, actionTypeUp, actionTypeDown } from '../util/r3types';
import { r3DeepClone, r3DeepCompare, r3RenameObjectKey, r3IsJSON, r3ConvertFromJSON, r3IsEmptyEntity, r3IsEmptyEntityObject, r3IsEmptyStringObject, r3IsEmptyString, r3IsSafeTypedEntity } from '../util/r3util';

//
// Local variables
//
const tooltipValues = {
	deleteKeysTooltip:				'deleteKeysTooltip',
	addKeysTooltip:					'addKeysTooltip',
	downAliasTooltip:				'downAliasTooltip',
	upAliasTooltip:					'upAliasTooltip',
	deleteAliasTooltip:				'deleteAliasTooltip',
	addAliasTooltip:				'addAliasTooltip'
};

const resourceComponentValues = {
	valueTypeRadioName:				'resourceType',
	valueTextFieldName:				'resourceValue',
	keysKeyTextFieldNamePrefix:		'resourceKeysKey_',
	keysValueTextFieldNamePrefix:	'resourceKeysValue_',
	keysNewKeyTextFieldName:		'resourceKeysNewKey',
	keysNewValueTextFieldName:		'resourceKeysNewValue',
	aliasTextFieldNamePrefix:		'aliasValue_',
	aliasNewTextFieldName:			'aliasNew'
};

//
// Resource Contents Class
//
export default class R3Resource extends React.Component
{
	static propTypes = {
		r3provider:	PropTypes.object.isRequired,
		resource:	PropTypes.object.isRequired,
		dispUnique:	PropTypes.number.isRequired,
		isReadMode:	PropTypes.bool,
		onSave:		PropTypes.func.isRequired,
		onUpdate:	PropTypes.func.isRequired
	};

	static defaultProps = {
		isReadMode:	false
	};

	state = R3Resource.createState(this.props.resource, this.props.dispUnique);

	constructor(props)
	{
		super(props);

		// Binding(do not define handlers as arrow functions for performance)
		this.handleSave					= this.handleSave.bind(this);
		this.handleCancel				= this.handleCancel.bind(this);
		this.handleConfirmDialogClose	= this.handleConfirmDialogClose.bind(this);
		this.handleMessageDialogClose	= this.handleMessageDialogClose.bind(this);

		this.handleValueTypeChange		= this.handleValueTypeChange.bind(this);
		this.handleValueChange			= this.handleValueChange.bind(this);
		this.handleKeysChange			= this.handleKeysChange.bind(this);
		this.handleAddKeysChange		= this.handleAddKeysChange.bind(this);
		this.handleAliasesChange		= this.handleAliasesChange.bind(this);
		this.handleAddAliasesChange		= this.handleAddAliasesChange.bind(this);

		// styles
		this.sxClasses					= r3Resource(props.theme);
	}

	componentDidMount()
	{
		// update State
		this.setState(R3Resource.createState(this.props.resource, this.props.dispUnique));
	}

	// [NOTE]
	// Use getDerivedStateFromProps by deprecating componentWillReceiveProps in React 17.x.
	// The only purpose is to set the state data from props when the dialog changes from hidden to visible.
	//
	static getDerivedStateFromProps(nextProps, prevState)
	{
		if(nextProps.dispUnique !== prevState.dispUnique){
			// Switching content
			return R3Resource.createState(nextProps.resource, nextProps.dispUnique);
		}
		return null;	// Return null to indicate no change to state.
	}

	static createState(resource, dispUnique)
	{
		let	resourceType	= resourceTypeString;	// default
		let	resourceValue	= '';
		if(!r3IsEmptyEntity(resource)){
			if(r3IsSafeTypedEntity(resource.string, 'string')){
				resourceType	= resourceTypeString;
				resourceValue	= resource.string;
			}else if(r3IsSafeTypedEntity(resource.object, 'object')){
				resourceType	= resourceTypeObject;
				resourceValue	= JSON.stringify(resource.object);
			}
		}

		return {
			dispUnique:				dispUnique,
			resource:				r3DeepClone(resource),
			resourceType:			resourceType,
			resourceValue:			resourceValue,
			addKeyName:				'',
			addKeyValue:			'',
			addAliases:				'',
			changed:				false,
			confirmMessageObject:	null,
			messageDialogObject:	null,

			tooltips: {
				deleteKeysTooltip:	null,			// keyname
				addKeysTooltip:		false,
				downAliasTooltip:	-1,				// position
				upAliasTooltip:		-1,				// position
				deleteAliasTooltip:	-1,				// position
				addAliasTooltip:	false
			}
		};
	}

	//
	// Check only resource value and type state
	//
	isChangedKeysValueState(nowType, nowValue)
	{
		if(r3IsEmptyEntity(nowType) || r3IsEmptyEntity(nowValue)){
			nowType	= this.state.resourceType;
			nowValue= this.state.resourceValue;
		}
		// check type
		let	propsType	= resourceTypeString;
		let	propsValue	= '';
		if(!r3IsEmptyEntity(this.props.resource) && r3IsSafeTypedEntity(this.props.resource.string, 'string')){
			propsType	= resourceTypeString;
			propsValue	= this.props.resource.string;
		}else if(!r3IsEmptyEntity(this.props.resource) && r3IsSafeTypedEntity(this.props.resource.object, 'object')){
			propsType	= resourceTypeObject;
			propsValue	= this.props.resource.object;
		}
		if(propsType !== nowType){
			return true;
		}

		// check value
		return !r3DeepCompare(nowValue, propsValue);
	}

	//
	// Check only keys state
	//
	isChangedKeysNameState(nowKeys)
	{
		if(r3IsEmptyEntity(nowKeys)){
			nowKeys	= r3IsEmptyEntity(this.state.resource) ? undefined : this.state.resource.keys;
		}
		if(r3IsEmptyEntity(nowKeys)){
			nowKeys = {};									// empty object
		}
		let	propsKeys = r3IsEmptyEntity(this.props.resource) ? undefined : this.props.resource.keys;
		if(r3IsEmptyEntity(propsKeys)){
			propsKeys = {};									// empty object
		}
		// check
		return !r3DeepCompare(nowKeys, propsKeys);
	}

	//
	// Check only aliases state
	//
	isChangedAliasesState(nowAliases)
	{
		if(r3IsEmptyEntity(nowAliases)){
			nowAliases	= r3IsEmptyEntity(this.state.resource) ? undefined : this.state.resource.aliases;
		}
		if(!r3IsSafeTypedEntity(nowAliases, 'array')){
			nowAliases = [];									// empty array
		}
		let	propsAliases = r3IsEmptyEntity(this.props.resource) ? undefined : this.props.resource.aliases;
		if(!r3IsSafeTypedEntity(propsAliases, 'array')){
			propsAliases = [];									// empty object
		}
		// check
		return !r3DeepCompare(nowAliases, propsAliases);
	}

	//
	// Check all state
	//
	isChangedState(nowType, nowValue, nowKeys, nowAliases)
	{
		// check resource value and type
		if(this.isChangedKeysValueState(nowType, nowValue)){
			return true;
		}
		// check keys
		if(this.isChangedKeysNameState(nowKeys)){
			return true;
		}
		// check aliases
		if(this.isChangedAliasesState(nowAliases)){
			return true;
		}
		return false;
	}

	//
	// Handle Form Button : Save
	//
	handleSave(event)														// eslint-disable-line no-unused-vars
	{
		if(this.state.changed){
			//
			// Check resource
			//
			let	newResource = r3DeepClone(this.state.resource);
			let	cnt;
			let	cnt2;

			//
			// type and value
			//
			if(resourceTypeString === this.state.resourceType){
				if(r3IsEmptyString(this.state.resourceValue)){
					newResource.string = null;
					newResource.object = null;
				}else{
					newResource.string = this.state.resourceValue.trim();
					newResource.object = null;
				}
			}else if(resourceTypeObject === this.state.resourceType){
				if(r3IsEmptyString(this.state.resourceValue)){
					newResource.object = null;
					newResource.string = null;
				}else{
					let	tmpValue = this.state.resourceValue.trim();
					if(!r3IsJSON(tmpValue)){
						// value as string to JSON
						tmpValue = JSON.stringify(tmpValue);
					}
					newResource.object = r3ConvertFromJSON(tmpValue);	// to objects
					newResource.string = null;
				}
			}else{
				this.setState({
					messageDialogObject:	new R3Message(this.props.r3provider.getR3TextRes().eResourceType, errorType)
				});
				return;
			}

			//
			// keys
			//
			if(!r3IsSafeTypedEntity(newResource.keys, 'object')){
				newResource.keys = {};
			}
			let	keyNames = Object.keys(newResource.keys);
			let	nameLists= {};

			// check same key name and empty
			for(cnt = 0; cnt < keyNames.length; ++cnt){
				let	name= keyNames[cnt];
				if(r3IsEmptyString(name, true)){
					this.setState({
						messageDialogObject:	new R3Message(this.props.r3provider.getR3TextRes().eEmptyKey, errorType)
					});
					return;
				}
				name = name.trim();
				let	pos	= name.lastIndexOf('\n');
				if(-1 !== pos){
					name = name.substr(0, pos);							// cut '\n'...
					if(r3IsEmptyString(name)){
						this.setState({
							messageDialogObject:	new R3Message(this.props.r3provider.getR3TextRes().eEmptyKey, errorType)
						});
						return;
					}
				}
				if(!r3IsEmptyEntity(nameLists[name])){
					this.setState({
						messageDialogObject:	new R3Message(this.props.r3provider.getR3TextRes().eSameKey, errorType)
					});
					return;
				}
				nameLists[name] = null;
			}

			// convert key name in object and convert value to object
			for(cnt = 0; cnt < keyNames.length; ++cnt){
				let	name= keyNames[cnt].trim();
				let	pos	= name.lastIndexOf('\n');
				if(-1 !== pos){
					name = name.substr(0, pos);							// cut '\n'...
					newResource.keys[name] = newResource.keys[keyNames[cnt]];
					delete newResource.keys[keyNames[cnt]];
				}
				let	tmpValue = newResource.keys[name];
				if(!r3IsJSON(tmpValue)){
					// value to JSON
					tmpValue = JSON.stringify(tmpValue);
				}
				newResource.keys[name] = r3ConvertFromJSON(tmpValue);	// to objects
			}

			//
			// aliases
			//
			if(!r3IsSafeTypedEntity(newResource.aliases, 'array')){
				newResource.aliases = [];
			}
			// check empty and yrn path by regex
			let	regAliasPath = new RegExp(regYrnAnyResourcePath);
			for(cnt = 0; cnt < newResource.aliases.length; ++cnt){
				newResource.aliases[cnt] = newResource.aliases[cnt].trim();

				if(null === newResource.aliases[cnt].match(regAliasPath)){
					this.setState({
						messageDialogObject:	new R3Message(this.props.r3provider.getR3TextRes().eNotYRNAliases, errorType)
					});
					return;
				}
			}
			// check same value
			for(cnt = 0; cnt < newResource.aliases.length; ++cnt){
				for(cnt2 = (cnt + 1); cnt2 < newResource.aliases.length; ++cnt2){
					if(newResource.aliases[cnt] === newResource.aliases[cnt2]){
						this.setState({
							messageDialogObject:	new R3Message(this.props.r3provider.getR3TextRes().eAliasArray, errorType)
						});
						return;
					}
				}
			}

			//
			// Check changed
			//
			if(!this.isChangedState(this.state.resourceType, (resourceTypeString === this.state.resourceType ? newResource.string : newResource.object), newResource.keys, newResource.aliases)){
				this.setState({
					messageDialogObject:	new R3Message(this.props.r3provider.getR3TextRes().eNoChange, errorType)
				});
				return;
			}
			this.props.onSave(newResource);
		}
	}

	//
	// Handle Form Button : Cancel
	//
	handleCancel(event)														// eslint-disable-line no-unused-vars
	{
		if(this.state.changed){
			this.setState({
				confirmMessageObject:	new R3Message(this.props.r3provider.getR3TextRes().cUpdatingCancel)
			});
		}
	}

	//
	// Handle Confirm Dialog : Close( OK / Cancel )
	//
	handleConfirmDialogClose(event, reason, result)
	{
		if(result){
			// case for 'cancel updating' to do
			this.props.onUpdate(false);

			// rewind State
			this.setState(R3Resource.createState(this.props.resource, this.props.dispUnique));
		}else{
			// case for 'cancel updating' to cancel
			this.setState({
				confirmMessageObject:	null
			});
		}
	}

	//
	// Handle Message Dialog : Close
	//
	handleMessageDialogClose(event, reason, result)							// eslint-disable-line no-unused-vars
	{
		this.setState({
			messageDialogObject:	null
		});
	}

	//
	// Handle Resource Value Type : Change
	//
	handleValueTypeChange(event, type)
	{
		if(this.state.resourceType === type){
			console.warn('changed value type(' + JSON.stringify(type) + ') is something wrong.');
			return;
		}
		// now type and value
		let	nowResourceType	= this.state.resourceType;
		let	nowResourceValue= this.state.resourceValue;

		// default(props) value by now type
		let	defaultResourceValue;
		if(resourceTypeString === nowResourceType){
			defaultResourceValue = r3IsEmptyStringObject(this.props.resource, 'string') ? '' : this.props.resource.string;
		}else{
			defaultResourceValue = (r3IsEmptyEntity(this.props.resource) || !r3IsSafeTypedEntity(this.props.resource.object, 'object')) ? '' : this.props.resource.object;
		}

		// new type and value
		let	newResourceType	= type;
		let	newResourceValue= nowResourceValue;			// = now value

		// check whether now value and default value is changed
		if(r3DeepCompare(nowResourceValue, defaultResourceValue)){
			// not modified value, thus gets new value for new type from props
			if(resourceTypeString === newResourceType){
				newResourceValue = r3IsEmptyStringObject(this.props.resource, 'string') ? '' : this.props.resource.string;
			}else{
				newResourceValue = (r3IsEmptyEntity(this.props.resource) || !r3IsSafeTypedEntity(this.props.resource.object, 'object')) ? '' : JSON.stringify(this.props.resource.object);
			}
		}

		// set parent changed state
		let	changed = this.isChangedState(newResourceType, newResourceValue);
		this.props.onUpdate(changed);

		// update state
		this.setState({
			changed:		changed,
			resourceType:	newResourceType,
			resourceValue:	newResourceValue
		});
	}

	//
	// Handle Resource Value : Change
	//
	handleValueChange(event)
	{
		// set parent changed state
		let	changed = this.isChangedState(this.state.resourceType, event.target.value);
		this.props.onUpdate(changed);

		// update state
		this.setState({
			changed:		changed,
			resourceValue:	event.target.value
		});
	}

	//
	// Handle Resource Keys( key, value ) : Change
	//
	handleKeysChange(event, type, keyname)
	{
		let	nowKeys = {};
		if(r3IsSafeTypedEntity(this.state.resource.keys, 'object')){
			nowKeys = r3DeepClone(this.state.resource.keys);
		}

		let	changedValue	= r3IsEmptyEntityObject(event.target, 'value') ? null : event.target.value;
		let	newKeys			= nowKeys;
		let	isClearNewKey	= false;
		if(actionTypeName === type){
			//
			// changed name
			//
			if(!r3IsEmptyEntity(nowKeys[changedValue])){
				// found same name, we allow it as temporary. then pickups temporary additional name.
				for(let counter = 0, found = false; !found; ++counter){
					let tmpName		= changedValue + '\n' + String(counter);		// convert new name with ('\n' + number)
					if(r3IsEmptyEntity(nowKeys[tmpName])){
						changedValue= tmpName;
						found		= true;
					}
				}
			}
			// change keyname
			newKeys = r3RenameObjectKey(nowKeys, keyname, changedValue);

		}else if(actionTypeValue === type){
			//
			// changed value
			//
			newKeys[keyname] = changedValue;

		}else if(actionTypeDelete === type){
			if(!r3IsEmptyEntity(newKeys[keyname])){
				delete newKeys[keyname];
			}

		}else if(actionTypeAdd === type){
			if(r3IsEmptyString(this.state.addKeyName)){
				this.setState({
					messageDialogObject:	new R3Message(this.props.r3provider.getR3TextRes().eNewKeyName, errorType)
				});
				return;
			}
			if(!r3IsSafeTypedEntity(this.state.addKeyValue, 'string')){
				this.setState({
					messageDialogObject:	new R3Message(this.props.r3provider.getR3TextRes().eNewKeyValue, errorType)
				});
				return;
			}

			//
			// check name
			//
			let	newName = this.state.addKeyName;
			if(!r3IsEmptyEntity(nowKeys[newName])){
				// found same name, we allow it as temporary. then pickups temporary additional name.
				for(let counter = 0, found = false; !found; ++counter){
					let tmpName	= newName + '\n' + String(counter);		// convert new name with ('\n' + number)
					if(r3IsEmptyEntity(nowKeys[tmpName])){
						newName	= tmpName;
						found	= true;
					}
				}
			}
			// add
			newKeys[newName]	= this.state.addKeyValue;
			isClearNewKey		= true;

		}else{
			console.warn('unknown action(' + JSON.stringify(type) + ') for keys.');
			return;
		}

		// make new resource object
		let	newResource		= r3DeepClone(this.state.resource);
		newResource.keys	= newKeys;

		// set parent changed state
		let	changed			= this.isChangedState(null, null, newKeys);
		this.props.onUpdate(changed);

		// update state
		this.setState({
			resource:		newResource,
			changed:		changed,
			addKeyName:		(isClearNewKey ? '' : this.state.addKeyName),
			addKeyValue:	(isClearNewKey ? '' : this.state.addKeyValue)
		});
	}

	//
	// Handle Add Resource Keys( key, value ) : Change
	//
	handleAddKeysChange(event, type)
	{
		// update state
		if(actionTypeNewKey === type){
			this.setState({
				addKeyName:		event.target.value
			});
		}else if(actionTypeNewValue === type){
			this.setState({
				addKeyValue:	event.target.value
			});
		}else{
			console.warn('unknown action(' + JSON.stringify(type) + ') for keys.');
			return;
		}
	}

	//
	// Handle Resource Aliases : Change
	//
	handleAliasesChange(event, type, pos)
	{
		let	changedValue	= r3IsEmptyEntityObject(event.target, 'value') ? null : event.target.value;
		let	isClearNewAlias	= false;
		let	newAliases		= [];
		if(r3IsSafeTypedEntity(this.state.resource.aliases, 'array')){
			newAliases		= r3DeepClone(this.state.resource.aliases);
		}

		if(actionTypeValue === type){
			// change value at pos
			newAliases[pos]	= changedValue;

		}else if(actionTypeDelete === type){
			// delete value at pos
			newAliases.splice(pos, 1);

		}else if(actionTypeUp === type){
			// move pos's value to up
			let	targetValue = newAliases[pos];			// real or ref
			newAliases.splice((pos - 1), 0, targetValue);
			newAliases.splice(pos + 1, 1);

		}else if(actionTypeDown === type){
			// move pos's value to down
			let	targetValue = newAliases[pos + 1];		// real or ref
			newAliases.splice(pos, 0, targetValue);
			newAliases.splice(pos + 2, 1);

		}else if(actionTypeAdd === type){
			// add new aliases
			if(r3IsEmptyString(this.state.addAliases, true)){
				this.setState({
					messageDialogObject:	new R3Message(this.props.r3provider.getR3TextRes().eNewAliases, errorType)
				});
				return;
			}
			// add
			newAliases.push(this.state.addAliases.trim());
			isClearNewAlias = true;

		}else{
			console.warn('unknown action(' + JSON.stringify(type) + ') for aliases.');
			return;
		}

		// make new resource object
		let	newResource		= r3DeepClone(this.state.resource);
		newResource.aliases	= newAliases;

		// set parent changed state
		let	changed			= this.isChangedState(null, null, null, newAliases);
		this.props.onUpdate(changed);

		// update state
		this.setState({
			resource:		newResource,
			changed:		changed,
			addAliases:		(isClearNewAlias ? '' : this.state.addAliases)
		});
	}

	//
	// Handle Add Resource Aliases : Change
	//
	handleAddAliasesChange(event)
	{
		// update state
		this.setState({
			addAliases:		event.target.value
		});
	}

	handTooltipChange = (event, type, extData) =>
	{
		if(tooltipValues.deleteKeysTooltip === type){
			this.setState({
				tooltips: {
					deleteKeysTooltip:	extData
				}
			});
		}else if(tooltipValues.addKeysTooltip === type){
			this.setState({
				tooltips: {
					addKeysTooltip:		extData
				}
			});
		}else if(tooltipValues.downAliasTooltip === type){
			this.setState({
				tooltips: {
					downAliasTooltip:	extData
				}
			});
		}else if(tooltipValues.upAliasTooltip === type){
			this.setState({
				tooltips: {
					upAliasTooltip:		extData
				}
			});
		}else if(tooltipValues.deleteAliasTooltip === type){
			this.setState({
				tooltips: {
					deleteAliasTooltip:	extData
				}
			});
		}else if(tooltipValues.addAliasTooltip === type){
			this.setState({
				tooltips: {
					addAliasTooltip:	extData
				}
			});
		}
	};

	getKeysContents(items)
	{
		const { theme, r3provider } = this.props;

		if(!r3IsSafeTypedEntity(items, 'object')){
			return;
		}

		let	elementArray = [];
		let	elementCount = 0;
		Object.keys(items).forEach( (keyname) => {
			let	value = '';
			if(r3IsEmptyEntity(items[keyname])){
				value = '';
			}else if(r3IsSafeTypedEntity(items[keyname], 'array')){
				value = JSON.stringify(items[keyname]);
			}else if(r3IsSafeTypedEntity(items[keyname], 'object')){
				value = JSON.stringify(items[keyname]);
			}else{
				// probabry string type
				value = String(items[keyname]);
			}

			// if keyname has '\n', it means escaped same key name.
			let	dispName= keyname;
			let	pos		= dispName.lastIndexOf('\n');
			if(-1 !== pos){
				dispName = dispName.substr(0, pos);
			}

			let	deleteButton;
			if(this.props.isReadMode){
				deleteButton = (
					<IconButton
						disabled={ true }
						{ ...theme.r3Resource.deleteKeysButton }
						sx={ this.sxClasses.deleteInvisibleKeysButton }
						size="large"
					>
						<DeleteIcon />
					</IconButton>
				);
			}else{
				deleteButton = (
					<Tooltip
						title={ r3provider.getR3TextRes().tResResourceKeysDelTT }
						open={ ((r3IsEmptyEntityObject(this.state, 'tooltips') || !r3IsSafeTypedEntity(this.state.tooltips.deleteKeysTooltip, 'string') || (this.state.tooltips.deleteKeysTooltip != keyname)) ? false : true) }
					>
						<IconButton
							onClick={ (event) => this.handleKeysChange(event, actionTypeDelete, keyname) }
							onMouseEnter={ event => this.handTooltipChange(event, tooltipValues.deleteKeysTooltip, keyname) }
							onMouseLeave={ event => this.handTooltipChange(event, tooltipValues.deleteKeysTooltip, null) }
							{ ...theme.r3Resource.deleteKeysButton }
							sx={ this.sxClasses.deleteKeysButton }
							size="large"
						>
							<DeleteIcon />
						</IconButton>
					</Tooltip>
				);
			}

			let	inputProps;
			if(this.props.isReadMode){
				inputProps = {};
			}else{
				inputProps = {
					sx: this.sxClasses.inputTextField
				};
			}

			elementArray.push(
				<Box
					key={ elementCount++ }
					sx={ this.sxClasses.enclosureElement }
				>
					<TextField
						name={ resourceComponentValues.keysKeyTextFieldNamePrefix + String(elementCount) }
						value={ dispName }
						disabled={ this.props.isReadMode }
						placeholder={ r3provider.getR3TextRes().tResResourceKeysKeyHint }
						onChange={ (event) => this.handleKeysChange(event, actionTypeName, keyname) }
						InputProps={ inputProps }
						{ ...theme.r3Resource.keysKeyTextField }
						sx={ this.sxClasses.keysKeyTextField }
					/>
					<TextField
						name={ resourceComponentValues.keysValueTextFieldNamePrefix + String(elementCount) }
						value={ value }
						disabled={ this.props.isReadMode }
						placeholder={ r3provider.getR3TextRes().tResResourceKeysValueHint }
						onChange={ (event) => this.handleKeysChange(event, actionTypeValue, keyname) }
						InputProps={ inputProps }
						{ ...theme.r3Resource.keysValueTextField }
						sx={ this.sxClasses.keysValueTextField }
					/>
					{ deleteButton }
				</Box>
			);
		});

		return (
			elementArray.map( (item, pos) => {								// eslint-disable-line no-unused-vars
				return item;
			})
		);
	}

	getAddKeysContents()
	{
		const { theme, r3provider } = this.props;

		if(this.props.isReadMode){
			return;
		}

		return (
			<Box
				sx={ this.sxClasses.enclosureElement }
			>
				<TextField
					name={ resourceComponentValues.keysNewKeyTextFieldName }
					value={ this.state.addKeyName }
					placeholder={ r3provider.getR3TextRes().tResResourceKeysKeyHint }
					onChange={ (event) => this.handleAddKeysChange(event, actionTypeNewKey) }
					InputProps={{ sx: this.sxClasses.inputTextField }}
					{ ...theme.r3Resource.keysKeyTextField }
					sx={ this.sxClasses.keysKeyTextField }
				/>
				<TextField
					name={ resourceComponentValues.keysNewValueTextFieldName }
					value={ this.state.addKeyValue }
					placeholder={ r3provider.getR3TextRes().tResResourceKeysValueHint }
					onChange={ (event) => this.handleAddKeysChange(event, actionTypeNewValue) }
					InputProps={{ sx: this.sxClasses.inputTextField }}
					{ ...theme.r3Resource.keysValueTextField }
					sx={ this.sxClasses.keysValueTextField }
				/>
				<Tooltip
					title={ r3provider.getR3TextRes().tResResourceKeysAddTT }
					open={ ((r3IsEmptyEntityObject(this.state, 'tooltips') || !r3IsSafeTypedEntity(this.state.tooltips.addKeysTooltip, 'boolean')) ? false : this.state.tooltips.addKeysTooltip) }
				>
					<IconButton
						onClick={ (event) => this.handleKeysChange(event, actionTypeAdd, null) }
						onMouseEnter={ event => this.handTooltipChange(event, tooltipValues.addKeysTooltip, true) }
						onMouseLeave={ event => this.handTooltipChange(event, tooltipValues.addKeysTooltip, false) }
						{ ...theme.r3Resource.addKeysButton }
						sx={ this.sxClasses.addKeysButton }
						size="large"
					>
						<AddIcon />
					</IconButton>
				</Tooltip>
			</Box>
		);
	}

	getAliasContents(items)
	{
		const { theme, r3provider } = this.props;

		if(!r3IsSafeTypedEntity(items, 'array')){
			return;
		}
		let	_items = items;

		return _items.map( (item, pos) =>
		{
			let	downButton;
			if(this.props.isReadMode || (_items.length <= (pos + 1))){
				downButton = (
					<IconButton
						disabled={ true }
						{ ...theme.r3Resource.downAliasButton }
						sx={ this.sxClasses.arrowInvisibleAliasButton }
						size="large"
					>
						<DownIcon />
					</IconButton>
				);
			}else{
				downButton = (
					<Tooltip
						title={ r3provider.getR3TextRes().tResAliasDownTT }
						open={ ((r3IsEmptyEntityObject(this.state, 'tooltips') || !r3IsSafeTypedEntity(this.state.tooltips.downAliasTooltip, 'number') || (this.state.tooltips.downAliasTooltip != pos)) ? false : true) }
					>
						<IconButton
							onClick={ (event) => this.handleAliasesChange(event, actionTypeDown, pos) }
							onMouseEnter={ event => this.handTooltipChange(event, tooltipValues.downAliasTooltip, pos) }
							onMouseLeave={ event => this.handTooltipChange(event, tooltipValues.downAliasTooltip, -1) }
							{ ...theme.r3Resource.downAliasButton }
							sx={ this.sxClasses.arrowAliasButton }
							size="large"
						>
							<DownIcon />
						</IconButton>
					</Tooltip>
				);
			}

			let	upButton;
			if(this.props.isReadMode || (0 === pos)){
				upButton = (
					<IconButton
						disabled={ true }
						{ ...theme.r3Resource.upAliasButton }
						sx={ this.sxClasses.arrowInvisibleAliasButton }
						size="large"
					>
						<UpIcon />
					</IconButton>
				);
			}else{
				upButton = (
					<Tooltip
						title={ r3provider.getR3TextRes().tResAliasUpTT }
						open={ ((r3IsEmptyEntityObject(this.state, 'tooltips') || !r3IsSafeTypedEntity(this.state.tooltips.upAliasTooltip, 'number') || (this.state.tooltips.upAliasTooltip != pos)) ? false : true) }
					>
						<IconButton
							onClick={ (event) => this.handleAliasesChange(event, actionTypeUp, pos) }
							onMouseEnter={ event => this.handTooltipChange(event, tooltipValues.upAliasTooltip, pos) }
							onMouseLeave={ event => this.handTooltipChange(event, tooltipValues.upAliasTooltip, -1) }
							{ ...theme.r3Resource.upAliasButton }
							sx={ this.sxClasses.arrowAliasButton }
							size="large"
						>
							<UpIcon />
						</IconButton>
					</Tooltip>
				);
			}

			let	deleteButton;
			if(this.props.isReadMode){
				deleteButton = (
					<IconButton
						disabled={ true }
						{ ...theme.r3Resource.deleteAliasButton }
						sx={ this.sxClasses.deleteInvisibleAliasButton }
						size="large"
					>
						<DeleteIcon />
					</IconButton>
				);
			}else{
				deleteButton = (
					<Tooltip
						title={ r3provider.getR3TextRes().tResAliasDelTT }
						open={ ((r3IsEmptyEntityObject(this.state, 'tooltips') || !r3IsSafeTypedEntity(this.state.tooltips.deleteAliasTooltip, 'number') || (this.state.tooltips.deleteAliasTooltip != pos)) ? false : true) }
					>
						<IconButton
							onClick={ (event) => this.handleAliasesChange(event, actionTypeDelete, pos) }
							onMouseEnter={ event => this.handTooltipChange(event, tooltipValues.deleteAliasTooltip, pos) }
							onMouseLeave={ event => this.handTooltipChange(event, tooltipValues.deleteAliasTooltip, -1) }
							{ ...theme.r3Resource.deleteAliasButton }
							sx={ this.sxClasses.deleteAliasButton }
							size="large"
						>
							<DeleteIcon />
						</IconButton>
					</Tooltip>
				);
			}

			let	inputProps;
			if(this.props.isReadMode){
				inputProps = {};
			}else{
				inputProps = {
					sx: this.sxClasses.inputTextField
				};
			}

			return (
				<Box
					key={ pos }
					sx={ this.sxClasses.enclosureElement }
				>
					<TextField
						name={ resourceComponentValues.aliasTextFieldNamePrefix + String(pos) }
						disabled={ this.props.isReadMode }
						value={ item }
						placeholder={ r3provider.getR3TextRes().tResAliasHint }
						onChange={ (event) => this.handleAliasesChange(event, actionTypeValue, pos) }
						InputProps={ inputProps }
						{ ...theme.r3Resource.aliasTextField }
						sx={ this.sxClasses.aliasTextField }
					/>
					{ downButton }
					{ upButton }
					{ deleteButton }
				</Box>
			);
		});
	}

	getAddAliasContents()
	{
		const { theme, r3provider } = this.props;

		if(this.props.isReadMode){
			return;
		}

		return (
			<Box
				sx={ this.sxClasses.enclosureElement }
			>
				<TextField
					name={ resourceComponentValues.aliasNewTextFieldName }
					value={ this.state.addAliases }
					placeholder={ r3provider.getR3TextRes().tResAliasHint }
					onChange={ (event) => this.handleAddAliasesChange(event) }
					InputProps={{ sx: this.sxClasses.inputTextField }}
					{ ...theme.r3Resource.aliasTextField }
					sx={ this.sxClasses.aliasTextField }
				/>
				<Tooltip
					title={ r3provider.getR3TextRes().tResAliasAddTT }
					open={ ((r3IsEmptyEntityObject(this.state, 'tooltips') || !r3IsSafeTypedEntity(this.state.tooltips.addAliasTooltip, 'boolean')) ? false : this.state.tooltips.addAliasTooltip) }
				>
					<IconButton
						onClick={ (event) => this.handleAliasesChange(event, actionTypeAdd, 0) }
						onMouseEnter={ event => this.handTooltipChange(event, tooltipValues.addAliasTooltip, true) }
						onMouseLeave={ event => this.handTooltipChange(event, tooltipValues.addAliasTooltip, false) }
						{ ...theme.r3Resource.addAliasButton }
						sx={ this.sxClasses.addAliasButton }
						size="large"
					>
						<AddIcon />
					</IconButton>
				</Tooltip>
			</Box>
		);
	}

	render()
	{
		console.info('CALL : resource:render()');

		const { theme, r3provider } = this.props;

		let valueTextFieldTheme		= (resourceTypeObject === this.state.resourceType ? theme.r3Resource.valueObjectTextField : theme.r3Resource.valueStringTextField);
		let	leftValueSelectLabel	= (
			<Typography
				{ ...theme.r3Resource.valueFormControlLabel }
			>
				{ r3provider.getR3TextRes().tResResourceValueTypeText }
			</Typography>
		);
		let	rightValueSelectLabel	= (
			<Typography
				{ ...theme.r3Resource.valueFormControlLabel }
			>
				{ r3provider.getR3TextRes().tResResourceValueTypeObj }
			</Typography>
		);

		let	inputProps;
		if(this.props.isReadMode){
			inputProps = {};
		}else{
			inputProps = {
				sx: this.sxClasses.inputTextField
			};
		}

		return (
			<Box
				sx={ this.sxClasses.root }
			>
				<Typography
					{ ...theme.r3Resource.subTitle }
					sx={ this.sxClasses.subTitleTop }
				>
					{ r3provider.getR3TextRes().tResResourceValueSubTitle }
				</Typography>
				<FormControl
					{ ...theme.r3Resource.valueFormControl }
					sx={ this.sxClasses.valueFormControl }
				>
					<RadioGroup
						name={ resourceComponentValues.valueTypeRadioName }
						value={ this.state.resourceType }
						onChange={ this.handleValueTypeChange }
						{ ...theme.r3Resource.valueRadioGroup }
						sx={ this.sxClasses.valueRadioGroup }
					>
						<FormControlLabel
							value={ resourceTypeString }
							label={ leftValueSelectLabel }
							disabled={ this.props.isReadMode }
							control={ <Radio /> }
							{ ...theme.r3Resource.valueLeftFormControlLabel }
							sx={ this.sxClasses.valueLeftFormControlLabel }
						/>
						<FormControlLabel
							value={ resourceTypeObject }
							label={ rightValueSelectLabel }
							disabled={ this.props.isReadMode }
							control={ <Radio /> }
							{ ...theme.r3Resource.valueRightFormControlLabel }
							sx={ this.sxClasses.valueRightFormControlLabel }
						/>
					</RadioGroup>
				</FormControl>

				<TextField
					name={ resourceComponentValues.valueTextFieldName }
					value={ this.state.resourceValue }
					placeholder={ (resourceTypeObject === this.state.resourceType ? r3provider.getR3TextRes().tResResourceValueObjHint : r3provider.getR3TextRes().tResResourceValueTextHint) }
					disabled={ this.props.isReadMode }
					onChange={ (event) => this.handleValueChange(event) }
					InputProps={ inputProps }
					{ ...valueTextFieldTheme }
					sx={ this.sxClasses.valueTextField }
				/>

				<Typography
					{ ...theme.r3Resource.subTitle }
					sx={ this.sxClasses.subTitle }
				>
					{ r3provider.getR3TextRes().tResResourceKeysSubTitle }
				</Typography>
				<Typography
					{ ...theme.r3Resource.keysKeySubTitle }
					sx={ this.sxClasses.keysKeySubTitle }
				>
					{ r3provider.getR3TextRes().tResResourceKeySubTitle }
				</Typography>
				<Typography
					{ ...theme.r3Resource.keysValueSubTitle }
					sx={ this.sxClasses.keysValueSubTitle }
				>
					{ r3provider.getR3TextRes().tResResourceKValueSubTitle }
				</Typography>
				{ this.getKeysContents(this.state.resource.keys) }
				{ this.getAddKeysContents() }

				<Typography
					{ ...theme.r3Resource.subTitle }
					sx={ this.sxClasses.subTitle }
				>
					{ r3provider.getR3TextRes().tResAliasSubTitle }
				</Typography>
				{ this.getAliasContents(this.state.resource.aliases) }
				{ this.getAddAliasContents() }

				<R3FormButtons
					theme={ theme }
					r3provider={ this.props.r3provider }
					status={ this.state.changed }
					onSave={ this.handleSave }
					onCancel={ this.handleCancel }
				/>

				<R3PopupMsgDialog
					theme={ theme }
					r3provider={ this.props.r3provider }
					title={ this.props.r3provider.getR3TextRes().cUpdatingTitle }
					r3Message={ this.state.confirmMessageObject }
					twoButton={ true }
					onClose={ this.handleConfirmDialogClose }
				/>
				<R3PopupMsgDialog
					theme={ theme }
					r3provider={ this.props.r3provider }
					r3Message={ this.state.messageDialogObject }
					onClose={ this.handleMessageDialogClose }
				/>
			</Box>
		);
	}
}

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim600: noexpandtab sw=4 ts=4 fdm=marker
 * vim<600: noexpandtab sw=4 ts=4
 */
