/*

	Copyright (c) 2020, 2023, The Unified Company.

	This code is part of Unify.

	This program is free software; you can redistribute it and/or modify
	it under the terms of the ESA Software Community License - Strong Copyleft LICENSE,
	as published by the ESA.
	See the ESA Software Community License - Strong Copyleft LICENSE, for more details.

	https://unifyjs.org 

*/


import css 						from './css.js';	

import definitions 				from './definitions.js';	

import consoleColors			from './consoleColors.js';



class tools{

	css 				= new css();

	isUnifyObject( propertyName, propertyValue ) {

		const rejectClassNames		= new Array( "core", "cssRules", "stateMachine", "socketManager", "animationManager", "permissionManager", "permissionObject", "fileLoader", "WriteStream", "client");
		
		const rejectPropertyNames	= new Array( "objectToManipulate", "parent", "client", "queryRoot", "queryChildren", "queryCount" );



		if( propertyValue && typeof propertyValue.parseUnify == "boolean" && propertyValue.parseUnify == false ) {

			return false;

		}

		if( propertyValue == null ) {

			return false;

		}

		if( !propertyValue ) {

			return false;

		}

		var className	= this.getClassName( propertyValue );

		if( rejectPropertyNames.includes( propertyName ) ) {

			return false;

		}

		if( rejectClassNames.includes( className ) ) {

			return false;

		}

		if( Array.isArray( propertyValue ) ) {

			return false;

		}

		if( this.getEnvironment() == "Browser" ) { 

			if( propertyValue instanceof HTMLElement ) {

				return false;

			}

		}

		if( typeof propertyValue == "object" ){ 

			return true;

		} else {

			return false;
			
		}
	
	}

	htmlEntities( str ) {

		var escapeChars = {
			'¢' : 'cent',
			'£' : 'pound',
			'¥' : 'yen',
			'€': 'euro',
			'©' :'copy',
			'®' : 'reg',
			'<' : 'lt',
			'>' : 'gt',
			'"' : 'quot',
			'&' : 'amp',
			'\'' : '#39'
		};

		var regexString = '[';

		for(var key in escapeChars) {

			regexString += key;

		}

		regexString += ']';

		var regex = new RegExp( regexString, 'g');

		return str.replace(regex, function( m ) {

			return '&' + escapeChars[ m ] + ';';

		});

	}

	reverseString( string ) {

		var splitString 	= string.split(""); 

		var reverseArray 	= splitString.reverse(); 

		var joinArray 		= reverseArray.join("");

		return joinArray;

	}



	slash( path ) {

		const isExtendedLengthPath = /^\\\\\?\\/.test(path);

		if ( isExtendedLengthPath ) {

			return path;

		}

		return path.replace(/\\/g, '/');

	}

	serializeString( json ) {

		if( Buffer.isBuffer( json ) ) {

			return json.toString();

		} else {

			return json;
		}

	}

	isJsonString( str ) {

		try {

			JSON.parse( str );

		} catch (e) {

			return false;

		}

		return true;

	}

	serializeJSON( json ) {

		if( this.isJsonString( json ) ) {

			return JSON.parse( json );

		}

	}

	createOrderedArray( array ) {

		var keys 		= Object.keys( array );

		var output		= new Array();

		for ( var i = 0; i < keys.length; i++ ) {

			var key			= keys[i];

			output[ i ]		= array[ key ];

		}

		return output;

	}

	getEnvironment() {

		if( typeof document != "undefined" ) {

			return "Browser";

		}

		if( global ) {

			return "Node"

		}

	}

	// Security
	getObjectByPath( application, applicationPath ) {
		
		var objectName = applicationPath.pop();

		//console.log("getObjectByPath", objectName);

		if( !application[ objectName ] || typeof objectName != "string" ) {
		
			throw new Error('Get object by Path Failed, Faulty path');
	
		}

		var currentApplication = application[ objectName ];

		if( !currentApplication ) {

			throw new Error( 'Get object by Path Failed, Faulty path' );

		}

		if( applicationPath.length > 0 ) {
		
			return this.getObjectByPath( currentApplication, applicationPath );
			
		} else {
			
			return currentApplication;
			
		}

	}

