/*

	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 GNU AFFERO GENERAL PUBLIC LICENSE,
	as published by the Free Software Foundation.
	See the GNU AFFERO GENERAL PUBLIC LICENSE, for more details.

	https://unifyjs.org 

*/

import cssRules			from './cssRules.js';

import document			from './document.js';

import cssManager		from './cssManager.js';

import tools	 		from './tools.js';

import definitions		from './definitions.js';


export default class css{

	cssCache 		= new cssManager();

	global_css 		= "";

	skipBoxTerms	= new Array( "box-sizing", "box-shadow", "box-reflect", "box-decoration-break" );

	constructor() {

		this.boxTermWithoutPrefix	= new Array();

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

			var boxTerm 					= this.skipBoxTerms[i]

			this.boxTermWithoutPrefix[i] 	= boxTerm.replace("box-")

		}

	}

	PerformPropertyGridCorrection( property ) {
		
		for ( var i = 0; i < this.skipBoxTerms.length; i++ ) {
			
			if( this.boxTermWithoutPrefix[ i ].includes( property ) ) {

				property = this.skipBoxTerms[ i ];

			}

		}

		return property;

	}

	parseObject( object ){

		var objectName 			= object.propertyName;	

		object.__proto__.css 			= new cssRules();

		object.__proto__.boxRules 		= new cssRules();

		object.__proto__.cssRules		= new cssRules();


		object.cssRules.addStyleListener( "hover", ":hover" );

		object.cssRules.addStyleListener( "visited", ":visited" );

		object.cssRules.addStyleListener( "link", ":link" );

		object.cssRules.addStyleListener( "active", ":active" );


		object.cssRules.addStyleListener( "placeholder", "::placeholder" );

		object.cssRules.addStyleListener( "scrollbar", "::-webkit-scrollbar" );

		object.cssRules.addStyleListener( "scrollbarTrack", "::-webkit-scrollbar-track" );

		object.cssRules.addStyleListener( "scrollbarThumb", "::-webkit-scrollbar-thumb" );

		object.cssRules.addStyleListener( "scrollbarThumbHover", "::-webkit-scrollbar-thumb:hover" );

		
	}
	
	parseCSSValue( value ) {
	
		if( typeof value == "number" ) {
			
			value += "px";
		}

		return value;
		
	}
	
	createStyleRule( normalizedProperty, value ){

		if( !value ) {

			return "";

		}
	
		value = this.parseCSSValue( value );

		normalizedProperty = this.PerformPropertyGridCorrection( normalizedProperty );

		return "\t" + normalizedProperty + ":" + value + ";   \n";
	
	}

	compareUpperCase( term ) {
	
		const compare 	= function( current ){ if( term.indexOf( current ) != -1 ){  return true; } }
	
		const n 		= definitions.alphabet;

		var map 		= n.map( compare );
		
		return map; // [false, false, true, false] where true is where higher case is
		
	}
	
	
	hasUpperCase( a ) {

		let regExp = /[A-Z]/;

		return regExp.test( a );
	
	}
	
	replaceUpperCase( word ) {
	
		var map = this.compareUpperCase( word );
		
		var letter = definitions.alphabet[ map.indexOf( true ) ];
		
		return word.replace( letter, "-" + letter.toLowerCase() );
	
	}

	normalizePropertyName( name ) {

		return tools.firstLowerCase( name.replace("__", "").replace("box", "") );

	}
	
	// needs caching
	normalizeProperty( name ) {

		// webkit -> -webkit
		if( name.slice( 0, 6 ) == "webkit" ) {

			name = "-" + name;

		}

		for (var i = 0; i < 6; i++) {

			if( this.hasUpperCase( name ) ) {
			
				name = this.replaceUpperCase( name ); // backgroundColor -> background-color-and-something

			}

		}

		return name;
		
	}

	setter( object, domStyleObject, propertyName, property, value ) {

		object[ "__" + propertyName ] = value;

		value 				= this.parseCSSValue( value );

		property 			= this.PerformPropertyGridCorrection( property );

		var NSTabNames		= new Array("svg", "circle")

		// todo : doesnt work properly
		//if( NSTabNames.includes( object.element.tagName ) ) {

		//	domStyleObject.setAttributeNS( null, property, value ); 

		//} else {

			domStyleObject.setProperty( property, value ); 

		//}

	}

	createSetter( property, objectProperty, object, domStyleObject ) {
	
		var css = this;
		
		object[ "__" + objectProperty ] = object[ objectProperty ];
		
		object.__defineSetter__( objectProperty, this.setter.bind( this, object, domStyleObject, objectProperty, property ) );

	}

	getter( object, objectProperty, value ) {

		var newValue = object[ "__" + objectProperty ];

		if(typeof newValue == "string") {

			var chars = newValue.split();

			if( chars[chars.length-2] == "p" && chars[chars.length-1] == "x" ) {

				chars = chars.slice(0, -2);

				newValue = chars.join();

			}

		}

		if( tools.isCustomNumber( newValue ) ) {

			return parseFloat( newValue );

		} else {

			return newValue;

		}

	}

	createGetter( property, objectProperty, object, domStyleObject ) {

		object.__defineGetter__( objectProperty, this.getter.bind( this, object, objectProperty ) );

	}
	
	parseProperty( property, object ) {

		var propertyName 		= property.name;

		var propertyValue		= property.value;

		var normalizedProperty 	= this.normalizeProperty( propertyName );

		//if( propertyName == "gridArea" && propertyValue == "none" ) {

		//	return false;

		//}

		var slicedProperty		= propertyName.toLowerCase().slice( 0, 3 );

		if( slicedProperty == "box" ) {
		
			object.boxRules.style += this.parseCssTerm( object, propertyName, propertyValue, object.elements[0], "box" );
		
		} else {

			object.css.style += this.parseCssTerm( object, propertyName, propertyValue, object.element )

		}

		this.parseExtraStyleSuffixes( object, propertyName, propertyValue );

	}

	parseExtraStyleSuffixes( object, propertyName, propertyValue ) {

		var styleTypes			= object.cssRules.styleTypes;

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

			var cssType				= styleTypes[i];

			var propertyTerm		= cssType.propertyTerm;

			var slicedProperty		= propertyName.slice( 0, propertyTerm.length );


			if( slicedProperty == propertyTerm && propertyName.length > slicedProperty.length ) {

				cssType.css += this.parseCssTerm( object, propertyName, propertyValue, object.elements[0], propertyTerm );

			}

		}

	}
	
	parseCssTerm( object, propertyName, propertyValue, element, term = "" ) {

		var cssPropertyName 			= tools.firstLowerCase( propertyName.replace( term, "" ) );

		var normalizedProperty 			= this.normalizeProperty( cssPropertyName ); 

		if( this.propertyIsStyle( normalizedProperty ) ) {

			// Setters and getters don't work yet for special suffixes.
			if( !term || term == "box" ) {

				this.createSetter( normalizedProperty, propertyName, object, element.style );

				this.createGetter( normalizedProperty, propertyName, object, element.style );

			}
			
			return this.createStyleRule( normalizedProperty, propertyValue );

		} else {
		
			return "";
		
		}

	}

	clearCache() {

		this.cssCache.rules = new Array();

	}
	
	parseCssRule( object, objectPropertyName, propertyName, propertyValue ) {
	
		var normalizedProperty = this.normalizeProperty( objectPropertyName );

		object.boxRules.style += this.createStyleRule( normalizedProperty, propertyValue );


		this.createSetter( objectPropertyName, propertyName, object, object.elements[0].style );
		
		this.createGetter( objectPropertyName, propertyName, object, object.elements[0].style );

	}

	createSelector( className, afterTerm ) {

		var selector 	= "." + className;

		selector		+= "." + this.device;

		selector		+= "." + this.os;

		selector		+= "." + this.tint;

		// this doesnt work yet.
		if( afterTerm ) {

			selector		+= afterTerm;

		}

		//selector		+= "," + selector.replace( "_" + id, "" );

		return selector;

	}
	
	createStyleSheet( object ){

		var objectName 	= tools.createCSSClassName( object );

		var body		= object.css.style;


		if( object.customElement ) {
		
			var selector 	= this.createSelector( objectName + "Element" );

			this.global_css += this.cssCache.getRule( selector,  body );


			var selector 	= this.createSelector( objectName + "Grid" );

			this.global_css += this.cssCache.getRule( selector,  body );

		} else {
		
			var selector 	= this.createSelector( objectName + "Grid" );
			
			this.global_css += this.cssCache.getRule( selector,  body );

		}
		
		if( object.boxRules ) {

			var selector 	= this.createSelector( objectName + "Box" );

			var body		= object.boxRules.style;

			this.global_css += this.cssCache.getRule( selector, body );

		}



		var styleTypes			= object.cssRules.styleTypes;

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

			var cssType		= styleTypes[i];

			if( object.useCustomElement ) {

				var selector 	= this.createSelector( objectName + "Element", cssType.cssSuffix );

			} else {

				var selector 	= this.createSelector( objectName + "Grid", cssType.cssSuffix );

			}

			var body		= cssType.css;

			if( body != "" ) {

				//console.log("cssType.cssSuffix", cssType.cssSuffix);

				this.global_css += this.cssCache.getRule( selector, body );

			}

		}



		if( !object.parent ) {

			this.writeCssToPage( this.global_css );
			
			this.global_css = "";

		}
		
		if( object.dynamic ) {
		
			this.writeCssToPage( this.global_css );
			
			this.global_css = "";
		
		}
		
	}
	
	writeCssToPage( css ) {
	
		if( css == "" ) {
		
			return false;
		
		}

		var style = document.createElement("style");
		
		style.innerHTML = css;
		
		document.head.appendChild( style );
		
	}
	
	removeStripe( name ) {
	
		var splitName = name.split("-");
		
		if( splitName.length > 1 ) {
		
			return splitName[1].toLowerCase();
		
		} else {
		
			return name;
		
		}
	
	}
	
	getStyleSheetObject(){

		if( !document.body ) {

			return definitions.css;
/*
			var array =  JSON.parse(cssDef);

			var object = new Object();

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

				var def = array[i];

				if( this.hasUpperCase( def ) ) {

					object[ this.replaceUpperCase( def ) ] = '';

					console.log( this.replaceUpperCase( def ) );

				} else {

					object[  def  ] = '';

				}
			}
*/
			return object;

		}
		
		if( document.body.style ) {

			var bodyStyle = document.body.style;

			bodyStyle.webkitBoxShadow = "";

			return bodyStyle;

		}

		if( document.body.styleSheets ) {

			// fix please!!!! not allowed
			return document.styleSheets[1].cssRules[0].style;

		} else {
		
			return document.customStyleTerms;
			
		}

	}
	
	camelCaseToDash( name ) {

		for ( var i = 0; i < 4; i++ ) {

			if( this.hasUpperCase( name ) ) {
			
				name = this.replaceUpperCase( name ); // backgroundColor -> background-color
			
			}

		}

		return name;

	}

	removeBoxPrefix( name ) {

		if( !name.includes("webkit-box") ) {

			name = name.replace("box", "").toLowerCase();

		}

		return name;

	}

	checkBoxSpecialCase( name ) {

		for (var i = 0; i < this.boxTermWithoutPrefix.length; i++) {
			
			if( this.boxTermWithoutPrefix[ i ].includes( name ) ) {

				return true;

			}

		}

	}

	propertyIsStyle( name ){

		name = this.camelCaseToDash( name );

		name = this.removeBoxPrefix( name );

		

		if( this.checkBoxSpecialCase( name ) ) {

			return true;

		}


		var styleTerms = this.getStyleSheetObject();

		if( typeof styleTerms[ name ] == "string"  ) {

			return true;
		
		} else {

			return false;
		
		}

	}

	propertyHasStyle( name ){
		
		var styleTerms = Object.keys( definitions.css );

		if( styleTerms.includes( name.toLowerCase() ) ) {

			return true;

		} else {

			return false;

		}

	}
	
}

