#include <iostream>
#include <cmath>
#include <png.h>
#include <cstdlib>
#include <fstream>
#include <iomanip>
using namespace std;

// conversion based on code from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript

#define px(p,i,j,k) (p)->row_pointers[i][(j)*4+k]

struct change {
	int hg,h[6];
	int sg,s[6];
	int vg,v[6];
	double dhg,dh[6];
	double dsg,ds[6];
	double dvg,dv[6];
	void Reset() {
		hg=sg=vg=0;
		for (int i=0;i<6;i++)
			v[i]=s[i]=h[i]=0;
	}
	void Calc() {
		dhg=double(hg)/180/2;
		dsg=double(sg)/100;
		dvg=vg<0?1+double(vg)/100/2:1+double(vg)/100;
		for (int i=0;i<6;i++) {
			dh[i]=double(h[i])/180/2;
			ds[i]=double(s[i])/100;
			dv[i]=v[i]<0?1+double(v[i])/100/2:1+double(v[i])/100;
		}
	}
	void Load(const char *fname) {
		ifstream fin(fname);
		fin>>hg>>sg>>vg;
		for (int i=0;i<6;i++)
			fin>>h[i]>>s[i]>>v[i];
		fin.close();
		Calc();
	}
};

#define max3(a,b,c) ((a)>(b)?((a)>(c)?(a):(c)):((b)>(c)?(b):(c)))
#define min3(a,b,c) ((a)<(b)?((a)<(c)?(a):(c)):((b)<(c)?(b):(c)))

/**
* Converts an RGB color value to HSL. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes r, g, and b are contained in the set [0, 255] and
* returns h, s, and l in the set [0, 1].
*
* @param   Number  r       The red color value
* @param   Number  g       The green color value
* @param   Number  b       The blue color value
* @return  Array           The HSL representation
*/
void rgb2hsl(double r, double g, double b, double &h, double &s, double &l){
	r/=255; g/=255; b/=255;
	double max = max3(r, g, b), min = min3(r, g, b);
	h=s=0; l = (max + min) / 2;
	
	if(max == min){
		h = s = 0; // achromatic
	}else{
		double d = max - min;
		s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
		if (max==r) h = (g - b) / d + (g < b ? 6 : 0); 
		else if (max==g) h = (b - r) / d + 2; 
		else h = (r - g) / d + 4;
		h /= 6;
	}
}


double hue2rgb(double p, double q, double t){
	if(t < 0) t += 1;
	if(t > 1) t -= 1;
	if(t < 1.f/6) return p + (q - p) * 6 * t;
	if(t < 1.f/2) return q;
	if(t < 2.f/3) return p + (q - p) * (2.f/3 - t) * 6;
	return p;
}

/**
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes h, s, and l are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 255].
*
* @param   Number  h       The hue
* @param   Number  s       The saturation
* @param   Number  l       The lightness
* @return  Array           The RGB representation
*/
void hsl2rgb(double h, double s, double l, double &r, double &g, double &b){
	
	if(s == 0){
		r = g = b = l; // achromatic
	}else{
		
		double q = l < 0.5 ? l * (1 + s) : l + s - l * s;
		double p = 2 * l - q;
		r = hue2rgb(p, q, h + 1.f/3);
		g = hue2rgb(p, q, h);
		b = hue2rgb(p, q, h - 1.f/3);
	}
	
	r*=255; g*=255; b*=255;
	
}

/**
* Converts an RGB color value to HSV. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSV_color_space.
* Assumes r, g, and b are contained in the set [0, 255] and
* returns h, s, and v in the set [0, 1].
*
* @param   Number  r       The red color value
* @param   Number  g       The green color value
* @param   Number  b       The blue color value
* @return  Array           The HSV representation
*/
void rgb2hsv(double r, double g, double b, double &h, double &s, double &v){
	r = r/255, g = g/255, b = b/255;
	double max = max3(r, g, b), min = min3(r, g, b);
	h=0; s=0; v = max;
	
	double d = max - min;
	s = max == 0 ? 0 : d / max;
	
	if(max == min){
		h = 0; // achromatic
	}else{
		if (max==r) h = (g - b) / d + (g < b ? 6 : 0);
		else if (max==g) h = (b - r) / d + 2;
		else  h = (r - g) / d + 4; 
		h /= 6;
	}
	
}

/**
* Converts an HSV color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSV_color_space.
* Assumes h, s, and v are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 255].
*
* @param   Number  h       The hue
* @param   Number  s       The saturation
* @param   Number  v       The value
* @return  Array           The RGB representation
*/
void hsv2rgb(double h, double s, double v, double &r, double &g, double &b){
	r=0; g=0; b=0;
	
	double i = floor(h * 6);
	double f = h * 6 - i;
	double p = v * (1 - s);
	double q = v * (1 - f * s);
	double t = v * (1 - (1 - f) * s);
	
	switch(int(i) % 6){
	case 0: r = v, g = t, b = p; break;
	case 1: r = q, g = v, b = p; break;
	case 2: r = p, g = v, b = t; break;
	case 3: r = p, g = q, b = v; break;
	case 4: r = t, g = p, b = v; break;
	case 5: r = v, g = p, b = q; break;
	}
	
	r *= 255; g *= 255;  b *= 255;
}


struct PngFile {
	
	// read/write process based on code from Guillaume Cottenceau taken from (http://zarb.org/~gc/html/libpng.html)
	