	isNumber( value ) {

		return ( typeof value === 'number' && isFinite( value ) );

	}

	isNumberObject( n ) {

		return ( Object.prototype.toString.apply( n ) === '[object Number]' );

	}

	isCustomNumber( n ) {

		return this.isNumber( n ) || this.isNumberObject( n );

	}

	structuredClone( object ) {

		var prototype		= Object.getPrototypeOf( object );

		var create			= Object.create( prototype );
		  
		return Object.assign( create , object );

	}

	extendedClassIsColumn() {


	}

	isUnifyColumn( object ) {

		var extendedClasses = this.getExtendedClassesMulti( object );

		var isColumn 	= false;

		for ( var j = 0; j < extendedClasses.length; j++ ) {

			var extendedClass	= extendedClasses[j]

			var className 		= this.getClassName( extendedClass );

			if( className == "column" ) {

				isColumn = object;

			}

		}

		return isColumn;

	}

	filterPrims( item, prims, type ) {

		if( prims[ type ].hasOwnProperty( item ) ) {

			return false;

		} else {

			prims[ type ][ item ] = true;

			return true;

		}

	}

	filterObjects( item ) {

		var filterObjects	= this.filterObjects;

		if( filterObjects.indexOf( item ) >= 0 ) {

			return false;

		} else {

			filterObjects.push( item );

			return filterObjects;

		}

	}

	uniqueFilter( item ) {

		var type 			= typeof item;

		var prims			= this.prims;

		if( type in prims ) {

			return this.filterPrims( item, prims, type );

		} else {

			return this.filterObjects( item );

		}

	}

	uniqueArrayRows( a ) {

		this.prims 		= new Object();

		this.prims.boolean	= new Object();

		this.prims.number	= new Object();

		this.prims.string	= new Object();


		this.filterObjects 	= new Array();


		return a.filter( this.uniqueFilter.bind( this ) );

	}

	simpleClone( object ) {

		var prototype	= Object.getPrototypeOf( object );

		var created		= Object.create( prototype );

		let clone 		= Object.assign( created, object );

		return clone;
		
	}

	getPrototypeRecursive( object, properties = new Array() ) {

		properties.push( ...Object.getOwnPropertyNames( object ) )

		var prototype = Object.getPrototypeOf( object )

		if( prototype ) {

			return this.getPrototypeRecursive( prototype, properties );

		} else {

			return properties;

		}

	}

	filterFunctions( element, index, arr ) { 

		var nextElement = arr[ index + 1];

		var elementType	= typeof this[ element ];

		if ( element != nextElement && elementType == "function" ){

			return true;

		} 

	}

	getAllFuncs( object ) {

		var properties	= this.getPrototypeRecursive( object );

		var sorted		= properties.sort();

		var filterd		= sorted.filter( this.filterFunctions.bind( object ) )

		return filterd;

	}

	createRandomKey( numCharacters ) {

		var token = "";

		for ( var i = 0; i < numCharacters; i++ ) {
		
			token 	+= 	Math.floor( Math.random() * 10 );

		}

		return token;

	}

	isCSSProperty( propertyName ) {

		var object =  definitions.css;

		if( typeof  object[propertyName] !== 'undefined' ) {

			return true;

		} else {

			return false;

		}

	}

	hasUserPrototype( obj ) {

	    return obj.constructor !== Object;

	}

	addTabToArrayLines( lines, numSpaces ) {

		var tabbedRows 		= new Array();

		for ( var i = 0; i < lines.length; i++ ) {

			var row = lines[i];

			tabbedRows[i] = "".padEnd( numSpaces ) + row;

		}

		return tabbedRows;

	}

	addTabToBeginOfString( string, numSpaces ) {

		var lines 			= string.split("\n");

		var tabbedRows		= this.addTabToArrayLines( lines, numSpaces );

		return tabbedRows.join("\n");

	}

	addTabToBegin( string, numSpaces ) {

		if( typeof string == "string" ) {

			return this.addTabToBeginOfString( string, numSpaces );

		} else {

			return string;
		}

	}

