import * as THREE from 'three';

import { UIBreak, UIButton, UIInteger, UIPanel, UIRow, UISelect, UIText } from './libs/ui.js';

import { ViewportPathtracer } from './Viewport.Pathtracer.js';

function SidebarProjectImage( editor ) {

	const strings = editor.strings;

	const container = new UIPanel();
	container.setId( 'render' );

	// Image

	container.add( new UIText( strings.getKey( 'sidebar/project/image' ) ).setTextTransform( 'uppercase' ) );
	container.add( new UIBreak(), new UIBreak() );

	// Shading

	const shadingRow = new UIRow();
	container.add( shadingRow );

	shadingRow.add( new UIText( strings.getKey( 'sidebar/project/shading' ) ).setClass( 'Label' ) );

	const shadingTypeSelect = new UISelect().setOptions( {
		'solid': 'SOLID',
		'realistic': 'REALISTIC'
	} ).setWidth( '170px' ).onChange( refreshShadingRow ).setValue( 'solid' );
	shadingRow.add( shadingTypeSelect );

	const pathTracerMinSamples = 3;
	const pathTracerMaxSamples = 65536;
	const samplesNumber = new UIInteger( 16 ).setRange( pathTracerMinSamples, pathTracerMaxSamples );

	const samplesRow = new UIRow();
	samplesRow.add( new UIText( strings.getKey( 'sidebar/project/image/samples' ) ).setClass( 'Label' ) );
	samplesRow.add( samplesNumber );

	container.add( samplesRow );

	function refreshShadingRow() {

		samplesRow.setHidden( shadingTypeSelect.getValue() !== 'realistic' );

	}

	refreshShadingRow();

	// Resolution

	const resolutionRow = new UIRow();
	container.add( resolutionRow );

	resolutionRow.add( new UIText( strings.getKey( 'sidebar/project/resolution' ) ).setClass( 'Label' ) );

	const imageWidth = new UIInteger( 1024 ).setTextAlign( 'center' ).setWidth( '28px' );
	resolutionRow.add( imageWidth );

	resolutionRow.add( new UIText( '×' ).setTextAlign( 'center' ).setFontSize( '12px' ).setWidth( '12px' ) );

	const imageHeight = new UIInteger( 1024 ).setTextAlign( 'center' ).setWidth( '28px' );
	resolutionRow.add( imageHeight );

	// Render

	const renderButton = new UIButton( strings.getKey( 'sidebar/project/render' ) );
	renderButton.setWidth( '170px' );
	renderButton.setMarginLeft( '120px' );
	renderButton.onClick( async () => {

		if ( shadingTypeSelect.getValue() === 'realistic' ) {

			let isMaterialsValid = true;

			editor.scene.traverseVisible( ( object ) => {

				if ( object.isMesh ) {

					const materials = Array.isArray( object.material ) ? object.material : [ object.material ];

					for ( let i = 0; i < materials.length; i ++ ) {

						const material = materials[ i ];

						if ( ! material.isMeshStandardMaterial ) {

							isMaterialsValid = false;
							return;

						}

					}

				}

			} );

			if ( isMaterialsValid === false ) {

				alert( strings.getKey( 'prompt/rendering/realistic/unsupportedMaterial' ) );
				return;

			}

		}

		//

		const json = editor.toJSON();
		const project = json.project;

		//

		const loader = new THREE.ObjectLoader();

		const camera = loader.parse( json.camera );
		camera.aspect = imageWidth.getValue() / imageHeight.getValue();
		camera.updateProjectionMatrix();
		camera.updateMatrixWorld();

		const scene = loader.parse( json.scene );

		const renderer = new THREE.WebGLRenderer( { antialias: true } );
		renderer.setSize( imageWidth.getValue(), imageHeight.getValue() );

		if ( project.shadows !== undefined ) renderer.shadowMap.enabled = project.shadows;
		if ( project.shadowType !== undefined ) renderer.shadowMap.type = project.shadowType;
		if ( project.toneMapping !== undefined ) renderer.toneMapping = project.toneMapping;
		if ( project.toneMappingExposure !== undefined ) renderer.toneMappingExposure = project.toneMappingExposure;

		// popup

		const width = imageWidth.getValue() / window.devicePixelRatio;
		const height = imageHeight.getValue() / window.devicePixelRatio;

		const left = ( screen.width - width ) / 2;
		const top = ( screen.height - height ) / 2;

		const output = window.open( '', '_blank', `location=no,left=${left},top=${top},width=${width},height=${height}` );

		const meta = document.createElement( 'meta' );
		meta.name = 'viewport';
		meta.content = 'width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0';
		output.document.head.appendChild( meta );

		output.document.body.style.background = '#000';
		output.document.body.style.margin = '0px';
		output.document.body.style.overflow = 'hidden';

		const canvas = renderer.domElement;
		canvas.style.width = width + 'px';
		canvas.style.height = height + 'px';
		output.document.body.appendChild( canvas );

		//

		switch ( shadingTypeSelect.getValue() ) {

			case 'solid':

				renderer.render( scene, camera );
				renderer.dispose();

				break;

			case 'realistic':

				const status = document.createElement( 'div' );
				status.style.position = 'absolute';
				status.style.top = '10px';
				status.style.left = '10px';
				status.style.color = 'white';
				status.style.fontFamily = 'system-ui';
				status.style.fontSize = '12px';
				output.document.body.appendChild( status );

				const pathTracer = new ViewportPathtracer( renderer );
				pathTracer.init( scene, camera );
				pathTracer.setSize( imageWidth.getValue(), imageHeight.getValue() );

				const maxSamples = Math.max( pathTracerMinSamples, Math.min( pathTracerMaxSamples, samplesNumber.getValue() ) );

				function animate() {

					if ( output.closed === true ) return;

					const samples = Math.floor( pathTracer.getSamples() ) + 1;

					if ( samples < maxSamples ) {

						requestAnimationFrame( animate );

					}

					pathTracer.update();

					const progress = Math.floor( samples / maxSamples * 100 );

					status.textContent = `${ samples } / ${ maxSamples } ( ${ progress }% )`;

					if ( progress === 100 ) {

						status.textContent += ' ✓';

					}

				}

				animate();

				break;

		}

	} );
	container.add( renderButton );

	//

	return container;

}

export { SidebarProjectImage };
