import http                 from "http";

import { readdir }          from "fs/promises";
import { stat }             from "fs/promises";
import { readFile }         from "fs/promises";
import { join }             from "path";
import { dirname }          from "path";
import { fileURLToPath }    from "url";


class App
{


	constructor( )
	{

		const selfPath       = fileURLToPath( import.meta.url );

		this.rootPath        = dirname( selfPath );
		this.httpServer      = null;

	}


	async start( )
	{

		this.httpServer      = http.createServer( this.handleRequest.bind( this ) );

		this.httpServer.listen( 2020 );

		console.log("Server started on port http://localhost:2020/");

	}


	async handleRequest( req, res )
	{

		const requestedPath  = decodeURI( req.url );
		const fullPath       = join( this.rootPath, requestedPath );

		const exists         = await this.checkFileExists( fullPath );

		if ( !exists )
		{

			res.statusCode   = 404;
			res.end( "Not Found" );
			return;

		}

		const stats          = await stat( fullPath );

		if ( stats.isDirectory( ) )
		{

			const indexPath   = join( fullPath, "index.html" );
			const indexExists = await this.checkFileExists( indexPath );

			if ( indexExists )
			{

				await this.sendFile( indexPath, res );
				return;

			}

			await this.sendDirectoryListing( fullPath, requestedPath, res );
			return;

		}

		await this.sendFile( fullPath, res );

	}


	async sendFile( path, res )
	{

		const contentType    = this.getContentType( path );
		const fileData       = await readFile( path );

		res.setHeader( "Content-Type", contentType );
		res.statusCode       = 200;

		res.end( fileData );

	}


	async sendDirectoryListing( dirPath, urlPath, res )
	{

		const entries        = await readdir( dirPath, { withFileTypes : true } );

		let html             = "<html><body><h1>Index of " + urlPath + "</h1><ul>";

		let i                = 0;

		while ( i < entries.length )
		{

			const e           = entries[ i ].name;
			const link        = urlPath.endsWith( "/" )
				? urlPath + e
				: urlPath + "/" + e;

			html              = html + "<li><a href=\"" + link + "\">" + e + "</a></li>";

			i = i + 1;

		}

		html                 = html + "</ul></body></html>";

		res.setHeader( "Content-Type", "text/html" );
		res.statusCode       = 200;

		res.end( html );

	}


	async checkFileExists( path )
	{

		const exists          = await stat( path )
			.then( function( ) { return true; } )
			.catch( function( ) { return false; } );

		return exists;

	}


	getContentType( path )
	{

		const lower          = path.toLowerCase( );

		if ( lower.endsWith( ".html" ) ) return "text/html";
		if ( lower.endsWith( ".css" ) )  return "text/css";
		if ( lower.endsWith( ".js" ) )   return "text/javascript";
		if ( lower.endsWith( ".json" ) ) return "application/json";
		if ( lower.endsWith( ".wasm" ) ) return "application/wasm";
		if ( lower.endsWith( ".png" ) )  return "image/png";
		if ( lower.endsWith( ".jpg" ) )  return "image/jpeg";
		if ( lower.endsWith( ".jpeg" ) ) return "image/jpeg";
		if ( lower.endsWith( ".gif" ) )  return "image/gif";
		if ( lower.endsWith( ".svg" ) )  return "image/svg+xml";
		if ( lower.endsWith( ".wgsl" ) ) return "text/plain";
		if ( lower.endsWith( ".txt" ) )  return "text/plain";

		return "application/octet-stream";

	}


}


const app                 = new App( );

await app.start( );