	int width, height;
	png_byte color_type;
	png_byte bit_depth;
	png_structp png_ptr;
	png_infop info_ptr;
	int number_of_passes;
	png_bytep * row_pointers;
	PngFile() { row_pointers=NULL; }
	~PngFile() { 
		Free();
	}
	void Free(){
		if (!row_pointers) return;
		cerr<<"Free..."<<endl;
		for (int i=0;i<height;i++)
			free(row_pointers[i]);
		free(row_pointers);
	}
	bool Load(const char* file_name) {
		
		cerr<<"Loading "<<file_name<<"..."<<endl;
		Free();
		png_byte header[8];	// 8 is the maximum size that can be checked
		
		/* open file and test f
		or it being a png */
		FILE *fp = fopen(file_name, "rb");
		if (!fp) {
			cerr<<"   File could not be opened for reading"<<endl;
			return false;
		}
		fread(header, 1, 8, fp);
		if (png_sig_cmp(header, 0, 8)) {
			cerr<<"   File is not recognized as a PNG file"<<endl;
			return false;
		}
		
		/* initialize stuff */
		png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
		if (!png_ptr) {
			cerr<<"   png_create_read_struct failed"<<endl;
			return false;
		}
		
		info_ptr = png_create_info_struct(png_ptr);
		if (!info_ptr) {
			cerr<<"   png_create_info_struct failed"<<endl;
			return false;
		}
		
		if (setjmp(png_jmpbuf(png_ptr))) {
			cerr<<"   Error during init_io"<<endl;
			return false;
		}
		png_init_io(png_ptr, fp);
		png_set_sig_bytes(png_ptr, 8);
		
		png_read_info(png_ptr, info_ptr);
		
		width = info_ptr->width;
		height = info_ptr->height;
		color_type = info_ptr->color_type;
		bit_depth = info_ptr->bit_depth;
		
		number_of_passes = png_set_interlace_handling(png_ptr);
		png_read_update_info(png_ptr, info_ptr);
		
		/* read file */
		if (setjmp(png_jmpbuf(png_ptr))) {
			cerr<<"   Error during read_image"<<endl;
			return false;
		}
		
		row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * height);
		for (int y=0; y<height; y++)
			row_pointers[y] = (png_byte*) malloc(info_ptr->rowbytes);
		
		png_read_image(png_ptr, row_pointers);
		
		fclose(fp);
		
		return true;
	}
	bool Save(const char* file_name) {
		
		cerr<<"Saving "<<file_name<<"..."<<endl;
		
		/* create file */
		FILE *fp = fopen(file_name, "wb");
		if (!fp) {
			cerr<<"   File could not be opened for writing"<<endl;
			return false;
		}
		
		/* initialize stuff */
		png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
		
		if (!png_ptr) {
			cerr<<"   png_create_write_struct failed"<<endl;
			return false;
		}
		
		info_ptr = png_create_info_struct(png_ptr);
		if (!info_ptr) {
			cerr<<"   png_create_info_struct failed"<<endl;
			return false;
		}
		
		if (setjmp(png_jmpbuf(png_ptr))) {
			cerr<<"   Error during init_io"<<endl;
			return false;
		}
		
		png_init_io(png_ptr, fp);
		
		/* write header */
		if (setjmp(png_jmpbuf(png_ptr))) {
			cerr<<"   Error during writing header"<<endl;
			return false;
		}
		
		png_set_IHDR(png_ptr, info_ptr, width, height,
			bit_depth, color_type, PNG_INTERLACE_NONE,
			PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
		
		png_write_info(png_ptr, info_ptr);
		
		/* write bytes */
		if (setjmp(png_jmpbuf(png_ptr))) {
			cerr<<"   Error during writing bytes"<<endl;
			return false;
		}
		
		png_write_image(png_ptr, row_pointers);
		
		/* end write */
		if (setjmp(png_jmpbuf(png_ptr))) {
			cerr<<"   Error during end of write"<<endl;
			return false;
		}
		
		png_write_end(png_ptr, NULL);
		
		fclose(fp);
		
		return true;
	}
	
	void Apply(change c) {
		double r,g,b,h,s,v; int k;
		cerr<<"Applying..."<<endl;
		for (int i=0;i<height;i++) {
			for (int j=0;j<width;j++) {
				r=px(this,i,j,0);
				g=px(this,i,j,1);
				b=px(this,i,j,2);
				rgb2hsl(r,g,b,h,s,v);
				k=(h+1.f/12)*6;
				if (k==6) k=0;
				h+=c.dhg; s+=c.dsg; v*=c.dvg;
				h+=c.dh[k]; s+=c.ds[k]; v*=c.dv[k];
				while (h<0) h+=1; while (h>1) h-=1;
				if (s<0) s=0; if (s>1) s=1;
				if (v<0) v=0; if (v>1) v=1;
				hsl2rgb(h,s,v,r,g,b); 
				px(this,i,j,0)=r;
				px(this,i,j,1)=g;
				px(this,i,j,2)=b;
			}
		}
	}
};



int main(int argc, char *argv[]) {
	PngFile f1;
	change c;
	if (argc<4) {
		cerr<<"use: "<<argv[0]<<" <orig.png> <config.txt> <dest.png>"<<endl;
		return 1;
	}
	f1.Load(argv[1]);
	c.Load(argv[2]);
	f1.Apply(c);
	f1.Save(argv[3]);
	return 0;
}
