import { ApplicationRef, Injectable, NgZone } from '@angular/core';
import { AbstractControl, FormArray, FormControl, FormGroup } from '@angular/forms';
import { isNumeric } from 'rxjs/internal/util/isNumeric';
import { Rule } from '../../../../core/model/dataset-profile-definition/rule';
import { VisibilityRule } from './models/visibility-rule';
import { VisibilityRulesContext } from './models/visibility-rules-context';

@Injectable()
export class VisibilityRulesService {


	private visibilityRuleContext: VisibilityRulesContext;
	private form: AbstractControl;
	private elementVisibilityMap = new Map<String, boolean>();

	constructor(
		public applicationReference: ApplicationRef,
		public ngZone: NgZone
	) {

	}

	public checkElementVisibility(id: string): boolean {
		if (this.visibilityRuleContext.rules.filter(item => item.targetControlId === id).length === 0) { return true; }
		return this.elementVisibilityMap.has(id) ? this.elementVisibilityMap.get(id) : false;
	}

	public buildVisibilityRules(item: Array<Rule>, form: AbstractControl) {
		this.visibilityRuleContext = new VisibilityRulesContext();
		this.visibilityRuleContext.buildVisibilityRuleContext(item || []);
		this.form = form;
		this.resetVisibilityRules();
	}

	public updateValueAndVisibility(id: string, value: any) {
		const visibilityRules = this.visibilityRuleContext.rules.filter(item => item.sourceVisibilityRules.filter(source => source.sourceControlId === id).length > 0);
		visibilityRules.forEach(item => this.evaluateVisibility(item, value));
	}

	private evaluateVisibility(visibilityRule: VisibilityRule, value: any) {
		for (let i = 0; i < visibilityRule.sourceVisibilityRules.length; i++) {
			if (value != null && (this.parseValue(value) !== this.parseValue(visibilityRule.sourceVisibilityRules[i].sourceControlValue))) {
				this.elementVisibilityMap.set(visibilityRule.targetControlId, false);
				this.resetControlWithId(this.form, visibilityRule.targetControlId);
				//this.updateValueAndVisibility(visibilityRule.targetControlId, null);
				// this.clearValues(targetPathKey);
				return;
			}
		}
		this.elementVisibilityMap.set(visibilityRule.targetControlId, true);
		//this.updateValueAndVisibility(visibilityRule.targetControlId, null);
	}

	private resetVisibilityRules() {
		this.elementVisibilityMap.clear();
		this.elementVisibilityMap = new Map<String, boolean>();
	}

	parseValue(value: any) {
		if (typeof value === 'string') {
			if (isNumeric(value)) { return value; }
			else if (value === 'true') {
				return true;
			}
			else if (value === 'false') {
				return false;
			}
			else { return this.translate(value); }
		} else { return value; }
	}

	search(path, obj, target) {
		for (const k in obj) {
			if (obj.hasOwnProperty(k)) {
				if (obj[k] === target) {
					return path + '.' + k;
				} else if (typeof obj[k] === 'object') {
					const result = this.search(path + '.' + k, obj[k], target);
					if (result) {
						return result;
					}
				}
			}
		}
		return false;
	}

	scanIfChildsOfCompositeFieldHasVisibleItems(compositeFieldParent: FormGroup): boolean {
		let isVisible = false;
		(<FormArray>(compositeFieldParent.get('fields'))).controls.forEach(element => {
			if (this.checkElementVisibility(element.get('id').value)) {
				isVisible = true;
			}
		});
		return isVisible;
	}

	private translate(item: any) {
		try {
			return JSON.parse(item).value;
		} catch (error) {
			return item;
		}
	}

	private resetControlWithId(formControl: AbstractControl, id: string) {
		if (formControl instanceof FormGroup) {
			if ((formControl as FormGroup).contains('id') && (formControl as FormGroup).contains('value') && (formControl as FormGroup).get('id').value === id) {
				this.resetFieldFormGroup(formControl);
			} if ((formControl as FormGroup).contains('id') && (formControl as FormGroup).contains('fields') && (formControl as FormGroup).get('id').value === id) {
				this.resetCompositeFieldFormGroup(formControl);
			} else {
				Object.keys(formControl.controls).forEach(item => {
					const control = formControl.get(item);
					this.resetControlWithId(control, id);
				});
			}
		} else if (formControl instanceof FormArray) {
			formControl.controls.forEach(item => {
				this.resetControlWithId(item, id);
			});
		}
	}

	private resetFieldFormGroup(formGroup: FormGroup) {
		formGroup.get('value').setValue(formGroup.get('defaultValue').value ? this.parseValue(formGroup.get('defaultValue').value.value) : undefined);
	}

	private resetCompositeFieldFormGroup(formGroup: FormGroup) {
		(formGroup.get('fields') as FormArray).controls.forEach((element: FormGroup) => {
			this.resetFieldFormGroup(element);
		});
		(formGroup.get('multiplicityItems') as FormArray).controls.splice(0);
	}
}
