/**
 ** Iwin8.h - 8-bit image window.
 **
 ** Written: 8/13/98 - JSF
 **/

/*
Copyright (C) 1998 Jeffrey S. Freedman
Copyright (C) 2000-2022 The Exult Team

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA  02111-1307, USA.
*/


#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <cstring>

#include "SDL_video.h"
#include "iwin8.h"
#include "common_types.h"
#include "gamma.h"
#include <climits>
#include <algorithm>
using std::rotate;
using std::unique_ptr;
using std::make_unique;

GammaTable<uint8> Image_window8::GammaRed(256);
GammaTable<uint8> Image_window8::GammaBlue(256);
GammaTable<uint8> Image_window8::GammaGreen(256);

Image_window8::Image_window8(unsigned int w, unsigned int h, unsigned int gwidth, unsigned int gheight,
                             int scl, bool fs, int sclr, Image_window::FillMode fillmode, unsigned int fillsclr)
	: Image_window(new Image_buffer8(0, 0, nullptr), w, h, gwidth, gheight,
	               scl, fs, sclr, fillmode, fillsclr) {
	ib8 = static_cast<Image_buffer8 *>(ibuf);
}

void Image_window8::get_gamma(double &r, double &g, double &b) {
	r = GammaRed.get_gamma();
	g = GammaGreen.get_gamma();
	b = GammaBlue.get_gamma();
}

void Image_window8::set_gamma(double r, double g, double b) {
	GammaRed.set_gamma(r);
	GammaGreen.set_gamma(g);
	GammaBlue.set_gamma(b);
}

/*
 *  Convert rgb value.
 */

inline unsigned char Get_color8(
    unsigned char val,
    int maxval,
    int brightness          // 100=normal.
) {
	uint32 c = (static_cast<uint32>(val) * brightness * 255L) / (100 * maxval);
	return c <= 255L ? static_cast<unsigned char>(c) : 255;
}

/*
 *  Set palette.
 */

void Image_window8::set_palette(
    const unsigned char *rgbs,        // 256 3-byte entries.
    int maxval,         // Highest val. for each color.
    int brightness          // Brightness control (100 = normal).
) {
	// Get the colors.
	SDL_Color colors2[256];
	for (int i = 0; i < 256; i++) {
		colors2[i].r = colors[i * 3] = GammaRed[Get_color8(rgbs[3 * i], maxval, brightness)];
		colors2[i].g = colors[i * 3 + 1]  = GammaGreen[Get_color8(rgbs[3 * i + 1], maxval, brightness)];
		colors2[i].b = colors[i * 3 + 2]  = GammaBlue[Get_color8(rgbs[3 * i + 2], maxval, brightness)];
	}
	SDL_SetPaletteColors(paletted_surface->format->palette, colors2, 0, 256);

	if (paletted_surface != draw_surface)
		SDL_SetPaletteColors(draw_surface->format->palette, colors2, 0, 256);
}

/*
 *  Rotate a range of colors.
 */

void Image_window8::rotate_colors(
    int first,          // Palette index of 1st.
    int num,            // # in range.
    int upd             // 1 to update hardware palette.
) {
	first *= 3;
	num *= 3;
	int cnt = abs(num);
	unsigned char  *start = colors + first;
	unsigned char  *finish = start + cnt;
	if (num > 0) {
		// Shift upward.
		rotate(start, finish - 3, finish);
	} else {
		// Shift downward.
		rotate(start, start + 3, finish);
	}

	if (upd) {          // Take effect now?
		SDL_Color colors2[256];
		for (int i = 0; i < 256; i++) {
			colors2[i].r = colors[i * 3];
			colors2[i].g = colors[i * 3 + 1];
			colors2[i].b = colors[i * 3 + 2];
		}
		SDL_SetPaletteColors(paletted_surface->format->palette, colors2, 0, 256);

		if (paletted_surface != draw_surface)
			SDL_SetPaletteColors(draw_surface->format->palette, colors2, 0, 256);
	}
}

static inline int pow2(int x) {
	return x * x;
}

//a nearest-average-colour 1/3 scaler
unique_ptr<unsigned char[]> Image_window8::mini_screenshot() {
	int i;
	if (!paletted_surface) {
		return nullptr;
	}

	auto buf = make_unique<Uint8[]>(96 * 60);
	const int w = 3 * 96;
	const int h = 3 * 60;
	const unsigned char *pixels = ibuf->get_bits();
	int pitch = ibuf->get_line_width();

	for (int y = 0; y < h; y += 3)
		for (int x = 0; x < w; x += 3) {
			//calculate average colour
			int r = 0;
			int g = 0;
			int b = 0;
			for (i = 0; i < 3; i++)
				for (int j = 0; j < 3; j++) {
					int pix = pixels[pitch * (j + y + (get_game_height() - h) / 2) +
					                 i + x + (get_game_width()  - w) / 2 ];
					r += colors[3 * pix + 0];
					g += colors[3 * pix + 1];
					b += colors[3 * pix + 2];
				}
			r = r / 9;
			g = g / 9;
			b = b / 9;

			//find nearest-colour in non-rotating palette
			int bestdist = INT_MAX;
			int bestindex = -1;
			for (i = 0; i < 224; i++) {
				int dist = pow2(colors[3 * i + 0] - r)
				           + pow2(colors[3 * i + 1] - g)
				           + pow2(colors[3 * i + 2] - b);
				if (dist < bestdist) {
					bestdist = dist;
					bestindex = i;
				}
			}
			buf[y * w / 9 + x / 3] = bestindex;
		}

	return buf;
}
