import * as i0 from '@angular/core';
import { Input, ViewChild, Component, Optional } from '@angular/core';
import { of, Subject, from, combineLatest, BehaviorSubject } from 'rxjs';
import { shareReplay, filter, switchMap, map, distinctUntilChanged, startWith } from 'rxjs/operators';
import { loadThree } from '@c8y/ngx-components/lazy/three';
import { loadOrbitControls } from '@c8y/ngx-components/lazy/three-orbit-controls';
import * as i2 from '@c8y/ngx-components';
import { MeasurementRealtimeService, CoreModule } from '@c8y/ngx-components';
import { loadBoxModel } from '@c8y/ngx-components/widgets/implementations/three-d-rotation/lazy-box-model';
import { loadPhoneModel } from '@c8y/ngx-components/widgets/implementations/three-d-rotation/lazy-phone-model';
import * as i2$1 from '@c8y/ngx-components/context-dashboard';
import * as i1 from '@angular/forms';
import { Validators, NgForm, ControlContainer } from '@angular/forms';
import { ButtonsModule } from 'ngx-bootstrap/buttons';

class ThreeDRotationComponent {
    constructor() {
        this.angles$ = of({ x: 0, y: 0, z: 0 });
        this.cameraType$ = of('PC');
        this.isWireframe$ = of(true);
        this.afterViewInit$ = new Subject();
    }
    get canvas() {
        return this.canvasRef?.nativeElement;
    }
    ngOnInit() {
        const three$ = from(loadThree()).pipe(shareReplay(1));
        const model$ = combineLatest([three$, this.modelObj$]).pipe(filter(([, modelObj]) => !!modelObj), switchMap(([three, modelObj]) => this.loadModel(modelObj, three)));
        const modelWithWireframe$ = combineLatest([model$, this.isWireframe$]).pipe(map(([model, isWireframe]) => this.setWireframe(model, isWireframe)));
        const rotatedModel$ = combineLatest([modelWithWireframe$, this.angles$]).pipe(filter(([, angles]) => !!angles), map(([model, angles]) => {
            Object.assign(model.rotation, angles);
            return model;
        }));
        const cameraType$ = this.cameraType$.pipe(filter(type => !!type), distinctUntilChanged());
        let previousCameraType;
        this.renderSubscription = combineLatest([
            three$,
            rotatedModel$,
            cameraType$,
            this.afterViewInit$
        ])
            .pipe(filter(([, model]) => !!model))
            .subscribe(async ([three, model, cameraType]) => {
            if (model !== this.model || previousCameraType !== cameraType) {
                this.model = model;
                previousCameraType = cameraType;
                this.createScene(three, model, cameraType);
            }
            if (!this.renderer) {
                await this.setupRenderer(three);
            }
            this.render();
        });
    }
    ngOnDestroy() {
        this.renderSubscription?.unsubscribe();
        this.controls?.dispose();
    }
    ngAfterViewInit() {
        this.afterViewInit$.next();
    }
    async loadModel(modelObj, three) {
        const loader = new three.ObjectLoader();
        const parsedModel = await loader.parse(modelObj);
        return parsedModel;
    }
    async setupRenderer(three) {
        //* Renderer
        // Use canvas element in template
        this.renderer = new three.WebGLRenderer({ canvas: this.canvas });
        this.renderer.setPixelRatio(devicePixelRatio);
        this.renderer.setSize(this.canvas.clientWidth, this.canvas.clientHeight);
        const OrbitControls = await loadOrbitControls();
        this.controls = new OrbitControls(this.camera, this.renderer.domElement);
        this.controls.enableDamping = true;
        this.controls.dampingFactor = 0.25;
        this.controls.rotateSpeed = 0.35;
        this.controls.addEventListener('change', () => this.render());
    }
    setWireframe(parsedModel, isWireframe) {
        parsedModel.children.forEach((child) => {
            if (child.material) {
                child.material.wireframe = isWireframe;
            }
        });
        return parsedModel;
    }
    render() {
        this.renderer?.render(this.scene, this.camera);
    }
    createScene(three, model, cameraType) {
        //* Scene
        this.scene = new three.Scene();
        this.scene.background = new three.Color(0xffffff);
        this.scene.add(model);
        const light = new three.AmbientLight(0xffffff, 0.5);
        const lightDirectional = new three.DirectionalLight(0xffffff);
        const lightDirectional2 = new three.DirectionalLight(0xffffff);
        lightDirectional.position.set(20, 25, 30);
        lightDirectional2.position.set(-20, -25, -30);
        this.scene.add(lightDirectional);
        this.scene.add(lightDirectional2);
        this.scene.add(light);
        this.camera = this.createCamera(three, cameraType);
    }
    createCamera(three, cameraType) {
        let camera;
        switch (cameraType) {
            case 'OC':
                camera = new three.OrthographicCamera(30 / -2, 30 / 2, 30 / 2, 30 / -2, 1, 1000);
                break;
            case 'PC':
            default:
                camera = new three.PerspectiveCamera(30, this.getAspectRatio(), 0.1, 1000);
                break;
        }
        camera.rotateX(Math.PI / 2);
        camera.rotateY(Math.PI / 2);
        camera.position.z = 23;
        camera.position.x = 14;
        camera.position.y = 7;
        return camera;
    }
    getAspectRatio() {
        return this.canvas.clientWidth / this.canvas.clientHeight;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ThreeDRotationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: ThreeDRotationComponent, isStandalone: true, selector: "c8y-three-d-rotation", inputs: { angles$: "angles$", modelObj$: "modelObj$", cameraType$: "cameraType$", isWireframe$: "isWireframe$" }, viewQueries: [{ propertyName: "canvasRef", first: true, predicate: ["canvas"], descendants: true }], ngImport: i0, template: "<canvas #canvas class=\"fit-w fit-h\"></canvas>\n" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ThreeDRotationComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-three-d-rotation', standalone: true, imports: [], template: "<canvas #canvas class=\"fit-w fit-h\"></canvas>\n" }]
        }], propDecorators: { canvasRef: [{
                type: ViewChild,
                args: ['canvas']
            }], angles$: [{
                type: Input
            }], modelObj$: [{
                type: Input
            }], cameraType$: [{
                type: Input
            }], isWireframe$: [{
                type: Input
            }] } });

