import React from "react";
	import ReactDOM from 'react-dom/client';
/**
 The Country Input specifies an input field where the user can select a country.
*/
export class CountryInput extends React.Component {
	// Gets the id of the React component.
	get id() {
		if (!this._id) {
			this._id =  'CountryInput' + Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
		}

		return this._id;
	}
	/** Enables or disables the element.
	*	Property type: boolean
	*/
	get disabled() {
		return this.nativeElement ? this.nativeElement.disabled : undefined;
	}
	set disabled(value) {
		if (this.nativeElement) {
			this.nativeElement.disabled = value;
		}
	}

	/** Sets additional class names to the Input drop down.
	*	Property type: any
	*/
	get dropDownClassList() {
		return this.nativeElement ? this.nativeElement.dropDownClassList : undefined;
	}
	set dropDownClassList(value) {
		if (this.nativeElement) {
			this.nativeElement.dropDownClassList = value;
		}
	}

	/** Determines the position of the drop down button.
	*	Property type: DropDownButtonPosition | string
	*/
	get dropDownButtonPosition() {
		return this.nativeElement ? this.nativeElement.dropDownButtonPosition : undefined;
	}
	set dropDownButtonPosition(value) {
		if (this.nativeElement) {
			this.nativeElement.dropDownButtonPosition = value;
		}
	}

	/** Sets the height of the drop down. By default it's set to an empty string. In this case the height of the drop down is controlled by a CSS variable.
	*	Property type: string | number
	*/
	get dropDownHeight() {
		return this.nativeElement ? this.nativeElement.dropDownHeight : undefined;
	}
	set dropDownHeight(value) {
		if (this.nativeElement) {
			this.nativeElement.dropDownHeight = value;
		}
	}

	/** Sets the width of the drop down. By default it's set to an empty string. In this case the width of the drop down is controlled by a CSS variable.
	*	Property type: string | number
	*/
	get dropDownWidth() {
		return this.nativeElement ? this.nativeElement.dropDownWidth : undefined;
	}
	set dropDownWidth(value) {
		if (this.nativeElement) {
			this.nativeElement.dropDownWidth = value;
		}
	}

	/** Sets or gets the name attribute for the element. Name is used when submiting data inside an HTML form.
	*	Property type: string
	*/
	get name() {
		return this.nativeElement ? this.nativeElement.name : undefined;
	}
	set name(value) {
		if (this.nativeElement) {
			this.nativeElement.name = value;
		}
	}

	/** Determines whether the drop down is opened or not.
	*	Property type: boolean
	*/
	get opened() {
		return this.nativeElement ? this.nativeElement.opened : undefined;
	}
	set opened(value) {
		if (this.nativeElement) {
			this.nativeElement.opened = value;
		}
	}

	/** Sets or gets an array of country codes which will be used instead of the default one with all countries. The country code should be ISO 3166-1 alpha-2 codes(https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2).
	*	Property type: any
	*/
	get onlyCountries() {
		return this.nativeElement ? this.nativeElement.onlyCountries : undefined;
	}
	set onlyCountries(value) {
		if (this.nativeElement) {
			this.nativeElement.onlyCountries = value;
		}
	}

	/** Determines the placeholder of the input.
	*	Property type: string
	*/
	get placeholder() {
		return this.nativeElement ? this.nativeElement.placeholder : undefined;
	}
	set placeholder(value) {
		if (this.nativeElement) {
			this.nativeElement.placeholder = value;
		}
	}

	/** Sets or gets the selected country of the element. The country code should be ISO 3166-1 alpha-2 codes(https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2).
	*	Property type: string
	*/
	get selectedCountry() {
		return this.nativeElement ? this.nativeElement.selectedCountry : undefined;
	}
	set selectedCountry(value) {
		if (this.nativeElement) {
			this.nativeElement.selectedCountry = value;
		}
	}

	/** Sets or gets the value indicating whether the element is aligned to support locales using right-to-left fonts.
	*	Property type: boolean
	*/
	get rightToLeft() {
		return this.nativeElement ? this.nativeElement.rightToLeft : undefined;
	}
	set rightToLeft(value) {
		if (this.nativeElement) {
			this.nativeElement.rightToLeft = value;
		}
	}

