import { testfs } from 'mocha-pod';
import { stripIndent } from 'common-tags';

import { expect } from 'chai';
import * as hostUtils from '~/lib/host-utils';

import { promises as fs } from 'fs';
import { ConfigTxt } from '~/src/config/backends/config-txt';

describe('config/config-txt', () => {
	it('correctly parses a config.txt file', async () => {
		const tfs = await testfs({
			[hostUtils.pathOnBoot('config.txt')]: stripIndent`
	        initramfs initramf.gz 0x00800000
					dtparam=i2c=on
					dtparam=audio=on
					dtoverlay=ads7846
					enable_uart=1
					avoid_warnings=1
					gpio=8=pd
					gpio=17=op,dh
					gpu_mem=16
					hdmi_force_hotplug:1=1
					dtoverlay=lirc-rpi,gpio_out_pin=17,gpio_in_pin=13
					dtparam=gpio_out_pin=17`,
		}).enable();

		const configTxt = new ConfigTxt();

		// Will try to parse /test/data/mnt/boot/config.txt
		await expect(configTxt.getBootConfig()).to.eventually.deep.equal({
			dtparam: ['i2c=on', 'audio=on'],
			dtoverlay: [
				'ads7846',
				'lirc-rpi,gpio_out_pin=17,gpio_in_pin=13,gpio_out_pin=17',
			],
			enable_uart: '1',
			avoid_warnings: '1',
			gpio: ['8=pd', '17=op,dh'],
			gpu_mem: '16',
			initramfs: 'initramf.gz 0x00800000',
			'hdmi_force_hotplug:1': '1',
		});

		await tfs.restore();
	});

	it('correctly parses a config.txt file with repeated overlays', async () => {
		const tfs = await testfs({
			[hostUtils.pathOnBoot('config.txt')]: stripIndent`
	        gpu_mem=64
					avoid_warnings=1
					dtoverlay=vc4-kms-v3d
					dtoverlay=ads1015
					dtparam=addr=0x48
					dtparam=cha_cfg=4
					dtparam=chb_cfg=5
					dtparam=chc_cfg=6
					dtparam=chd_cfg=7
					dtoverlay=ads1015
					dtparam=addr=0x49
					dtparam=chc_enable=false
					dtparam=chd_enable=false
					dtparam=cha_cfg=0
					dtparam=chb_cfg=3
					dtparam=i2c_arm=on
					dtparam=spi=on
					dtparam=audio=on
					enable_uart=0
					gpio=8=pd
					gpio=17=op,dh
					`,
		}).enable();

		const configTxt = new ConfigTxt();

		// Will try to parse /test/data/mnt/boot/config.txt
		await expect(configTxt.getBootConfig()).to.eventually.deep.equal({
			dtparam: ['i2c_arm=on', 'spi=on', 'audio=on'],
			dtoverlay: [
				'vc4-kms-v3d',
				'ads1015,addr=0x48,cha_cfg=4,chb_cfg=5,chc_cfg=6,chd_cfg=7',
				'ads1015,addr=0x49,chc_enable=false,chd_enable=false,cha_cfg=0,chb_cfg=3',
			],
			enable_uart: '0',
			avoid_warnings: '1',
			gpio: ['8=pd', '17=op,dh'],
			gpu_mem: '64',
		});

		await tfs.restore();
	});

	it('correctly parses a config.txt file with an empty overlay', async () => {
		const tfs = await testfs({
			[hostUtils.pathOnBoot('config.txt')]: stripIndent`
	        initramfs initramf.gz 0x00800000
					dtoverlay=lirc-rpi,gpio_out_pin=17,gpio_in_pin=13
					dtparam=gpio_out_pin=17
					enable_uart=1
					avoid_warnings=1
					dtoverlay=
					dtparam=i2c=on
					dtparam=lala=on
					dtparam=audio=on
					dtoverlay=ads7846
					gpu_mem=16
					hdmi_force_hotplug:1=1
					`,
		}).enable();

		const configTxt = new ConfigTxt();

		// Will try to parse /test/data/mnt/boot/config.txt
		await expect(configTxt.getBootConfig()).to.eventually.deep.equal({
			dtparam: ['i2c=on', 'audio=on', 'lala=on'],
			dtoverlay: [
				'lirc-rpi,gpio_out_pin=17,gpio_in_pin=13,gpio_out_pin=17',
				'ads7846',
			],
			enable_uart: '1',
			avoid_warnings: '1',
			gpu_mem: '16',
			initramfs: 'initramf.gz 0x00800000',
			'hdmi_force_hotplug:1': '1',
		});

		await tfs.restore();
	});

	it('correctly parses default params on config.txt', async () => {
		const tfs = await testfs({
			[hostUtils.pathOnBoot('config.txt')]: stripIndent`
	        initramfs initramf.gz 0x00800000
					dtoverlay=lirc-rpi,gpio_out_pin=17,gpio_in_pin=13
					dtparam=gpio_out_pin=17
					enable_uart=1
					avoid_warnings=1
					dtparam=spi=on
					dtparam=audio=on
					dtparam=i2c=on
					dtparam=i2c_arm=on
					dtparam=i2c_vc=on
					dtparam=i2c_baudrate=100000
					dtparam=i2c_arm_baudrate=100000
					dtoverlay=ads7846
					dtparam=i2c_vc_baudrate=100000
					gpu_mem=16
					hdmi_force_hotplug:1=1
					`,
		}).enable();

		const configTxt = new ConfigTxt();

		// Will try to parse /test/data/mnt/boot/config.txt
		await expect(configTxt.getBootConfig()).to.eventually.deep.equal({
			dtparam: [
				'spi=on',
				'audio=on',
				'i2c=on',
				'i2c_arm=on',
				'i2c_vc=on',
				'i2c_baudrate=100000',
				'i2c_arm_baudrate=100000',
				'i2c_vc_baudrate=100000',
			],
			dtoverlay: [
				'lirc-rpi,gpio_out_pin=17,gpio_in_pin=13,gpio_out_pin=17',
				'ads7846',
			],
			enable_uart: '1',
			avoid_warnings: '1',
			gpu_mem: '16',
			initramfs: 'initramf.gz 0x00800000',
			'hdmi_force_hotplug:1': '1',
		});

		await tfs.restore();
	});

	it('maintains ordering of dtoverlays and dtparams', async () => {
		const tfs = await testfs({
			[hostUtils.pathOnBoot('config.txt')]: stripIndent`
	        initramfs initramf.gz 0x00800000
					dtparam=i2c=on
					dtparam=audio=on
					dtoverlay=ads7846
					enable_uart=1
					avoid_warnings=1
					gpu_mem=16
					hdmi_force_hotplug:1=1
					dtoverlay=lirc-rpi,gpio_out_pin=17,gpio_in_pin=13
					dtoverlay=ads1015,addr=0x48,cha_enable=true
					dtparam=chb_enable=true
					dtparam=chc_enable=true
			`,
		}).enable();

		const configTxt = new ConfigTxt();

		// Will try to parse /test/data/mnt/boot/config.txt
		await expect(configTxt.getBootConfig()).to.eventually.deep.equal({
			dtparam: ['i2c=on', 'audio=on'],
			dtoverlay: [
				'ads7846',
				'lirc-rpi,gpio_out_pin=17,gpio_in_pin=13',
				'ads1015,addr=0x48,cha_enable=true,chb_enable=true,chc_enable=true',
			],
			enable_uart: '1',
			avoid_warnings: '1',
			gpu_mem: '16',
			initramfs: 'initramf.gz 0x00800000',
			'hdmi_force_hotplug:1': '1',
		});

		await tfs.restore();
	});

	it('splits dtoverlays into params to stay under the 80 char limit', async () => {
		const tfs = await testfs({
			[hostUtils.pathOnBoot('config.txt')]: stripIndent`
	        enable_uart=1
					dtparam=i2c_arm=on
					dtparam=spi=on
					disable_splash=1
					dtparam=audio=on
					gpu_mem=16
					`,
		}).enable();

		const configTxt = new ConfigTxt();

		await configTxt.setBootConfig({
			dtparam: ['i2c=on', 'audio=on'],
			dtoverlay: [
				'ads7846',
				'lirc-rpi,gpio_out_pin=17,gpio_in_pin=13',
				'ads1015,addr=0x48,cha_enable=true,chb_enable=true',
			],
			enable_uart: '1',
			avoid_warnings: '1',
			gpu_mem: '256',
			initramfs: 'initramf.gz 0x00800000',
			'hdmi_force_hotplug:1': '1',
		});

		// Confirm that the file was written correctly
		await expect(
			fs.readFile(hostUtils.pathOnBoot('config.txt'), 'utf8'),
		).to.eventually.equal(
			stripIndent`
					dtparam=i2c=on
					dtparam=audio=on
					enable_uart=1
					avoid_warnings=1
					gpu_mem=256
					initramfs initramf.gz 0x00800000
					hdmi_force_hotplug:1=1
					dtoverlay=ads7846
					dtoverlay=lirc-rpi
					dtparam=gpio_out_pin=17
					dtparam=gpio_in_pin=13
					dtoverlay=ads1015
					dtparam=addr=0x48
					dtparam=cha_enable=true
					dtparam=chb_enable=true
				` + '\n',
		);
		await tfs.restore();
	});

	it('ensures required fields are written to config.txt', async () => {
		const tfs = await testfs({
			[hostUtils.pathOnBoot('config.txt')]: stripIndent`
	        enable_uart=1
					dtparam=i2c_arm=on
					dtparam=spi=on
					disable_splash=1
					dtparam=audio=on
					gpu_mem=16
					`,
		}).enable();

		const configTxt = new ConfigTxt();

		await configTxt.setBootConfig({
			dtparam: ['i2c=on', 'audio=on'],
			dtoverlay: ['ads7846', 'lirc-rpi,gpio_out_pin=17,gpio_in_pin=13'],
			enable_uart: '1',
			avoid_warnings: '1',
			gpu_mem: '256',
			initramfs: 'initramf.gz 0x00800000',
			'hdmi_force_hotplug:1': '1',
		});

		// Will try to parse /test/data/mnt/boot/config.txt
		await expect(configTxt.getBootConfig()).to.eventually.deep.equal({
			dtparam: ['i2c=on', 'audio=on'],
			dtoverlay: ['ads7846', 'lirc-rpi,gpio_out_pin=17,gpio_in_pin=13'],
			enable_uart: '1',
			avoid_warnings: '1',
			gpu_mem: '256',
			initramfs: 'initramf.gz 0x00800000',
			'hdmi_force_hotplug:1': '1',
		});

		await tfs.restore();
	});
});
