'use strict';

/* global shapefile */

/* eslint no-console: off */

async function main() {

	const size = 4096;
	const pickCtx = document.querySelector( '#pick' ).getContext( '2d' );
	pickCtx.canvas.width = size;
	pickCtx.canvas.height = size;

	const outlineCtx = document.querySelector( '#outline' ).getContext( '2d' );
	outlineCtx.canvas.width = size;
	outlineCtx.canvas.height = size;
	outlineCtx.translate( outlineCtx.canvas.width / 2, outlineCtx.canvas.height / 2 );
	outlineCtx.scale( outlineCtx.canvas.width / 360, outlineCtx.canvas.height / - 180 );
	outlineCtx.strokeStyle = '#FFF';

	const workCtx = document.createElement( 'canvas' ).getContext( '2d' );
	workCtx.canvas.width = size;
	workCtx.canvas.height = size;

	let id = 1;
	const countryData = {};
	const countriesById = [];
	let min;
	let max;

	function resetMinMax() {

		min = [ 10000, 10000 ];
		max = [ - 10000, - 10000 ];

	}

	function minMax( p ) {

		min[ 0 ] = Math.min( min[ 0 ], p[ 0 ] );
		min[ 1 ] = Math.min( min[ 1 ], p[ 1 ] );
		max[ 0 ] = Math.max( max[ 0 ], p[ 0 ] );
		max[ 1 ] = Math.max( max[ 1 ], p[ 1 ] );

	}

	const geoHandlers = {
		'MultiPolygon': multiPolygonArea,
		'Polygon': polygonArea,
	};

	function multiPolygonArea( ctx, geo, drawFn ) {

		const { coordinates } = geo;
		for ( const polygon of coordinates ) {

			ctx.beginPath();
			for ( const ring of polygon ) {

				ring.forEach( minMax );
				ctx.moveTo( ...ring[ 0 ] );
				for ( let i = 0; i < ring.length; ++ i ) {

					ctx.lineTo( ...ring[ i ] );

				}

				ctx.closePath();

			}

			drawFn( ctx );

		}

	}

	function polygonArea( ctx, geo, drawFn ) {

		const { coordinates } = geo;
		ctx.beginPath();
		for ( const ring of coordinates ) {

			ring.forEach( minMax );
			ctx.moveTo( ...ring[ 0 ] );
			for ( let i = 0; i < ring.length; ++ i ) {

				ctx.lineTo( ...ring[ i ] );

			}

			ctx.closePath();

		}

		drawFn( ctx );

	}

	function fill( ctx ) {

		ctx.fill( 'evenodd' );

	}

	// function stroke(ctx) {
	//   ctx.save();
	//   ctx.setTransform(1, 0, 0, 1, 0, 0);
	//   ctx.stroke();
	//   ctx.restore();
	// }

	function draw( area ) {

		const { properties, geometry } = area;
		const { type } = geometry;
		const name = properties.NAME;

		console.log( name );

		if ( ! countryData[ name ] ) {

			const r = ( id >> 0 ) & 0xFF;
			const g = ( id >> 8 ) & 0xFF;
			const b = ( id >> 16 ) & 0xFF;

			countryData[ name ] = {
				color: [ r, g, b ],
				id: id ++,
			};
			countriesById.push( { name } );

		}

		const countryInfo = countriesById[ countryData[ name ].id - 1 ];

		const handler = geoHandlers[ type ];
		if ( ! handler ) {

			throw new Error( 'unknown geometry type.' );

		}

		resetMinMax();

		workCtx.save();
		workCtx.clearRect( 0, 0, workCtx.canvas.width, workCtx.canvas.height );
		workCtx.fillStyle = '#000';
		workCtx.strokeStyle = '#000';
		workCtx.translate( workCtx.canvas.width / 2, workCtx.canvas.height / 2 );
		workCtx.scale( workCtx.canvas.width / 360, workCtx.canvas.height / - 180 );

		handler( workCtx, geometry, fill );

		workCtx.restore();

		countryInfo.min = min;
		countryInfo.max = max;
		countryInfo.area = properties.AREA;
		countryInfo.lat = properties.LAT;
		countryInfo.lon = properties.LON;
		countryInfo.population = {
			'2005': properties.POP2005,
		};

		//
		const left = Math.floor( ( min[ 0 ] + 180 ) * workCtx.canvas.width / 360 );
		const bottom = Math.floor( ( - min[ 1 ] + 90 ) * workCtx.canvas.height / 180 );
		const right = Math.ceil( ( max[ 0 ] + 180 ) * workCtx.canvas.width / 360 );
		const top = Math.ceil( ( - max[ 1 ] + 90 ) * workCtx.canvas.height / 180 );
		const width = right - left + 1;
		const height = Math.max( 1, bottom - top + 1 );

		const color = countryData[ name ].color;
		const src = workCtx.getImageData( left, top, width, height );
		for ( let y = 0; y < height; ++ y ) {

			for ( let x = 0; x < width; ++ x ) {

				const off = ( y * width + x ) * 4;
				if ( src.data[ off + 3 ] ) {

					src.data[ off + 0 ] = color[ 0 ];
					src.data[ off + 1 ] = color[ 1 ];
					src.data[ off + 2 ] = color[ 2 ];
					src.data[ off + 3 ] = 255;

				}

			}

		}

		workCtx.putImageData( src, left, top );
		pickCtx.drawImage( workCtx.canvas, 0, 0 );

		//    handler(outlineCtx, geometry, stroke);

	}

	const source = await shapefile.open( 'TM_WORLD_BORDERS-0.3.shp' );
	const areas = [];
	for ( let i = 0; ; ++ i ) {

		const { done, value } = await source.read();
		if ( done ) {

			break;

		}

		areas.push( value );
		draw( value );
		if ( i % 20 === 19 ) {

			await wait();

		}

	}

	console.log( JSON.stringify( areas ) );

	console.log( 'min', min );
	console.log( 'max', max );

	console.log( JSON.stringify( countriesById, null, 2 ) );

	const pick = pickCtx.getImageData( 0, 0, pickCtx.canvas.width, pickCtx.canvas.height );
	const outline = outlineCtx.getImageData( 0, 0, outlineCtx.canvas.width, outlineCtx.canvas.height );

	function getId( imageData, x, y ) {

		const off = ( ( ( y + imageData.height ) % imageData.height ) * imageData.width + ( ( x + imageData.width ) % imageData.width ) ) * 4;
		return imageData.data[ off + 0 ] +
           imageData.data[ off + 1 ] * 256 +
           imageData.data[ off + 2 ] * 256 * 256 +
           imageData.data[ off + 3 ] * 256 * 256 * 256;

	}

	function putPixel( imageData, x, y, color ) {

		const off = ( y * imageData.width + x ) * 4;
		imageData.data.set( color, off );

	}


	for ( let y = 0; y < pick.height; ++ y ) {

		for ( let x = 0; x < pick.width; ++ x ) {

			const s = getId( pick, x, y );
			const r = getId( pick, x + 1, y );
			const d = getId( pick, x, y + 1 );
			let v = 0;
			if ( s !== r || s !== d ) {

				v = 255;

			}

			putPixel( outline, x, y, [ v, v, v, v ] );

		}

	}

	for ( let y = 0; y < outline.height; ++ y ) {

		for ( let x = 0; x < outline.width; ++ x ) {

			const s = getId( outline, x, y );
			const l = getId( outline, x - 1, y );
			const u = getId( outline, x, y - 1 );
			const r = getId( outline, x + 1, y );
			const d = getId( outline, x, y + 1 );
			//const rd = getId(outline, x + 1, y + 1);
			let v = s;
			if ( ( s && r && d ) ||
          ( s && l && d ) ||
          ( s && r && u ) ||
          ( s && l && u ) ) {

				v = 0;

			}

			putPixel( outline, x, y, [ v, v, v, v ] );

		}

	}

	outlineCtx.putImageData( outline, 0, 0 );

}

function wait( ms = 0 ) {

	return new Promise( ( resolve ) => {

		setTimeout( resolve, ms );

	} );

}

main();