	getChildFromEntry( parent, entry, children ) {

		const invalid	= new Array( "parent", "table", "user" );

		var name		= this.getClassNameByEntry( entry );

		var child		= this.getObjectByEntry( entry );


		if( !this.isUnifyObject( name, child ) ) {

			return false;

		}

		if( invalid.includes( name ) ) {

			return false;

		}
		
		child.propertyName 	= name;

		child.parent 		= parent;

		children.push( child );

	}

	getChildren( object, bindParent = true ) {
		
		var children	= new Array();

		var entries 	= Object.entries( object );
		
		for( var c = 0; c < entries.length; c++ ){
			
			var entry 		= entries[ c ];

			this.getChildFromEntry( object, entry, children );
			
		}

		return children;

	}
	
	addPropertyToArray( property, properties ) {

		const invalid		= new Array( "user" );

		if( this.isUnifyObject( property.name, property.value ) ) {

			return false;

		}

		if( invalid.includes( property.name ) ) {

			return false;

		}

		properties.push( property );

	}

	createPropertyFromEntry( entry ) {

		var name	= this.getClassNameByEntry( entry );

		var object	= this.getObjectByEntry( entry );


		var property 		= new Object();
		
		property.name 		= name;

		property.value		= object;

		return property;

	}

	getProperties( object ) {
	
		var properties		= new Array();

		var entries 		= Object.entries( object );

		for( var c = 0; c < entries.length; c++ ){
			
			var entry 		= entries[c];

			var property	= this.createPropertyFromEntry( entry ) ;


			this.addPropertyToArray( property, properties )

		}
		
		return properties;
		
	}

	replaceChildWithFragment( source, newType ) {

		const frag = document.createDocumentFragment(); 

		while ( source.firstChild ) { 
		
			frag.appendChild( source.firstChild ); 
			
		} 

		const newElem = document.createElement( newType ); 

		newElem.appendChild( frag ); 

		source.parentNode.replaceChild( newElem, source ); 

	}

	replaceElement( source, newType, parentElement ) { 
	
		if( parentElement ) {
		
			source.parentNode = parentElement;
		
		}
	
		if( source.tagName != newType.toUpperCase() ) {
	
			this.replaceChildWithFragment( source, newType );

		}
		
	} 

	parseObject( object ) {
	
		var entries = Object.entries( object );
		
		return entries[0];
	
	}
	
	getFirstEntry( object ) {
	
		var entries = Object.entries( object );

		if( entries[0] ) {

			return entries[0];

		} else {

			return false;

		}
		
	}

	getExtendedClass( object ) {
	
		if( !object.prototype ) {
		
			object = object.constructor;
			
		}
		
		return object.prototype.__proto__;
		
	}

	removeDuplicates( arr ) {

        return arr.filter( ( item, index ) => arr.indexOf( item ) === index );
    
    }

    removeEmptyRows( arr ) {

    	return arr.filter( n => n );

    }

    isVisible( element ) {

		if( !element ) {

			return false;

		}

		if( !element.offsetWidth ) {

			return false;

		}

		return  element.offsetWidth > 0 || element.offsetHeight > 0;

    }

	getExtendedObjects( object ) {

		if( !object.getClassName ) {

			return false;

		}

		if( typeof document == "undefined" ){

			var extendedObjects 	= global.extendMap[ object.getClassName() ];

		} else {

			var extendedObjects 	= document.extendMap[ object.getClassName() ];

		}

		return extendedObjects;

    }

	getSingleExtendedClassInArray( object ) {

		if( !object.prototype ) {
		
			object 			= object.constructor;
			
		}

		var extendedClass 	= this.getExtendedClass( object );

		if( !extendedClass ) {

			return false;

		} else {
		
			return new Array( new extendedClass.constructor() );

		}

    }

	getExtendedClassesMulti( object ) {

		var extendedObjects 	= this.getExtendedObjects( object );

		if( extendedObjects ) {

			return extendedObjects;

		} else {

			return this.getSingleExtendedClassInArray( object );

		}
		
	}

	getExtendedClassName( object ) {
	
		return this.getClassName( this.getExtendedClass( object ) );
	
	}
	
	logError( message ) {

		console.log( this.consoleColors().red( message ) );

	}

	debugVar( object, variable ) {

		var colors 				= this.consoleColors();

		var path 				= this.getApplicationPath(object);

		var pathColor 			= colors.red( "(" + path.toString()  + ")"  );
		
		var variableColor 		= colors.magenta( variable );
	
		console.log( variableColor + pathColor );	
		
	}
	
