#!/usr/bin/ucode

'use strict';

import { find_phy } from 'wifi.utils';
import * as uci from 'uci';
import * as iwinfo from 'iwinfo';

function normalize_ssid(ssid) {
	if (!ssid)
		return 'unknown';
	return '"' + replace(ssid, /[[:cntrl:]]/g,
			     function(c) { return '\\x' + hexenc(c); }) + '"'
}

function print_assoclist(stations) {
	for (let mac, station in stations) {
		printf(`${station.mac}  ${station.signal} dBm / ${station.noise} dBm (SNR ${station.snr})  ${station.inactive_time} ms ago\n`);
		for (let k in [ 'rx', 'tx' ]) {
			let bitrate = station[k];
			let flags = join(', ', bitrate.flags);

			printf(`\t${uc(k)}: ${bitrate.bitrate == 'unknown' ? 'unknown' : bitrate.bitrate + ' MBit/s'}`);
			if (length(bitrate.flags))
				printf(', %s', flags);
			printf('%10d Pkts.\n', bitrate.packets);
		}
		let expected_throughput = station.expected_throughput;
		printf(`\texpected throughput: ${expected_throughput == 'unknown' ? 'unknown' : expected_throughput + ' MBit/s'}\n\n`);
	}
}

function print_countrylist(list) {
	for (let k, v in list.countries)
		printf(`${k == list.active ? '*' : ' '}   ${k} "${v}"\n`);
}

function print_freqlist(channels) {
	for (let channel in channels) {
		printf(`${channel.active ? '*' : ' '} ${channel.freq} GHz (Band: ${channel.band} GHz, Channel ${channel.channel})`);
		if (length(channel.flags))
			printf(` [${join(', ', channel.flags)}]`);
		printf('\n');
	}
}

function print_htmodelist(htmode) {
	printf('%s\n', join(' ', htmode));
}

function print_info(list) {
	let padding = '         ';

	for (let bss in list) {
		printf(`${bss.iface} ESSID: ${normalize_ssid(bss.ssid)}\n`);
		printf(`${padding}Access Point: ${bss.mac}\n`);
		printf(`${padding}Mode: ${bss.mode}  Channel: ${bss.channel} (${bss.freq} GHz)  HT Mode: ${bss.htmode}\n`);
		printf(`${padding}Center Channel 1: ${bss.center_freq1} 2: ${bss.center_freq2}\n`);
		printf(`${padding}Tx-Power: ${bss.txpower} dBm  Link Quality: ${bss.quality}/70\n`);
		printf(`${padding}Signal: ${bss.signal} dBm  Noise: ${bss.noise} dBm\n`);
		printf(`${padding}Bit Rate: ${bss.bitrate == 'unknown' ? 'unknown' : bss.bitrate + ' MBit/s'}\n`);
		printf(`${padding}Encryption: ${bss.encryption}\n`);
		printf(`${padding}Type: nl80211  HW Mode(s): 802.11${bss.hwmode}\n`);
		printf(`${padding}Hardware: ${bss.hw_type} [${bss.hw_id}]\n`);
		printf(`${padding}TX power offset: ${bss.power_offset}\n`);
		printf(`${padding}Channel offset: ${bss.channel_offset}\n`);
		printf(`${padding}Supports VAPs: ${bss.vaps}  PHY name: ${bss.phy}\n`);
		if (bss.owe_transition_ifname)
			printf(`${padding}OWE partner: ${bss.owe_transition_ifname}\n`);
		printf('\n');
	}
	return 0;
}

