import { Component, ElementRef, Input, OnInit } from '@angular/core';
import { AbstractControl, FormArray, FormControl, FormGroup } from '@angular/forms';
import { VisibilityRulesService } from '@app/ui/misc/dataset-description-form/visibility-rules/visibility-rules.service';
import { BaseComponent } from '@common/base/base.component';
import { takeUntil } from 'rxjs/operators';

@Component({
	selector: 'app-form-progress-indication',
	templateUrl: './form-progress-indication.component.html',
	styleUrls: ['./form-progress-indication.component.scss']
})
export class FormProgressIndicationComponent extends BaseComponent implements OnInit {
	@Input() formGroup: FormGroup;
	@Input() isDmpEditor: boolean;
	@Input() isDatasetEditor: boolean;
	@Input() public progressValueAccuracy = 2;
	determinateProgressValue: number;
	progressSoFar: number;
	total: number;
	percent: number;

	constructor(private visibilityRulesService: VisibilityRulesService) { super(); }

	public value = 0;
	ngOnInit() {
		setTimeout(() => {this.calculateValueForProgressbar();});
		this.formGroup
			.valueChanges
			.pipe(takeUntil(this._destroyed))
			.subscribe(control => {
				setTimeout(() => {this.calculateValueForProgressbar();});
			});
	}

	calculateValueForProgressbar() {
		if (this.isDmpEditor) {
			this.progressSoFar = this.countFormControlsValidForProgress(this.formGroup);
			this.total = this.countFormControlsRequiredFieldsForTotal(this.formGroup);
		} else if (this.isDatasetEditor) {
			this.progressSoFar = this.countFormControlsValidForProgress(this.formGroup) + this.countFormControlsWithValueForProgress(this.formGroup);
			this.total = this.countFormControlsRequiredFieldsForTotal(this.formGroup) + this.CountFormControlDepthLengthFotTotal(this.formGroup);
		} else {
			this.progressSoFar = this.countFormControlsWithValueForProgress(this.formGroup);
			this.total = this.CountFormControlDepthLengthFotTotal(this.formGroup);
		}
		this.percent = (this.progressSoFar / this.total) * 100;
		this.value = Number.parseFloat(this.percent.toPrecision(this.progressValueAccuracy));
	}

	countFormControlsWithValueForProgress(formControl: AbstractControl): number {
		let valueCurent = 0;
		if (formControl instanceof FormGroup) {
			if (this.checkFormsIfIsFieldsAndVisible(formControl) && this.checkIfIsRequired((formControl as FormGroup))) {
				if (this.haseValue(formControl))
					valueCurent++;
			}
			if (this.chechFieldIfIsFieldSetAndVisible((formControl as FormGroup)) && this.checkIfIsRequired((formControl as FormGroup))) {
				valueCurent = valueCurent + this.compositeFieldsGetChildsForProgress(formControl);
			} else {
				Object.keys(formControl.controls).forEach(item => {
					const control = formControl.get(item);
					valueCurent = valueCurent + this.countFormControlsWithValueForProgress(control);
				});
			}
		} else if (formControl instanceof FormArray) {
			formControl.controls.forEach(item => {
				valueCurent = valueCurent + this.countFormControlsWithValueForProgress(item);
			});
		}
		return valueCurent;
	}
	private haseValue(formGroup: FormGroup): boolean {
		if (formGroup.get('value').value != null && formGroup.get('value').value !== '' && this.visibilityRulesService.checkElementVisibility(formGroup.get('id').value)) {
			return true;
		}
		return false;
	}

	private compositeFieldsGetChildsForProgress(formGroup: FormGroup): number {
		let valueCurent = 0;
		if (this.visibilityRulesService.checkElementVisibility(formGroup.get('id').value)) {
			(formGroup.get('fields') as FormArray).controls.forEach((element: FormGroup) => {
				valueCurent = valueCurent + this.countFormControlsWithValueForProgress(element);
			});

			(formGroup.get('multiplicityItems') as FormArray).controls.forEach((element: FormGroup) => {
				valueCurent = valueCurent + this.countFormControlsWithValueForProgress(element);
			});
		}
		return valueCurent;
	}

