/*
 * env
 * Written in 2019 by Lucas
 * CC0 1.0 Universal/Public domain - No rights reserved
 *
 * To the extent possible under law, the author(s) have dedicated all
 * copyright and related and neighboring rights to this software to the
 * public domain worldwide. This software is distributed without any
 * warranty. You should have received a copy of the CC0 Public Domain
 * Dedication along with this software. If not, see
 * <http://creativecommons.org/publicdomain/zero/1.0/>.
 */
#include <errno.h>
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define LENGTH 50

#define INRANGE(x, a, b) ((a) <= (x) && (x) <= (b))

static void
hsvtorgb(double hsv[3], uint8_t rgb[3])
{
	double c, hh, m, x;
	double r, g, b;

	c = hsv[2] * hsv[1];
	hh = hsv[0] / 60.0;
	x = c * (1.0 - fabs(fmod(hh, 2.0) - 1.0));
 
	switch ((int)floor(hh == 6.0 ? 0.0 : hh)) {
	case 0: r = c; g = x; b = 0; break;
	case 1: r = x; g = c; b = 0; break;
	case 2: r = 0; g = c; b = x; break;
	case 3: r = 0; g = x; b = c; break;
	case 4: r = x; g = 0; b = c; break;
	case 5: r = c; g = 0; b = x; break;
	}

	m = hsv[2] - c;
	r += m; g += m; b += m;

	rgb[0] = round(255 * r);
	rgb[1] = round(255 * g);
	rgb[2] = round(255 * b);
}

static void
hsltorgb(double hsl[3], uint8_t rgb[3])
{
	double c, hh, m, x;
	double r, g, b;

	c = (1.0 - fabs(2.0 * hsl[2] - 1.0)) * hsl[1];
	hh = hsl[0] / 60.0;
	x = c * (1.0 - fabs(fmod(hh, 2.0) - 1.0));

	switch ((int)floor(hh == 6.0 ? 0.0 : hh)) {
	case 0: r = c; g = x; b = 0; break;
	case 1: r = x; g = c; b = 0; break;
	case 2: r = 0; g = c; b = x; break;
	case 3: r = 0; g = x; b = c; break;
	case 4: r = x; g = 0; b = c; break;
	case 5: r = c; g = 0; b = x; break;
	}

	m = hsl[2] - c / 2.0;
	r += m; g += m; b += m;

	rgb[0] = round(255 * r);
	rgb[1] = round(255 * g);
	rgb[2] = round(255 * b);
}

static void
parsecolorline(const char *s, size_t n, uint8_t rgb[3])
{
	double d[3];
	unsigned int u[3];

	if (sscanf(s, "hsv %lf %lf %lf", &d[0], &d[1], &d[2]) == 3) {
		if (!INRANGE(d[0], 0, 360) || !INRANGE(d[1], 0, 1)
		    || !INRANGE(d[2], 0, 1))
			goto err;
		hsvtorgb(d, rgb);
	} else if (sscanf(s, "hsl %lf %lf %lf", &d[0], &d[1], &d[2]) == 3) {
		if (!INRANGE(d[0], 0, 360) || !INRANGE(d[1], 0, 1)
		    || !INRANGE(d[2], 0, 1))
			goto err;
		hsltorgb(d, rgb);
	} else if (sscanf(s, "rgb %u %u %u", &u[0], &u[1], &u[2]) == 3) {
		if (!INRANGE(u[0], 0, 255) || !INRANGE(u[1], 0, 255)
		    || !INRANGE(u[2], 0, 255))
			goto err;
		rgb[0] = u[0]; rgb[1] = u[1]; rgb[2] = u[2];
	} else if (sscanf(s, "rgbhex %x %x %x", &u[0], &u[1], &u[2]) == 3) {
		if (!INRANGE(u[0], 0, 255) || !INRANGE(u[1], 0, 255)
		    || !INRANGE(u[2], 0, 255))
			goto err;
		rgb[0] = u[0]; rgb[1] = u[1]; rgb[2] = u[2];
	} else {
err:
		fprintf(stderr, "Invalid input: \"%s\".", s);
		exit(1);
	}
}

static void
readcolorscheme(uint8_t colorscheme[18][3])
{
	char *line = NULL;
	size_t linesize = 0;
	ssize_t linelen;
	int nlines = 0;

	while ((linelen = getline(&line, &linesize, stdin)) != -1
	    && nlines < 18) {
		if (linelen == 0 || line[0] == '\n' || line[0] == '#')
			continue;
		parsecolorline(line, linelen, colorscheme[nlines]);
		nlines++;
	}
	free(line);
	if (ferror(stdin)) {
		fputs("Input error.\n", stderr);
		exit(1);
	}
	if (nlines < 18) {
		fputs("Not enought colors.\n", stderr);
		exit(1);
	}
}

static void
writecolor(uint8_t rgb[3], int l)
{
	int i;
	for (i = 0; i < l; i++) {
		if (fwrite(rgb, sizeof(rgb[0]), 3, stdout) != 3) {
			fputs("Short write.\n", stderr);
			exit(1);
		}
	}
}

static void
do_image(uint8_t colorscheme[18][3])
{
	int i, n;

	printf("P6\n%u %u 255\n", 9 * LENGTH, 2 * LENGTH);
	for (i = 0; i < LENGTH; i++)
		for (n = 0; n < 9; n++)
			writecolor(colorscheme[n], LENGTH);
	for (i = 0; i < LENGTH; i++)
		for (n = 9; n < 18; n++)
			writecolor(colorscheme[n], LENGTH);
}

static void
do_colors(uint8_t colorscheme[18][3])
{
	int n;
	for (n = 0; n < 18; n++)
		printf("#%02x%02x%02x\n", colorscheme[n][0],
		    colorscheme[n][1], colorscheme[n][2]);
}

int
main(int argc, char *argv[])
{
	uint8_t colorscheme[18][3];
	int iflag = 0;

	if (argc > 2)
		goto usage;
	if (argc == 2) {
		if (strncmp(argv[1], "-i", 3) != 0)
			goto usage;
		iflag = 1;
	}

	readcolorscheme(colorscheme);

	if (iflag)
		do_image(colorscheme);
	else
		do_colors(colorscheme);

	return 0;

usage:
	fprintf(stderr, "Usage: %s [-i]\n", argv[0]);
	return 1;
}