function print_scan(cells) {
	let idx = 1;

	for (let cell in cells) {
		printf('Cell %02d - Address: %s\n', idx++, cell.bssid);
		printf('\t  ESSID: %s\n', normalize_ssid(cell.ssid));
		printf('\t  Mode: %s  Frequency: %s GHz  Band: %s GHz  Channel: %d\n', cell.mode, cell.frequency, cell.band, cell.channel);
		printf('\t  Signal: %d dBm  Quality: %2d/70\n', cell.dbm, cell.quality);

		if (!cell.crypto || !length(cell.crypto.key_mgmt))
			printf('\t  Encryption: NONE\n');
		else
			printf('\t  Encryption: %s (%s)\n', join(' / ', cell.crypto.key_mgmt), join(' / ', cell.crypto.pair));

		if (cell.ht) {
			printf('\t  HT Operation:\n');
			printf('\t\tPrimary Channel: %d\n', cell.ht.primary_channel);
			printf('\t\tSecondary Channel Offset: %s\n', cell.ht.secondary_chan_off);
			printf('\t\tChannel Width: %s\n', cell.ht.chan_width);
		}
		
		if (cell.vht) {
			printf('\t  VHT Operation:\n');
			printf('\t\tCenter Frequency 1: %d\n', cell.vht.center_chan_1);
			printf('\t\tCenter Frequency 2: %s\n', cell.vht.center_chan_2);
			printf('\t\tChannel Width: %s\n', cell.vht.chan_width);
		}

		if (cell.he) {
			printf('\t  HE Operation:\n');
			printf('\t\tCenter Frequency 1: %d\n', cell.he.center_chan_1);
			printf('\t\tCenter Frequency 2: %s\n', cell.he.center_chan_2);
			printf('\t\tChannel Width: %s\n', cell.he.chan_width);
		}

		if (cell.eht) {
			printf('\t  EHT Operation:\n');
			printf('\t\tCenter Frequency 1: %d\n', cell.eht.center_chan_1);
			printf('\t\tCenter Frequency 2: %s\n', cell.eht.center_chan_2);
			printf('\t\tChannel Width: %s\n', cell.eht.chan_width);
		}

		printf('\n'); 
	}
}

function print_txpowerlist(list) {
	for (let power in list)
		printf('%s %2d dbm (%4d mW)\n', power.active ? '*' : ' ', power.dbm, power.mw);
}

let pretty = true;
if (ARGV[0] == '-j') {
	pretty = false;
	shift(ARGV);
}

if (!length(ARGV)) {
	let info = iwinfo.info();
	if (pretty)
		print_info(info);
	else
		printf('%.J\n', info);
	return 0;
}

const commands = {
	assoclist: [ iwinfo.assoclist, print_assoclist ],
	countrylist: [ iwinfo.countrylist, print_countrylist ],
	freqlist: [ iwinfo.freqlist, print_freqlist ],
	htmodelist: [ iwinfo.htmodelist, print_htmodelist ],
	info: [ iwinfo.info, print_info ],
	scan: [ iwinfo.scan, print_scan ],
	txpowerlist: [ iwinfo.txpowerlist, print_txpowerlist ],
};

if (ARGV[0] == 'nl80211' && ARGV[1] == 'phyname') {
	let sec_name = ARGV[2];
	let sec = uci.cursor(null, null, null, { strict: false }).get_all('wireless', sec_name);
	if (!sec || sec['.type'] != 'wifi-device') {
		warn(`Config section ${sec_name} not found\n`);
		return 1;
	}

	let name = find_phy(sec);
	if (!name) {
		warn('Phy not found\n');
		return 1;
	}

	print(name + '\n');
	return 0;
}

if (length(ARGV) == 2 && iwinfo.ifaces[ARGV[0]])
	for (let cmd, cb in commands)
		if (substr(cmd, 0, length(ARGV[1])) == ARGV[1]) {
			let ret = cb[0](ARGV[0]);
			
			if (pretty)
				cb[1](ret);
			else
				printf('%.J\n', ret);
			return 0;
		}

switch(ARGV[0]) {
case 'phy':
	printf('%.J\n', iwinfo.phys);
	return 0;

case 'iface':
	printf('%.J\n', iwinfo.ifaces);
	return 0;
}

warn('Usage:\n' +
	'\tiwinfo <device> info\n' +
	'\tiwinfo <device> scan\n' +
	'\tiwinfo <device> txpowerlist\n' +
	'\tiwinfo <device> freqlist\n' +
	'\tiwinfo <device> assoclist\n' +
	'\tiwinfo <device> countrylist\n' +
	'\tiwinfo <device> htmodelist\n' +
	'\tiwinfo nl80211 phyname <section>\n');
return 1;
