#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
/*****************************************************************************
 * OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
 *
 * OpenRCT2 is the work of many authors, a full list can be found in contributors.md
 * For more information, visit https://github.com/OpenRCT2/OpenRCT2
 *
 * OpenRCT2 is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * A full copy of the GNU General Public License can be found in licence.txt
 *****************************************************************************/
#pragma endregion

#include "../addresses.h"
#include "../config.h"
#include "../interface/colour.h"
#include "../localisation/localisation.h"
#include "drawing.h"

/* size: 0xA12 */
typedef struct rct_draw_scroll_text {
	rct_string_id string_id;	// 0x00
	uint32 string_args_0;		// 0x02
	uint32 string_args_1;		// 0x06
	uint16 position;			// 0x0A
	uint16 mode;				// 0x0C
	uint32 id;					// 0x0E
	uint8 bitmap[64 * 8 * 5];	// 0x12
} rct_draw_scroll_text;

rct_draw_scroll_text *gDrawScrollTextList = RCT2_ADDRESS(RCT2_ADDRESS_DRAW_SCROLL_LIST, rct_draw_scroll_text);
uint8 *gCharacterBitmaps = RCT2_ADDRESS(RCT2_ADDRESS_CHARACTER_BITMAP, uint8);

void scrolling_text_set_bitmap_for_sprite(utf8 *text, int scroll, uint8 *bitmap, sint16 *scrollPositionOffsets);
void scrolling_text_set_bitmap_for_ttf(utf8 *text, int scroll, uint8 *bitmap, sint16 *scrollPositionOffsets);

void scrolling_text_initialise_bitmaps()
{
	uint8 drawingSurface[64];
	rct_drawpixelinfo dpi = {
		.bits = (uint8 *)&drawingSurface,
		.x = 0,
		.y = 0,
		.width = 8,
		.height = 8,
		.pitch = 0,
		.zoom_level = 0
	};


	for (int i = 0; i < 224; i++) {
		memset(drawingSurface, 0, sizeof(drawingSurface));
		gfx_draw_sprite(&dpi, i + 0x10D5, -1, 0, 0);

		for (int x = 0; x < 8; x++) {
			uint8 val = 0;
			for (int y = 0; y < 8; y++) {
				val >>= 1;
				if (dpi.bits[x + y * 8] == 1) {
					val |= 0x80;
				}
			}
			gCharacterBitmaps[i * 8 + x] = val;
		}

	}
}

static uint8 *font_sprite_get_codepoint_bitmap(int codepoint)
{
	return &gCharacterBitmaps[font_sprite_get_codepoint_offset(codepoint) * 8];
}


static int scrolling_text_get_matching_or_oldest(rct_string_id stringId, uint16 scroll, uint16 scrollingMode)
{
	uint32 oldestId = 0xFFFFFFFF;
	int scrollIndex = -1;
	rct_draw_scroll_text* oldestScroll = NULL;
	for (int i = 0; i < 32; i++) {
		rct_draw_scroll_text *scrollText = &gDrawScrollTextList[i];
		if (oldestId >= scrollText->id) {
			oldestId = scrollText->id;
			scrollIndex = i;
			oldestScroll = scrollText;
		}

		// If exact match return the matching index
		uint32 stringArgs0, stringArgs1;
		memcpy(&stringArgs0, gCommonFormatArgs + 0, sizeof(uint32));
		memcpy(&stringArgs1, gCommonFormatArgs + 4, sizeof(uint32));
		if (
			scrollText->string_id == stringId &&
			scrollText->string_args_0 == stringArgs0 &&
			scrollText->string_args_1 == stringArgs1 &&
			scrollText->position == scroll &&
			scrollText->mode == scrollingMode
		) {
			scrollText->id = RCT2_GLOBAL(RCT2_ADDRESS_DRAW_SCROLL_NEXT_ID, uint32);
			return i + 0x606;
		}
	}
	return scrollIndex;
}

static uint8 scrolling_text_get_colour(uint32 character)
{
	int colour = character & 0x7F;
	if (colour & (1 << 7)) {
		return ColourMapA[colour].light;
	} else {
		return ColourMapA[colour].mid_dark;
	}
}

static void scrolling_text_format(utf8 *dst, rct_draw_scroll_text *scrollText)
{
	if (gConfigGeneral.upper_case_banners) {
		format_string_to_upper(dst, scrollText->string_id, &scrollText->string_args_0);
	} else {
		format_string(dst, scrollText->string_id, &scrollText->string_args_0);
	}
}

extern bool TempForScrollText;

/**
 *
 *  rct2: 0x006C42D9
 * @param stringId (ax)
 * @param scroll (cx)
 * @param scrollingMode (bp)
 * @returns ebx
 */