	private checkIfIsRequired(formControl: FormGroup): boolean {
		return !!(formControl.get('validationRequired') && formControl.get('validationRequired').value);

	}

	private checkFormsIfIsFieldsAndVisible(formControl: FormGroup): boolean {
		if (formControl.contains('id') && formControl.contains('value')) {
			return true;
		}
		return false;
	}
	private chechFieldIfIsFieldSetAndVisible(formControl: FormGroup): boolean {
		if (formControl.contains('id') && formControl.contains('fields')) {
			return true;
		}
		return false;
	}

	CountFormControlDepthLengthFotTotal(formControl: AbstractControl): number {
		let valueCurent = 0;
		if (formControl instanceof FormArray) {
			formControl.controls.forEach(item => {
				valueCurent = valueCurent + this.CountFormControlDepthLengthFotTotal(item);
			});
		} else if (formControl instanceof FormGroup) {
			if ((formControl as FormGroup).contains('id') && (formControl as FormGroup).contains('value') && (this.visibilityRulesService.checkElementVisibility((formControl as FormGroup).get('id').value)) && this.checkIfIsRequired((formControl as FormGroup))) {
				valueCurent++;
			} else if ((formControl as FormGroup).contains('id') && (formControl as FormGroup).contains('fields')) {
				valueCurent = valueCurent + this.compositeFieldsGetChildsForTotal(formControl);
			} else {
				Object.keys(formControl.controls).forEach(item => {
					const control = formControl.get(item);
					valueCurent = valueCurent + this.CountFormControlDepthLengthFotTotal(control);
				});
			}
		}

		return valueCurent;
	}

	private compositeFieldsGetChildsForTotal(formGroup: FormGroup): number {
		let valueCurent = 0;
		if (this.visibilityRulesService.checkElementVisibility(formGroup.get('id').value)) {
			(formGroup.get('fields') as FormArray).controls.forEach((element: FormGroup) => {
				valueCurent = valueCurent + this.CountFormControlDepthLengthFotTotal(element);
			});
			(formGroup.get('multiplicityItems') as FormArray).controls.forEach((element: FormGroup) => {
				valueCurent = valueCurent + this.CountFormControlDepthLengthFotTotal(element);
			});
		}
		return valueCurent;
	}

	countFormControlsValidForProgress(formControl: AbstractControl): number {
		let valueCurrent = 0;
		if (formControl instanceof FormControl) {
			if (this.controlRequired(formControl) && this.controlEnabled(formControl) && formControl['nativeElement'] &&  formControl.valid) {
				valueCurrent++;
			}
		} else if (formControl instanceof FormGroup) {
			Object.keys(formControl.controls).forEach(item => {
				const control = formControl.get(item);
				valueCurrent = valueCurrent + this.countFormControlsValidForProgress(control);
			});
		} else if (formControl instanceof FormArray) {
			formControl.controls.forEach(item => {
				valueCurrent = valueCurrent + this.countFormControlsValidForProgress(item);
			});
		}
		return valueCurrent;
	}

	countFormControlsRequiredFieldsForTotal(formControl: AbstractControl): number {
		let valueCurrent = 0;
		if (formControl instanceof FormControl) {
			if (this.controlRequired(formControl) && this.controlEnabled(formControl) && formControl['nativeElement']) {
				valueCurrent++;
			}
		} else if (formControl instanceof FormGroup) {
			Object.keys(formControl.controls).forEach(item => {
				const control = formControl.get(item);
				valueCurrent = valueCurrent + this.countFormControlsRequiredFieldsForTotal(control);
			});
		} else if (formControl instanceof FormArray) {
			formControl.controls.forEach(item => {
				valueCurrent = valueCurrent + this.countFormControlsRequiredFieldsForTotal(item);
			});
		}
		return valueCurrent;
	}

	controlRequired(formControl: AbstractControl) {
		if (formControl.validator) {
			const validator = formControl.validator({} as AbstractControl);
			if (validator && validator.required) {
				return true;
			}
		} else { return false }
	}

	controlEnabled(formControl: AbstractControl) {
		if (formControl.enabled) {
			return true;
		} else { return false }
	}

	isEditor(): boolean {
		return this.isDmpEditor || this.isDatasetEditor;
	}
}