	logParameter2( parameter2 ) {

		if( typeof parameter2 == "string" ) {

			console.log( parameter2 );

		} else {

			this.logObject( parameter2 ); 

		}

	}

	log( parameter1, parameter2, parameter3 ) {
	
		var colors = this.consoleColors();
	
		if( typeof parameter1 == "string" ) {

			console.log( colors.yellow("\n\n" + parameter1) );

		} else {

			this.logObject( parameter1 );

		}

		if( parameter2 ) {

			this.logParameter2( parameter2 );
				
		}

	}

	log( ...value ) {

		for (var i = 0; i < value.length; i++) {
			
			console.log( util.inspect( value[i], { showHidden: false, depth: 1, colors: true  }) )

		}

	}

	
	logObject( object ) {
	
		var colors 			= this.consoleColors();

		var path 			= this.getApplicationPath(object);
		
		var pathColor 		= colors.red( "(" + path.reverse().toString()  + ")"  );
		

		console.log( pathColor  + " " + this.getClassName( object ) );
		
		console.log( this.serialize( object ) );

	}

	saveImportDefault( importObject ) {

		if( importObject.default ) {
		
			return importObject.default;
			
		} else {
	
			console.log("error2 ", url, " does not exist");
	
		}

	}
	
	async saveImport( url ) {
	
		var importObject 	= await import( url );
		
		if( importObject ) {
			
			return this.saveImportDefault( importObject );
		
		} else {
		
			console.log("error1 ", url, " does not exist");
		
		}
		
	}
	
	getApplicationDepth( object ) {
		
		return this.getApplicationPath( object ).length;
	
	}

	getParentApplicationPath( object, array ) {

		array.push( object.propertyName );
	
		return this.getApplicationPath( object.parent, array );

	}
	
	getApplicationPath( object, array = new Array() ) {
	
		if( object.parent ) {
		
			return this.getParentApplicationPath( object, array )

		} else {
		
			return array;
		
		}
	
	}
	
	getClassName( object ) {
	
		if( object.__className ) {

			return object.__className;

		}

		if( !object ) {
		
			return false;
		
		}
		
		return object.constructor.name;

		//var rollupFix			= object.constructor.name.split("$")[0].split("$")[0];

		//var webPackFix		= rollupFix.split("_")

		//return rollupFix;

		//return webPackFix[webPackFix.length-1];

	}
	
	getID( object ){
		
		if( object.id ){

			return object.id;

		} else {

			return "";

		}

	}
	
	getClassNameByObject( object ) {

		if( typeof object == "string" ) {
		
			return object;
		
		}

		var entry		= this.parseObject( object );

		var className 	= this.getClassNameByEntry( entry );
		
		return className;
		
	}
	
	getClassNameByEntry( entry ) {
	
		return entry[0];
		
	}
	
	getObjectByEntry( entry ) {
	
		if( entry[1] ) {

			return entry[1];

		} else {

			return false;

		}
	
	}

	logObject( object, message ) {

		if( object.debug ) {

			console.log( message, object.getClassName(), "\n" );

		}

	}

	
	getFirstEntryName( object ) {
	
		var entries = Object.entries( object );
	
		return entries[0][0];
	
	}

	delay( millisec ) {

	    return new Promise(resolve => {
	        setTimeout(() => { resolve('') }, millisec);
	    })
	    
	}

	
	getPropertyNameWithID( object ) {

		if( object.id ) {

			// bug //          + "_" + object.id; todo: Make that only id is added when css content differs from other rows/ids
			// object.propertyName.replace( object.id, "" ) is, Unify objects rendered by rendercollections have an id in the propertyName
			// This results in double ids

			return this.CamelCase( object.propertyName ) + "_" + object.id;

		} else {

			return this.CamelCase( object.propertyName );

		}

		if( !propertyName ) {
		
			return "";	
			
		}
		
	}

	createCSSClassName( object ) {
		
		var propertyName	= this.getPropertyNameWithID( object );

		if( object.parent ) {
		
			object = object.parent;
			
		}
		
		return  this.getClassName( object ) + propertyName;

	}