int scrolling_text_setup(rct_string_id stringId, uint16 scroll, uint16 scrollingMode)
{
	if (TempForScrollText) {
		memcpy(gCommonFormatArgs, (const void*)0x013CE952, 16);
	}

	rct_drawpixelinfo* dpi = RCT2_GLOBAL(0x140E9A8, rct_drawpixelinfo*);

	if (dpi->zoom_level != 0) return 0x626;

	RCT2_GLOBAL(RCT2_ADDRESS_DRAW_SCROLL_NEXT_ID, uint32)++;

	int scrollIndex = scrolling_text_get_matching_or_oldest(stringId, scroll, scrollingMode);
	if (scrollIndex >= 0x606) return scrollIndex;

	// Setup scrolling text
	uint32 stringArgs0, stringArgs1;
	memcpy(&stringArgs0, gCommonFormatArgs + 0, sizeof(uint32));
	memcpy(&stringArgs1, gCommonFormatArgs + 4, sizeof(uint32));

	rct_draw_scroll_text* scrollText = &gDrawScrollTextList[scrollIndex];
	scrollText->string_id = stringId;
	scrollText->string_args_0 = stringArgs0;
	scrollText->string_args_1 = stringArgs1;
	scrollText->position = scroll;
	scrollText->mode = scrollingMode;
	scrollText->id = RCT2_GLOBAL(RCT2_ADDRESS_DRAW_SCROLL_NEXT_ID, uint32);

	// Create the string to draw
	utf8 scrollString[256];
	scrolling_text_format(scrollString, scrollText);

	sint16* scrollingModePositions = RCT2_ADDRESS(RCT2_ADDRESS_SCROLLING_MODE_POSITIONS, sint16*)[scrollingMode];

	memset(scrollText->bitmap, 0, 320 * 8);
	if (gUseTrueTypeFont) {
		scrolling_text_set_bitmap_for_ttf(scrollString, scroll, scrollText->bitmap, scrollingModePositions);
	} else {
		scrolling_text_set_bitmap_for_sprite(scrollString, scroll, scrollText->bitmap, scrollingModePositions);
	}

	return scrollIndex + 0x606;
}

void scrolling_text_set_bitmap_for_sprite(utf8 *text, int scroll, uint8 *bitmap, sint16 *scrollPositionOffsets)
{
	uint8 characterColour = scrolling_text_get_colour(gCommonFormatArgs[7]);

	utf8 *ch = text;
	while (true) {
		uint32 codepoint = utf8_get_next(ch, (const utf8**)&ch);

		// If at the end of the string loop back to the start
		if (codepoint == 0) {
			ch = text;
			continue;
		}

		// Set any change in colour
		if (codepoint <= FORMAT_COLOUR_CODE_END && codepoint >= FORMAT_COLOUR_CODE_START){
			codepoint -= FORMAT_COLOUR_CODE_START;
			characterColour = RCT2_GLOBAL(0x009FF048, uint8*)[codepoint * 4];
			continue;
		}

		// If another type of control character ignore
		if (codepoint < 32) continue;

		int characterWidth = font_sprite_get_codepoint_width(FONT_SPRITE_BASE_TINY, codepoint);
		uint8 *characterBitmap = font_sprite_get_codepoint_bitmap(codepoint);
		for (; characterWidth != 0; characterWidth--, characterBitmap++) {
			// Skip any none displayed columns
			if (scroll != 0) {
				scroll--;
				continue;
			}

			sint16 scrollPosition = *scrollPositionOffsets;
			if (scrollPosition == -1) return;
			if (scrollPosition > -1) {
				uint8 *dst = &bitmap[scrollPosition];
				for (uint8 char_bitmap = *characterBitmap; char_bitmap != 0; char_bitmap >>= 1){
					if (char_bitmap & 1) *dst = characterColour;

					// Jump to next row
					dst += 64;
				}
			}
			scrollPositionOffsets++;
		}
	}
}

TTFFontDescriptor *ttf_get_font_from_sprite_base(uint16 spriteBase);
SDL_Surface *_ttf_surface_cache_get_or_add(TTF_Font *font, const utf8 *text);

void scrolling_text_set_bitmap_for_ttf(utf8 *text, int scroll, uint8 *bitmap, sint16 *scrollPositionOffsets)
{
	TTFFontDescriptor *fontDesc = ttf_get_font_from_sprite_base(FONT_SPRITE_BASE_TINY);
	if (fontDesc->font == NULL) {
		scrolling_text_set_bitmap_for_sprite(text, scroll, bitmap, scrollPositionOffsets);
		return;
	}

	// Currently only supports one colour
	uint8 colour = 0;

	utf8 *dstCh = text;
	utf8 *ch = text;
	int codepoint;
	while ((codepoint = utf8_get_next(ch, (const utf8**)&ch)) != 0) {
		if (utf8_is_format_code(codepoint)) {
			if (codepoint >= FORMAT_COLOUR_CODE_START && codepoint <= FORMAT_COLOUR_CODE_END) {
				colour = (uint8)codepoint;
			}
		} else {
			dstCh = utf8_write_codepoint(dstCh, codepoint);
		}
	}
	*dstCh = 0;

	if (colour == 0) {
		colour = scrolling_text_get_colour(gCommonFormatArgs[7]);
	} else {
		colour = RCT2_GLOBAL(0x009FF048, uint8*)[(colour - FORMAT_COLOUR_CODE_START) * 4];
	}

	SDL_Surface *surface = _ttf_surface_cache_get_or_add(fontDesc->font, text);
	if (surface == NULL) {
		return;
	}

	if (SDL_MUSTLOCK(surface) && SDL_LockSurface(surface) == -1) {
		return;
	}

	int pitch = surface->pitch;
	int width = surface->w;
	int height = surface->h;
	uint8 *src = surface->pixels;

	// Offset
	height -= 3;
	src += 3 * pitch;
	height = min(height, 8);

	int x = 0;
	while (true) {
		// Skip any none displayed columns
		if (scroll == 0) {
			sint16 scrollPosition = *scrollPositionOffsets;
			if (scrollPosition == -1) return;
			if (scrollPosition > -1) {
				uint8 *dst = &bitmap[scrollPosition];

				for (int y = 0; y < height; y++) {
					if (src[y * pitch + x] != 0) *dst = colour;

					// Jump to next row
					dst += 64;
				}
			}
			scrollPositionOffsets++;
		} else {
			scroll--;
		}

		x++;
		if (x >= width) x = 0;
	}

	if (SDL_MUSTLOCK(surface)) SDL_UnlockSurface(surface);
}