class ThreeDRotationWidgetViewComponent {
    constructor(measurementRealtime, dashboard) {
        this.measurementRealtime = measurementRealtime;
        this.dashboard = dashboard;
        this.deviceId$ = new BehaviorSubject(null);
        this.modelName$ = new BehaviorSubject(null);
        this.cameraType$ = new BehaviorSubject('PC');
        this.isWireframe$ = new BehaviorSubject(true);
        this.modelObj$ = this.modelName$.pipe(filter(name => !!name), distinctUntilChanged(), switchMap(name => this.getModelUrl(name)), shareReplay(1));
        this.angles$ = this.deviceId$.pipe(filter(id => !!id), distinctUntilChanged(), switchMap(id => this.getAnglesOfDevice$(id)), startWith({ x: 0, y: 0, z: 0 }));
    }
    ngOnChanges(changes) {
        if (changes.config && this.config) {
            this.onConfigChange();
        }
    }
    onConfigChange() {
        if (this.config.device?.id) {
            this.deviceId$.next(`${this.config.device.id}`);
        }
        else if (this.dashboard?.context?.id) {
            this.deviceId$.next(`${this.dashboard.context?.id}`);
        }
        if (this.config.objectModel) {
            this.modelName$.next(this.config.objectModel);
        }
        if (this.config.cameraType) {
            this.cameraType$.next(this.config.cameraType);
        }
        if (this.config.isWireframe !== undefined) {
            this.isWireframe$.next(this.config.isWireframe);
        }
    }
    async getModelUrl(model) {
        // The name *.min.json still exist for backwards compatibility
        // it might be stored in certain widget configs.
        if (model === 'box.min.json') {
            return await loadBoxModel();
        }
        else {
            return await loadPhoneModel();
        }
    }
    getAnglesOfDevice$(deviceId) {
        const fragment = 'c8y_Acceleration';
        const series = ['accelerationX', 'accelerationY', 'accelerationZ'];
        return this.measurementRealtime
            .latestValueOfSpecificMeasurement$(fragment, series[0], deviceId, 1)
            .pipe(filter(m => !!m && m[fragment] && series.every(axisSeries => m[fragment][axisSeries])), map(measurement => {
            const [xAxisValue, yAxisValue, zAxisValue] = series.map(axisSeries => Math.round(measurement[fragment][axisSeries].value));
            return this.convertValues(xAxisValue, yAxisValue, zAxisValue);
        }));
    }
    convertValues(x, y, z) {
        let rotateX = Math.atan2(y, z);
        let rotateY = Math.atan2(x, Math.sqrt(y * y + z * z));
        rotateX = rotateX ? rotateX % (Math.PI * 2) : 0;
        rotateY = rotateY ? rotateY % (Math.PI * 2) : 0;
        return {
            x: rotateX,
            y: 0,
            z: rotateY
        };
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ThreeDRotationWidgetViewComponent, deps: [{ token: i2.MeasurementRealtimeService }, { token: i2$1.ContextDashboardComponent, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: ThreeDRotationWidgetViewComponent, isStandalone: true, selector: "c8y-three-d-rotation-widget-view", inputs: { config: "config" }, providers: [MeasurementRealtimeService], usesOnChanges: true, ngImport: i0, template: "<c8y-three-d-rotation\n  [modelObj$]=\"modelObj$\"\n  [angles$]=\"angles$\"\n  [cameraType$]=\"cameraType$\"\n  [isWireframe$]=\"isWireframe$\"\n></c8y-three-d-rotation>\n", dependencies: [{ kind: "component", type: ThreeDRotationComponent, selector: "c8y-three-d-rotation", inputs: ["angles$", "modelObj$", "cameraType$", "isWireframe$"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ThreeDRotationWidgetViewComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-three-d-rotation-widget-view', providers: [MeasurementRealtimeService], standalone: true, imports: [ThreeDRotationComponent], template: "<c8y-three-d-rotation\n  [modelObj$]=\"modelObj$\"\n  [angles$]=\"angles$\"\n  [cameraType$]=\"cameraType$\"\n  [isWireframe$]=\"isWireframe$\"\n></c8y-three-d-rotation>\n" }]
        }], ctorParameters: () => [{ type: i2.MeasurementRealtimeService }, { type: i2$1.ContextDashboardComponent, decorators: [{
                    type: Optional
                }] }], propDecorators: { config: [{
                type: Input
            }] } });

class ThreeDRotationWidgetConfigComponent {
    constructor(formBuilder, form) {
        this.formBuilder = formBuilder;
        this.form = form;
    }
    onBeforeSave(config) {
        if (this.formGroup.valid) {
            Object.assign(config, this.formGroup.value);
            return true;
        }
        return false;
    }
    ngOnInit() {
        this.initForm();
    }
    initForm() {
        this.formGroup = this.createForm(this.formBuilder);
        this.form.form.addControl('config', this.formGroup);
        this.formGroup.patchValue(this.config);
    }
    createForm(formBuilder) {
        return formBuilder.group({
            objectModel: ['box.min.json', [Validators.minLength(1)]],
            isWireframe: [true, []],
            cameraType: ['PC', [Validators.minLength(2), Validators.maxLength(2)]]
        });
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ThreeDRotationWidgetConfigComponent, deps: [{ token: i1.FormBuilder }, { token: i1.NgForm }], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: ThreeDRotationWidgetConfigComponent, isStandalone: true, selector: "c8y-three-d-rotation-widget-config", inputs: { config: "config" }, ngImport: i0, template: "<fieldset class=\"c8y-fieldset\">\n  <legend>{{ 'Rendering' | translate }}</legend>\n<form [formGroup]=\"formGroup\">\n  <c8y-form-group class=\"form-group-sm m-b-8\">\n    <label translate>Select object model for rendering</label>\n    <div class=\"input-group input-group-sm\">\n      <div class=\"c8y-select-wrapper\">\n        <select class=\"form-control\" formControlName=\"objectModel\">\n          <option value=\"box.min.json\" translate>Box model</option>\n          <option value=\"phoneModel.min.json\" translate>Phone model</option>\n        </select>\n      </div>\n      <span class=\"input-group-addon bg-level-0\">\n        <label class=\"c8y-switch\">\n          <input type=\"checkbox\" formControlName=\"isWireframe\" />\n          <span></span>\n          <span translate>Wireframe</span>\n        </label>\n      </span>\n    </div>\n  </c8y-form-group>\n\n  <c8y-form-group class=\"form-group-sm\">\n    <label translate>Camera type</label>\n    <div class=\"c8y-select-wrapper\">\n      <select class=\"form-control\" formControlName=\"cameraType\">\n        <option value=\"OC\" translate>Orthographic camera</option>\n        <option value=\"PC\" translate>Perspective camera</option>\n      </select>\n    </div>\n  </c8y-form-group>\n</form>\n</fieldset>\n", dependencies: [{ kind: "ngmodule", type: CoreModule }, { kind: "directive", type: i2.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "component", type: i2.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i2.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }], viewProviders: [{ provide: ControlContainer, useExisting: NgForm }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ThreeDRotationWidgetConfigComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-three-d-rotation-widget-config', viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], standalone: true, imports: [CoreModule, ButtonsModule], template: "<fieldset class=\"c8y-fieldset\">\n  <legend>{{ 'Rendering' | translate }}</legend>\n<form [formGroup]=\"formGroup\">\n  <c8y-form-group class=\"form-group-sm m-b-8\">\n    <label translate>Select object model for rendering</label>\n    <div class=\"input-group input-group-sm\">\n      <div class=\"c8y-select-wrapper\">\n        <select class=\"form-control\" formControlName=\"objectModel\">\n          <option value=\"box.min.json\" translate>Box model</option>\n          <option value=\"phoneModel.min.json\" translate>Phone model</option>\n        </select>\n      </div>\n      <span class=\"input-group-addon bg-level-0\">\n        <label class=\"c8y-switch\">\n          <input type=\"checkbox\" formControlName=\"isWireframe\" />\n          <span></span>\n          <span translate>Wireframe</span>\n        </label>\n      </span>\n    </div>\n  </c8y-form-group>\n\n  <c8y-form-group class=\"form-group-sm\">\n    <label translate>Camera type</label>\n    <div class=\"c8y-select-wrapper\">\n      <select class=\"form-control\" formControlName=\"cameraType\">\n        <option value=\"OC\" translate>Orthographic camera</option>\n        <option value=\"PC\" translate>Perspective camera</option>\n      </select>\n    </div>\n  </c8y-form-group>\n</form>\n</fieldset>\n" }]
        }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i1.NgForm }], propDecorators: { config: [{
                type: Input
            }] } });

/**
 * Generated bundle index. Do not edit.
 */

export { ThreeDRotationComponent, ThreeDRotationWidgetConfigComponent, ThreeDRotationWidgetViewComponent };
//# sourceMappingURL=c8y-ngx-components-widgets-implementations-three-d-rotation.mjs.map