	CamelCase( string ){
	
		if( !string ) {

			return false;

		}

		string 		= string.toUpperCase();
		
		string 		= string[0].toUpperCase() + string.slice( 1, string.length ).toLowerCase();
		
		return string;

	}
	
	firstLowerCase( string ){

		if( string ) {
		
			return  string[0].toLowerCase() + string.slice( 1, string.length );
		
		} else {
		
			return false;
		
		}

	}

	cleanRollup( term ) {

		return term;

		// Deprecated
		//return term.replaceAll("$0", "").replaceAll("$1", "").replaceAll("$2", "");

	}

	cleanEntry( entry ) {

		const invalid		= new Array( "parent", "table", "user", "objectToManipulate" );

		var name			= this.getClassNameByEntry( entry );

		var object			= this.getObjectByEntry( entry );

		if( invalid.includes( name ) ) {

			delete object[name];

		}

	}

	cleanObject( object ) {

		var entries 	= Object.entries( this );

		for ( var i = 0; i < entries.length; i++ ) {

			var entry 	= entries[i];

			this.cleanEntry( entry );

		}

	}
	
	objectExtendsTablePrimitive( object ) {

		var extendedClass = this.getExtendedClass( object );

		if( !extendedClass ) {
		
			return false;
		
		}

		var className = this.getClassName( extendedClass );
		
		if( className == "table" ) {
		
			return true;
			
		} else {
		
			return false;
		
		}

	}

	objectIsTable( object ) {
	
		if( !object ) {
		
			return false;
		
		}
	
		if( !object.prototype ) {

			object = object.constructor;
			
		}
		
		return this.objectExtendsTablePrimitive( object );
		
	}

	extendIsRenderCollection( object ) {

		var extendedClass 	= this.getExtendedClass( object );
		
		var className 		= this.getClassName( extendedClass );
		
		if( className == "renderCollection" ) {
		
			return true;
			
		} else {
		
			return false;
		
		}

	}
	
	objectIsRenderCollection( object ) {
	
		var className = this.getClassName( object );
		
		if( className == "renderCollection" ) {

			return true;
			
		}
			
		if( !object.prototype ) {
		
			object = object.constructor;
			
		}
		
		return this.extendIsRenderCollection( object );
		
	}
	
	getTableName( object ) {
	
		if( !this.objectIsTable( object ) ) {
		
			object = this.getTableFromObject( object );

		}
		
		var className = this.getClassName( object );
		
		return className;

	}

	isTableInHierarchy( object ) {


	}
	
	getParentWithTable( object, tableName ) {
		
		if( this.getClassName( this.getTableFromObject( object ) ) == tableName ) {
			
			return object;
		
		} else {
		
			return this.getParentWithTable( object.parent, tableName );
		
		}
	
	}

	classNameIsTable( className ) {

		// todo deprecated?
		//if( className == "table" || className == "unify_table_table" || className == "server_table_table" || className == "table_table" ) {

		if( className == "table" ){

			return true;
		
		} else {

			return false;

		}

	}

	evaluateExtendedClass( object, extendedClass ) {

		var className 		= this.getClassName( extendedClass );

		if( this.classNameIsTable( className ) ) {

			return object;
			
		}
		
		var table =  this.getTableFromObject( extendedClass );
	
		if( table ) {

			return table;

		} else {

			return false;

		}

	}

	getTableFromExtendedClasses( object, extendedClasses ) {

		for ( var i = 0; i < extendedClasses.length; i++ ) {

			var extendedClass 	= extendedClasses[i];

			var table 			= this.evaluateExtendedClass( object, extendedClass );

			if( table ) {

				return table;

			}

		}

	}
	
	getTableFromObject( object ) {

		if( object.table ) {

			return object.table;

		}

		var className = this.getClassName( object );

		if( className ) {

			var extendedClasses 	= this.getExtendedClassesMulti( object );

			return this.getTableFromExtendedClasses( object, extendedClasses );

		} else {

			console.log("classname not found",object);

		}
		
	}