	/** Determines the theme for the element. Themes define the look of the elements.
	*	Property type: string
	*/
	get theme() {
		return this.nativeElement ? this.nativeElement.theme : undefined;
	}
	set theme(value) {
		if (this.nativeElement) {
			this.nativeElement.theme = value;
		}
	}

	/** If is set to true, the element cannot be focused.
	*	Property type: boolean
	*/
	get unfocusable() {
		return this.nativeElement ? this.nativeElement.unfocusable : undefined;
	}
	set unfocusable(value) {
		if (this.nativeElement) {
			this.nativeElement.unfocusable = value;
		}
	}

	/** Sets or gets the value of the element.
	*	Property type: string
	*/
	get value() {
		return this.nativeElement ? this.nativeElement.value : undefined;
	}
	set value(value) {
		if (this.nativeElement) {
			this.nativeElement.value = value;
		}
	}


	// Gets the properties of the React component.
	get properties() {
		return ["disabled","dropDownClassList","dropDownButtonPosition","dropDownHeight","dropDownWidth","name","opened","onlyCountries","placeholder","selectedCountry","rightToLeft","theme","unfocusable","value"];
	}
	/**  This event is triggered when the selection is changed.
	*  @param event. The custom event. 	Custom event was created with: event.detail(	label, 	oldLabel, 	oldValue, 	value)
	*   label - The label of the new selected item.
	*   oldLabel - The label of the item that was previously selected before the event was triggered.
	*   oldValue - The value of the item that was previously selected before the event was triggered.
	*   value - The value of the new selected item.
	*/
	_onChange = null;	get onChange() {
		return this._onChange;
	}
	set onChange(value) {
		this._onChange = value;
	}
	/**  This event is triggered on each key up event of the Input, if the value is changed.
	*  @param event. The custom event. 	Custom event was created with: event.detail(	oldValue, 	value)
	*   oldValue - The previous value before it was changed.
	*   value - The new value.
	*/
	_onChanging = null;	get onChanging() {
		return this._onChanging;
	}
	set onChanging(value) {
		this._onChanging = value;
	}
	/**  This event is triggered when the user clicks on an item from the popup list.
	*  @param event. The custom event. 	Custom event was created with: event.detail(	item, 	label, 	value)
	*   item - The item that was clicked.
	*   label - The label of the item that was clicked.
	*   value - The value of the item that was clicked.
	*/
	_onItemClick = null;	get onItemClick() {
		return this._onItemClick;
	}
	set onItemClick(value) {
		this._onItemClick = value;
	}

	// Gets the events of the React component.
	get eventListeners() {
		return ["onChange","onChanging","onItemClick"];
	}
	/** Closes the drop down. 
	*/
    close(){
        if (this.nativeElement.isRendered) {
            this.nativeElement.close();
        }
        else
        {
            this.nativeElement.whenRendered(() => {
                this.nativeElement.close();
            });
        }
    }

	/** Ensures that the active ( selected ) item is always visible. 
	*/
    ensureVisible(){
        if (this.nativeElement.isRendered) {
            this.nativeElement.ensureVisible();
        }
        else
        {
            this.nativeElement.whenRendered(() => {
                this.nativeElement.ensureVisible();
            });
        }
    }

	/** Opens the drop down. 
	*/
    open(){
        if (this.nativeElement.isRendered) {
            this.nativeElement.open();
        }
        else
        {
            this.nativeElement.whenRendered(() => {
                this.nativeElement.open();
            });
        }
    }

	/** Selects the text inside the input or if it is readonly then the element is focused. 
	*/
    select(){
        if (this.nativeElement.isRendered) {
            this.nativeElement.select();
        }
        else
        {
            this.nativeElement.whenRendered(() => {
                this.nativeElement.select();
            });
        }
    }



	constructor(props) {
		super(props);
		this.componentRef = React.createRef();
	}

