import express from "express";
import { Downloader } from "./downloader";
import { UrlPoolStorage } from "./urlpool";
import { Readable } from 'stream';


let storage = new UrlPoolStorage;

export class Webserver {

	chunk_size = 5*1024*1024;
	partial_size = this.chunk_size*10;

	constructor() {
		
		const app = express();

		app.get("/status/:id", async (req, res) => {

			let p = storage.get(req.params.id);
			
			res.write(JSON.stringify({
				streams: {
					total: p.urls.length,
					available: p.available().length,
					generating: p.generating
				}
			}));
			res.end();

		});

		app.get("/stream/:id", async (req, res) => {

			let p = storage.get(req.params.id);
			let d = p.get_downloader();

			let range: Range = {from: 0, to: null};
			if(req.headers.range) range = this.parse_range(req.headers.range);
			if(!range.from) range.from = 0;

			if(!d.ready) await d.init();

			let [from, to] = this.from_to([range.from, range.from+this.chunk_size], d.total_size);

			let contentLength = d.total_size-from; 
			let headers = {
				"Content-Range": `bytes ${from}-${d.total_size}/${d.total_size+1}`,
				"Range": `bytes=${from}-${d.total_size}/${d.total_size+1}`,
				"Accept-Ranges": "bytes",
				"Content-Length": contentLength,
				"Content-Type": "application/octet-stream",
			};
		  
			res.writeHead(206, headers);

			const readable = new Readable()
			readable._read = async () => {
				[from, to] = this.from_to([to+1, to+1+this.chunk_size], d.total_size);
				if(from == d.total_size) {
					readable.push(null);
					//res.end();
				}
				else {
					let stream = await this.download_chunk(d, from, to);
					readable.push(stream);
				}
			}
			let stream = await this.download_chunk(d, from, to);
			readable.push(stream);
			readable.pipe(res);

		});
		
		app.listen(8000, function () {
			console.log("Listening on port 8000!");
		});

	}

	async download_chunk(d: Downloader, from: number, to: number) {
		console.log("downloading...", from, to);
		let stream = await d.download_range(from, to);
		console.log("downloaded    ", from, to);
		return stream;
	}

	parse_range(input: string): Range {

		let [from, to]: (number|null)[] = [null, null];
		let parts = input.split("=");
		if(parts[1]) [from, to] = parts[1].split("-").map(i => i ? parseInt(i) : null);
		return {from, to};

	}

	from_to(range: [number, number], max: number) {
		if(range[0] > max) range[0] = max;
		if(range[1] > max) range[1] = max;
		return range;
	}

}

interface Range {
	from: number | null;
	to: number | null;
}