	isServerValue( name, value ){

		const invalid 		= new Array( "style", "type", "label", "flexDirection", "boxPadding", "width", "DOM_ELEMENT" );

		const valid  		= new Array( "rows" );
		
		if( valid.includes( name ) ){
		
			return true;
		
		}
		
		if( invalid.includes( name ) ){
		
			return false;
		
		}
			
		if( this.css.propertyIsStyle( name ) ) {
		
			return false;
		
		}
		
		if( typeof value == "number" ){
		
			return true;
		
		}
		
		if( typeof value == "string" ){
		
			return true;
			
		} else {
		
			return false;
		
		}

	}

	deleteNormalBrowserEvent( object, browserEvent ) {

		if( object[ browserEvent ] ) {

			delete object[ browserEvent ];

		}

	}

	deleteServerBrowserEvent( object, browserEvent ) {

		if( object[ "server" + this.CamelCase( browserEvent ) ] ) {

			delete object[ "server" + this.CamelCase( browserEvent ) ];

		}

	}


	groomBrowserEvents( object ) {

		var browserEvents = definitions.browserEvents;

		for ( var i = 0; i < browserEvents.length; i++ ) {

			var browserEvent 	= browserEvents[ i ];
			
			this.deleteNormalBrowserEvent( object, browserEvent );

			this.deleteServerBrowserEvent( object, browserEvent );

		}

	}

	getCircularReplacer( key, value ) {

		if ( typeof value === "object" && value !== null ) {

			if ( this.seen.has( value ) ) {

				return;

			}

			this.seen.add( value );

		}

		return value;

	}

	sizeOf( object ) {

		this.seen 			= new WeakSet();

		var stringObject	= JSON.stringify( object, this.getCircularReplacer.bind( this ) );
		// OR  stringObject	= JSON.stringify( object, ( key, value ) => this.getCircularReplacer( key, value ) );
		
		var finalObject 	= JSON.parse( stringObject );

		return global.objectSizeof( finalObject );

	}

	groomCssProperties( object, propertyName ) {

		var propertyNameWithoutBox	= propertyName.replace("box", "");

		if( this.css.propertyHasStyle( propertyNameWithoutBox ) ) {

			delete object[ propertyName ];

		}

	}

	groomRemainingProperties( object, propertyName, propertyValue ) {

		//if( !this.isServerValue( propertyName, propertyValue ) && !this.isUnifyObject(propertyName, propertyValue )  ) {
		
		//		delete object[ propertyName ];
		
		//}	

	}

	groomPermissionMethods( object ) {

		if( object.enableWRITE ) {

			delete object.enableWRITE;

		}

		if( object.disableWRITE ) {

			delete object.disableWRITE;

		}

		if( object.enableREAD ) {

			delete object.enableREAD;

		}

		if( object.disableREAD ) {

			delete object.disableREAD;

		}

		if( object.enableDELETE ) {

			delete object.enableDELETE;

		}

		if( object.disableDELETE ) {

			delete object.disableDELETE;

		}

	}

	groomBrowserMethods( object ) {

		if( object.create ) {

			delete object.create;

		}

		if( object.update ) {

			delete object.update;

		}

		if( object.afterLoad ) {

			delete object.afterLoad;

		}

	}

	groomEntry( entry, object ) {

		var propertyName  	= this.getClassNameByEntry( entry );
		
		var propertyValue 	= this.getObjectByEntry( entry );


		this.groomPermissionMethods( object );
		
		this.groomBrowserMethods( object );

		this.groomBrowserEvents( object );

		this.groomCssProperties( object, propertyName );

		this.groomRemainingProperties( object, propertyName, propertyValue );


		if( this.isUnifyObject(propertyName, propertyValue ) ) {
		
			this.groomApplicationObject( propertyValue );
		
		}

		return object;

	}

	groomApplicationObject( object ) {
	
		var entries 		= Object.entries( object );

		for( var c = 0; c < entries.length; c++ ) {
		
			var entry			= entries[c];

			this.groomEntry( entry, object );

		}
		
		return object;
	
	}

	serializeEntry( entry, serializedObject ) {

		var propertyName  	= this.getClassNameByEntry( entry );

		var propertyValue 	= this.getObjectByEntry( entry );

		this.serializeProperty( propertyName, propertyValue, serializedObject );

	}