	componentDidRender(initialize) {
		const that = this;
		const props = {};
		const events = {};
		let styles = null;
		const stringifyCircularJSON = obj => {
		  const seen = new WeakSet();
		  return JSON.stringify(obj, (k, v) => {
			if (v !== null && typeof v === 'object') {
			  if (seen.has(v)) return;
			  seen.add(v);
			}
			
			if (k === 'Smart') {
				return v;
			}
			
			return v;
		  });
		};
		
		for(let prop in that.props) {
			if (prop === 'children') {
				continue;
			}

			if (prop === 'style') {
				styles = that.props[prop];
				continue;
			}

			if (prop.startsWith('on') && that.properties.indexOf(prop) === -1) {
				events[prop] = that.props[prop];
				continue;
			}

			props[prop] = that.props[prop];
		}

		if (initialize) {
			that.nativeElement = this.componentRef.current;
			that.nativeElement.React = React;
			that.nativeElement.ReactDOM = ReactDOM;
		
			
			if (that.nativeElement && !that.nativeElement.isCompleted) {
				that.nativeElement.reactStateProps = JSON.parse(stringifyCircularJSON(props));
			}
		}

		if (initialize && that.nativeElement && that.nativeElement.isCompleted) {
		//	return;
		}


		for(let prop in props) {
			if (prop === 'class' || prop === 'className') {
				const classNames = props[prop].trim().split(' ');
				
				if (that.nativeElement._classNames) {
					const oldClassNames = that.nativeElement._classNames;
					for(let className in oldClassNames) {
						if (that.nativeElement.classList.contains(oldClassNames[className]) && oldClassNames[className] !== "") {
							that.nativeElement.classList.remove(oldClassNames[className]);
						}
					}
				}
				
				that.nativeElement._classNames = classNames;
				
				for(let className in classNames) {
					if (!that.nativeElement.classList.contains(classNames[className]) && classNames[className] !== "") {
						that.nativeElement.classList.add(classNames[className]);
					}
				}

				continue;
			}

			if (props[prop] !== that.nativeElement[prop]) {
				const normalizeProp = (str) => {
					return str.replace(/-([a-z])/g, function (g) {
						return g[1].toUpperCase();
					});
				}

				if (prop === 'hover' || prop === 'active' || prop === 'focus' || prop === 'selected') {
					that.nativeElement.setAttribute(prop, '');
				}
				
				const normalizedProp = normalizeProp(prop);

				if (that.nativeElement[normalizedProp] === undefined) {
				    that.nativeElement.setAttribute(prop, props[prop]);
				}

				if (props[prop] !== undefined) {
					if (typeof props[prop] === 'object' && that.nativeElement.reactStateProps && !initialize) {
						if (stringifyCircularJSON(props[prop]) === stringifyCircularJSON(that.nativeElement.reactStateProps[normalizedProp])) {
							continue;
						}
					}
					
					that.nativeElement[normalizedProp] = props[prop];
				}
			}
		}

		
		for(let eventName in events) {
			that[eventName] = events[eventName];
			that.nativeElement[eventName.toLowerCase()] = events[eventName];
        }

		if (initialize) {
			Smart.Render();
	
			if (that.onCreate) {
				that.onCreate();
			}

			that.nativeElement.whenRendered(() => {
				if (that.onReady) {
					that.onReady();
				}
			});
		}

		// setup styles.

		if (styles) {
			for(let styleName in styles) {
				that.nativeElement.style[styleName] = styles[styleName];
			}
		}
	}

	componentDidMount() {
		this.componentDidRender(true);
    }

	componentDidUpdate() {
		this.componentDidRender(false);
    }

	componentWillUnmount() {
		const that = this;

		if (!that.nativeElement) {
			return;
		}
		
		that.nativeElement.whenRenderedCallbacks = [];
		
		for(let i = 0; i < that.eventListeners.length; i++){
			const eventName = that.eventListeners[i];

			that.nativeElement.removeEventListener(eventName.substring(2).toLowerCase(), that[eventName]);
		}
	}

	render() {
		return (
			React.createElement("smart-country-input", { ref: this.componentRef, suppressHydrationWarning: true }, this.props.children)
		)
	}
}

export default CountryInput;
