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


let storage = new UrlPoolStorage;

export class Webserver {

	constructor() {
		
		const app = express();

		app.use(express.static('public'));

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

			let p = await storage.get(req.params.id);
			if(!p) {
				res.status(404);
				res.end();
				return;
			}
			
			res.writeHead(200, {
				"Content-Type": "application/json"
			});
			res.write(JSON.stringify({
				streams: {
					total: p.urls.length,
					available: p.available().length,
					generating: p.generating,
					urls: p.urls,
					total_size: p.total_size,
					downloaders: p.downloaders.length
				}
			}));
			res.end();

		});

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

			let p = await storage.get(req.params.id);
			if(!p) {
				res.status(404);
				res.end();
				return;
			}

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

			let d = p.get_downloader(range.from);

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

			const readable = new Readable();
			readable._read = async () => {
				let stream = await d.more();
				readable.push(stream);
			}
			let stream = await d.more();
			readable.push(stream);
			readable.pipe(res);

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

	}

	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;
}