import axios from "axios";
import { UrlPool } from "./urlpool";

export class Downloader {

	pool: UrlPool;
	chunks: Buffer[] = [];
	downloaded_chunks = 0;
	total_chunks = 0;

	chunk_size = 512*1024;
	total_size = 0;

	ready: boolean = false;

	constructor(pool: UrlPool) {
		this.pool = pool;
	}

	async init() {
		let url = await this.pool.get();
		if(!url) throw "No available URL in pool!";
		let r = await axios.get(url[1], {
			responseType: 'arraybuffer',
			headers: {
				Range: `bytes=0-0`
			}
		});
		this.total_size = parseInt(r.headers["content-range"].split("/")[1]);
		this.pool.return(url[0]);
	}

	async download_range(from: number, to: number): Promise<Buffer> {
		return new Promise(async (complete) => {

			if(to > this.total_size) to = this.total_size;

			let chunk_count = Math.max(Math.ceil((to-from)/this.chunk_size), 1);
			let chunks: Buffer[] = [];
			let completed = 0;

			for(let i=0; i<chunk_count; i++) {

				this.download_chunk(from, to, i).then((result) => {
					chunks[i] = result;
					completed++;
					if(completed == chunk_count) {
						complete(Buffer.concat(chunks));
					}
				});

			}

		});
	}

	async download_chunk(from: number, to: number, part: number): Promise<Buffer> {

		let url = await this.pool.get();
		if(!url) throw "No available URL!";

		let lfrom = from + (part * this.chunk_size) + (part > 0 ? 1 : 0);
		let lto = Math.min(from + (part * this.chunk_size) + this.chunk_size, to);
		if(lfrom == lto) lfrom--;

		let r = await axios.get(url[1], {
			responseType: 'arraybuffer',
			headers: {
				Range: `bytes=${lfrom}-${lto}`
			}
		});	

		this.pool.return(url[0]);
		return r.data;

	}

	async download_part(i: number) {

		let chunk_size = 10_000_000;
		let from = i*chunk_size;
		let to = (from+chunk_size-1);
		if(to > this.total_size) to = this.total_size;

		if(this.chunks[i] || from > this.total_size) return false;
		this.total_chunks++;

		this.chunks[i] = await this.download_range(from, to);
		this.downloaded_chunks++;
		return true;

	}

}