var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
import * as THREE from 'three';
import OrbitCamera from './OrbitCamera';
import NavigationBox from './NavigationBox';
var rendererContainerStyles = "\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 100%;\n  height: 100%;\n";
var navBoxContainerStyles = "\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 150px;\n  height: 150px;\n";
var Renderer = /** @class */ (function () {
    function Renderer(scene, container, options) {
        if (options === void 0) { options = {}; }
        var _this = this;
        this.renderLoop = function () {
            _this.updateSize();
            var delta = _this.clock.getDelta();
            var cameraNeedsUpdate = _this.cameraControls.update(delta);
            if (cameraNeedsUpdate) {
                _this.updateNavigationBox();
            }
            requestAnimationFrame(_this.renderLoop);
            _this.threeRenderer.render(_this.threeScene, _this.camera);
        };
        this.handleMouseDown = function (e) {
            _this.mouseDownObject = _this.getObjectFromPosition(e.clientX, e.clientY);
            _this.selectOnMouseUp = true;
        };
        this.handleMouseMove = function (e) {
            _this.selectOnMouseUp = false;
        };
        this.handleMouseUp = function (e) {
            if (_this.selectOnMouseUp) {
                if (_this.mouseDownObject) {
                    _this.objectClickHandlers.forEach(function (handler) {
                        if (_this.mouseDownObject) {
                            var objectJSON = _this.mouseDownObject
                                .userData;
                            handler(objectJSON);
                        }
                    });
                }
                else {
                    _this.backgroundClickHandlers.forEach(function (handler) { return handler(); });
                }
            }
        };
        this.scene = scene;
        this.container = container;
        this.options = __assign({}, options, Renderer.defaultOptions);
        this.objectClickHandlers = [];
        this.backgroundClickHandlers = [];
        this.setup();
        this.renderLoop();
    }
    Renderer.prototype.setup = function () {
        var _this = this;
        var rendererParams = {
            antialias: this.options.antialias,
            sortObjects: this.options.sortObjects,
        };
        var threeRenderer = new THREE.WebGLRenderer(rendererParams);
        threeRenderer.setClearColor(this.options.clearColor);
        this.threeRenderer = threeRenderer;
        this.threeScene = new THREE.Scene();
        this.helpersGroup = new THREE.Group();
        this.threeScene.add(this.helpersGroup);
        this.sceneSetupGroup = this.scene.getSceneSetup();
        this.threeScene.add(this.sceneSetupGroup);
        this.clock = new THREE.Clock();
        this.camera = new THREE.PerspectiveCamera(50, 1, 0.1, 1000);
        this.camera.position.set(0, -150, 80);
        this.camera.up.set(0, 0, 1);
        this.camera.lookAt(this.threeScene.position);
        this.cameraControls = new OrbitCamera(this.camera, this.threeRenderer.domElement);
        this.container.addEventListener('mousedown', this.handleMouseDown);
        this.container.addEventListener('mousemove', this.handleMouseMove);
        this.container.addEventListener('mouseup', this.handleMouseUp);
        this.container.style.position = 'relative';
        var rendererContainer = document.createElement('div');
        rendererContainer.style.cssText = rendererContainerStyles;
        this.container.appendChild(rendererContainer);
        var navBoxContainer = document.createElement('div');
        navBoxContainer.style.cssText = navBoxContainerStyles;
        this.container.appendChild(navBoxContainer);
        this.navigationBox = new NavigationBox(navBoxContainer, {
            onChangeCameraAngle: function (theta, phi) {
                _this.cameraControls.rotateTo(theta, phi, true);
            },
        });
        this.updateNavigationBox();
        rendererContainer.appendChild(threeRenderer.domElement);
    };
    Renderer.prototype.updateSize = function () {
        this.containerRect = this.container.getBoundingClientRect();
        var _a = this.containerRect, width = _a.width, height = _a.height;
        this.threeRenderer.setSize(width, height);
        if (this.camera instanceof THREE.PerspectiveCamera) {
            this.camera.aspect = width / height;
        }
        else if (this.camera instanceof THREE.OrthographicCamera) {
            this.camera.left = width / -20;
            this.camera.right = width / 20;
            this.camera.top = height / 20;
            this.camera.bottom = height / -20;
        }
        this.camera.updateProjectionMatrix();
    };
    Renderer.prototype.updateNavigationBox = function () {
        var direction = new THREE.Vector3();
        this.camera.getWorldDirection(direction);
        var x = direction.x, y = direction.y, z = direction.z;
        this.navigationBox.updateCamera(-x, -y, -z);
    };
    Renderer.prototype.updateScene = function () {
        return __awaiter(this, void 0, void 0, function () {
            var newObjectInTranstion, newObjectsGroup;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.scene.getObjectInTransitionAsync()];
                    case 1:
                        newObjectInTranstion = _a.sent();
                        if (newObjectInTranstion) {
                            if (this.objectInTransition)
                                this.threeScene.remove(this.objectInTransition);
                            this.objectInTransition = newObjectInTranstion;
                            // (this.objectInTransition.material as THREE.MeshLambertMaterial).opacity = 0.5;
                            // (this.objectInTransition.material as THREE.MeshLambertMaterial).transparent = true;
                            // (this.objectInTransition.material as THREE.MeshLambertMaterial).needsUpdate = true;
                            this.threeScene.add(this.objectInTransition);
                        }
                        return [4 /*yield*/, this.scene.getObjectsAsync()];
                    case 2:
                        newObjectsGroup = _a.sent();
                        this.threeScene.remove(this.objectsGroup);
                        if (this.objectInTransition) {
                            this.threeScene.remove(this.objectInTransition);
                            this.objectInTransition = undefined;
                        }
                        this.threeScene.add(newObjectsGroup);
                        this.objectsGroup = newObjectsGroup;
                        return [2 /*return*/];
                }
            });
        });
    };
    Renderer.prototype.setActiveHelper = function (activeOperation) {
        return __awaiter(this, void 0, void 0, function () {
            var helpers;
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        while (this.helpersGroup.children.length > 0) {
                            this.helpersGroup.remove(this.helpersGroup.children[0]);
                        }
                        return [4 /*yield*/, this.scene.setActiveHelperAsync(activeOperation)];
                    case 1:
                        helpers = _a.sent();
                        helpers.forEach(function (helper) { return _this.helpersGroup.add(helper); });
                        return [2 /*return*/];
                }
            });
        });
    };
    Renderer.prototype.updateCameraAngle = function (theta, phi) {
        this.cameraControls.rotateTo(theta, phi, true);
    };
    Renderer.prototype.zoomIn = function () {
        this.cameraControls.zoomIn();
    };
    Renderer.prototype.zoomOut = function () {
        this.cameraControls.zoomOut();
    };
    Renderer.prototype.setOrtographicCamera = function (isOrtographic) {
        var _a = this.containerRect, width = _a.width, height = _a.height;
        var cameraPosition;
        var cameraLookAt;
        if (this.camera) {
            cameraPosition = this.camera.position;
            cameraLookAt = this.cameraControls.target;
        }
        else {
            cameraPosition = new THREE.Vector3(0, -150, 80);
            cameraLookAt = this.threeScene.position;
        }
        if (isOrtographic) {
            this.camera = new THREE.OrthographicCamera(-200, 200, 200, -200, 0.1, 1000);
        }
        else {
            this.camera = new THREE.PerspectiveCamera(50, 1, 0.1, 1000);
        }
        this.camera.up.set(0, 0, 1);
        this.camera.position.set(cameraPosition.x, cameraPosition.y, cameraPosition.z);
        this.camera.lookAt(cameraLookAt);
        this.cameraControls = new OrbitCamera(this.camera, this.threeRenderer.domElement);
        this.navigationBox.setOrtographicCamera(isOrtographic);
        this.updateNavigationBox();
    };
    Renderer.prototype.getObjectFromPosition = function (x, y) {
        var _a = this.containerRect, left = _a.left, top = _a.top, width = _a.width, height = _a.height;
        var mousePosition = new THREE.Vector2();
        mousePosition.x = ((x - left) / width) * 2 - 1;
        mousePosition.y = -((y - top) / height) * 2 + 1;
        var raycaster = new THREE.Raycaster();
        raycaster.setFromCamera(mousePosition, this.camera);
        var intersects = raycaster.intersectObjects(this.objectsGroup.children, true);
        if (intersects.length > 0) {
            return intersects[0].object;
        }
        return undefined;
    };
    Renderer.prototype.onObjectClick = function (handler) {
        this.objectClickHandlers.push(handler);
    };
    Renderer.prototype.onBackgroundClick = function (handler) {
        this.backgroundClickHandlers.push(handler);
    };
    Renderer.defaultOptions = {
        antialias: true,
        clearColor: 0xfafafa,
        sortObjects: false,
    };
    return Renderer;
}());
export default Renderer;
//# sourceMappingURL=Renderer.js.map