	serializeEntries( entries ) {

		var serializedObject 	= new Object();

		for( var c = 0; c < entries.length; c++ ) {
		
			var entry			= entries[c];

			this.serializeEntry( entry, serializedObject );

		}

		return serializedObject;

	}

	serialize( object, is_root = true, cut ){
	
		var entries 			= Object.entries( object );

		this.serializeEntries( entries );

		return this.createOuterObject( object, serializedObject, is_root );

	}

	serializeServerProperty( object, propertyName, propertyValue ) {

		object[ propertyName ] = propertyValue;

	}

	serializedUnifyObject( object, propertyName, propertyValue ) {

		object[ propertyName ] = this.serialize( propertyValue, false );

	}
	
	serializeProperty( propertyName, propertyValue, object ) {
		
		if( this.isServerValue( propertyName, propertyValue ) ) {
			
			this.serializeServerProperty( object, propertyName, propertyValue );
			
		}
		
		if( this.isUnifyObject(propertyName, propertyValue ) ) {
		
			this.serializedUnifyObject( object, propertyName, propertyValue );
			
		}
		
		return object;
	
	}
	
	isArray( array ) {
	
		if( !array ) {
		
			return false;
		
		}
	
		return Array.isArray( array );
	
	}
		
	validateNumber( value ) {
	
		if( typeof value == "number" ) {
		
			return value;
		
		} else {
		
			return false;
		
		}
	
	}
	
	validateString( value ) {
	
		if( typeof value == "string" ) {
		
			return value;
		
		} else {
		
			return false;
		
		}
		
	}

	validateValue( value ) {
	
		if( typeof value == "string" || typeof value == "number" || typeof value == "boolean" ) {
		
			return value;
		
		} else {
		
			return false;
		
		}
	
	}

	validateBoolean( value ) {
	
		if( typeof value == "boolean" ) {
		
			return value;
		
		} else {
		
			return false;
		
		}
	
	}
	
	validateArray( values ) {
	
		if( !this.isArray( values ) ) {

			return false;

		}

		for( var c = 0; c < values.length; c++ ) {
		
			values[ c ] = this.validateString( values[ c ] );

		}
	
		return values;
		
	}
	

	is_allowed_value( value ) {

		if( this.is_int( value ) ) {

			return true;

		}

		if( this.is_text( value ) ) {

			return true;

		}

		if( this.is_boolean( value ) ) {

			return true;

		}

		if( this.is_object( value ) ) {

			return true;

		}	

		return false;

	}

	is_float( value ) {

		if( typeof value == "number" ) {

			return true;

		} else {

			return false;

		}

	}

	is_number( value ) {

		if( typeof value == "number" ) {

			return true;

		} else {

			return false;

		}

	}

	is_object( value ) {

		if( typeof value == "object" ) {

			return true;

		} else {

			return false;

		}

	}

	is_text( value ) {

		if( typeof value == "string" ) {

			return true;

		} else {

			return false;

		}

	}

	is_string( value ) {

		if( typeof value == "string" ) {

			return true;

		} else {

			return false;

		}

	}

	is_boolean() {

		if( typeof value == "boolean" ) {

			return true;

		} else {

			return false;

		}

	}

	is_int( value ) {

		if( typeof value == "number" ) {

			return true;

		} else {

			return false;

		}

	}

	createOuterObjectWithRoot() {

		var objectName 			= this.getClassName( object );
	
		var output 				= new Object();
		
		output[ objectName ] 	= serializedObject;
		
		return output;

	}
	
	createOuterObject( object, serializedObject, is_root ) {
	
		if( is_root ) {
	
			return this.createOuterObjectWithRoot( object, serializedObject );
			
		} else {
		
			return serializedObject;
		
		}

	}

	consoleColors() {

		return consoleColors;

	}

	getFirstObjectFromEntries( entries ) {

		var firstChild 	= entries[0][1];
		
		if( firstChild ) {
		
			return firstChild;
			
		} else {
		
			return object;
		
		}

	}

	getFirstChild( object ) {
	
		var entries	= Object.entries( object );
		
		if( entries[0] ) {
		
			return this.getFirstObjectFromEntries( entries ); 
		
		}
	
	}

}

String.prototype.replaceAll = function replaceAll( search, replace ) { return this.split( search ).join( replace ); }


export default new tools();


