import archive.tar
import flag
import net.http
import os
import term

const default_url = 'https://github.com/vlang/v/archive/refs/tags/v0.1.3.tar.gz'

@[heap]
struct Context {
	url        string // Web starting with http:// or https://. Local starting with file:///
	chunks     bool   // true: decompress with callback
	debug      int    // print debug lines
	max_blocks int    // if max_blocks > 0 and is reached stops early.
	filename   string // if filename is found as a path of a data block, stops early.
}

fn (ctx &Context) read_last_block(mut read tar.Read) bool {
	if ctx.max_blocks > 0 && ctx.max_blocks < read.get_block_number() {
		read.stop_early = true
		return true
	}
	return false
}

fn new_context() !&Context {
	mut fp := flag.new_flag_parser(os.args)
	fp.application('tar_gz_reader')
	fp.version('0.0.20250721')
	fp.description('Reads into memory selected sections of *.tar.gz. archives from https or home_dir.')
	fp.skip_executable()
	ctx := &Context{
		url:        fp.string('url', `u`, default_url, 'archive *.tar.gz URL, default(${default_url}). Start name with file:/// for local')
		chunks:     fp.bool('chunks', `c`, false, 'decompress with chunks to reduce RAM usage, default(false)')
		debug:      fp.int('debug', `d`, 0, 'prints blocks: 1=other, 2:+dirs, 3=+files, 4=+data, default(0=silent)')
		max_blocks: fp.int('max_blocks', `m`, 0, 'maximum blocks to read, stop early. Default(0=read all)')
		filename:   fp.string('filename', `f`, '', 'filename content complete print, stop early. Default(empty means none)')
	}
	additional := fp.finalize()!
	if additional.len > 0 {
		println('unprocessed args ${additional.join_lines()}')
	}
	return ctx
}

// Downloader downloads a *.tar.gz using HTTP chunks
struct Downloader {
mut:
	chunks int
	data   []u8
}

fn new_downloader(url string) !&Downloader {
	mut downloader := &Downloader{}
	params := http.DownloaderParams{
		downloader: downloader
	}
	if url.starts_with('http://') || url.starts_with('https://') {
		http.download_file_with_progress(url, '', params)!
	} else if url.starts_with('file:///') {
		path := '${os.home_dir()}/${url[8..]}'
		println('path ${path}')
		downloader.data = os.read_bytes(path)!
	}
	return downloader
}

fn (mut d Downloader) on_start(mut request http.Request, path string) ! {}

fn (mut d Downloader) on_chunk(request &http.Request, chunk []u8, already_received u64, expected u64) ! {
	if expected == 0 {
		return
	}
	d.chunks++
	d.data << chunk
}

fn (mut d Downloader) on_finish(request &http.Request, response &http.Response) ! {}

struct FileReader implements tar.Reader {
	ctx &Context
mut:
	filepath string
	content  []u8
}

fn new_file_reader(ctx &Context) FileReader {
	return FileReader{
		ctx: ctx
	}
}

fn (mut f FileReader) other_block(mut read tar.Read, details string) {
	if f.ctx.read_last_block(mut read) {
		return
	}
	if f.ctx.debug > 0 {
		row := 'OTHER  block:${read.get_block_number():6} ${read.get_special()} ${details} ${read.get_path()} '
		println(term.colorize(term.bright_yellow, row))
	}
}

fn (mut f FileReader) dir_block(mut read tar.Read, size u64) {
	if f.ctx.read_last_block(mut read) {
		return
	}
	if f.ctx.debug > 1 {
		row := 'DIR    block:${read.get_block_number():6} ${read.get_path()} size:${size}'
		println(term.colorize(term.green, row))
	}
}

fn (mut f FileReader) file_block(mut read tar.Read, size u64) {
	if f.ctx.read_last_block(mut read) {
		return
	}
	path := read.get_path()
	if f.ctx.debug > 2 {
		row := ' FILE  block:${read.get_block_number():6} ${path} size:${size}'
		println(term.colorize(term.bright_blue, row))
	}
	if f.ctx.filename != '' && f.filepath == '' && path.ends_with(f.ctx.filename) {
		f.filepath = path
	}
}

fn (mut f FileReader) data_block(mut read tar.Read, data []u8, pending int) {
	if f.ctx.read_last_block(mut read) {
		return
	}
	path := read.get_path()
	if f.ctx.debug > 3 {
		println('  DATA block:${read.get_block_number():6} ${path} len:${data.len} pend:${pending}')
	}
	if f.ctx.filename != '' {
		if f.filepath == path {
			f.content << data
			if pending == 0 {
				// our file of interest data is complete
				read.stop_early = true
			}
		}
	}
}

fn main() {
	ctx := new_context()!
	reader := FileReader{
		ctx: ctx
	}
	mut untar := tar.new_untar(reader)
	mut decompressor := tar.new_decompressor(untar)
	downloader := new_downloader(ctx.url)!
	if ctx.chunks {
		decompressor.read_chunks(downloader.data)!
	} else {
		decompressor.read_all(downloader.data)!
	}
	println('-'.repeat(80))
	println('Download: ${ctx.url} chunks:${downloader.chunks} bytes=${downloader.data.len}')
	println('Untar:    ${untar}')
	println('Content:  Path:${reader.filepath} bytes:${reader.content.len}')
	println('-'.repeat(80))
	println('${reader.content.bytestr()}')
	println('-'.repeat(80))
}
