/*
 * This file is part of LibCSS
 * Licensed under the MIT License,
 *		  http://www.opensource.org/licenses/mit-license.php
 * Copyright 2009 John-Mark Bell <jmb@netsurf-browser.org>
 */

#include "bytecode/bytecode.h"
#include "bytecode/opcodes.h"
#include "select/propset.h"
#include "select/propget.h"
#include "utils/utils.h"

#include "select/properties/properties.h"
#include "select/properties/helpers.h"

css_error css__cascade_font_family(uint32_t opv, css_style *style, 
		css_select_state *state)
{
	uint16_t value = CSS_FONT_FAMILY_INHERIT;
	lwc_string **fonts = NULL;
	uint32_t n_fonts = 0;

	if (isInherit(opv) == false) {
		uint32_t v = getValue(opv);

		while (v != FONT_FAMILY_END) {
			lwc_string *font = NULL;
			lwc_string **temp;

			switch (v) {
			case FONT_FAMILY_STRING:
			case FONT_FAMILY_IDENT_LIST:
				css__stylesheet_string_get(style->sheet, 
					*((css_code_t *) style->bytecode), 
					&font);
				advance_bytecode(style, sizeof(css_code_t));
				break;
			case FONT_FAMILY_SERIF:
				if (value == CSS_FONT_FAMILY_INHERIT)
					value = CSS_FONT_FAMILY_SERIF;
				break;
			case FONT_FAMILY_SANS_SERIF:
				if (value == CSS_FONT_FAMILY_INHERIT)
					value = CSS_FONT_FAMILY_SANS_SERIF;
				break;
			case FONT_FAMILY_CURSIVE:
				if (value == CSS_FONT_FAMILY_INHERIT)
					value = CSS_FONT_FAMILY_CURSIVE;
				break;
			case FONT_FAMILY_FANTASY:
				if (value == CSS_FONT_FAMILY_INHERIT)
					value = CSS_FONT_FAMILY_FANTASY;
				break;
			case FONT_FAMILY_MONOSPACE:
				if (value == CSS_FONT_FAMILY_INHERIT)
					value = CSS_FONT_FAMILY_MONOSPACE;
				break;
			}

			/* Only use family-names which occur before the first
			 * generic-family. Any values which occur after the
			 * first generic-family are ignored. */
			/** \todo Do this at bytecode generation time? */
			if (value == CSS_FONT_FAMILY_INHERIT && font != NULL) {
				temp = state->computed->alloc(fonts, 
					(n_fonts + 1) * sizeof(lwc_string *), 
					state->computed->pw);
				if (temp == NULL) {
					if (fonts != NULL) {
						state->computed->alloc(fonts, 0,
							state->computed->pw);
					}
					return CSS_NOMEM;
				}

				fonts = temp;

				fonts[n_fonts] = font;

				n_fonts++;
			}

			v = *((uint32_t *) style->bytecode);
			advance_bytecode(style, sizeof(v));
		}
	}

	/* Terminate array with blank entry, if needed */
	if (n_fonts > 0) {
		lwc_string **temp;

		temp = state->computed->alloc(fonts, 
				(n_fonts + 1) * sizeof(lwc_string *), 
				state->computed->pw);
		if (temp == NULL) {
			state->computed->alloc(fonts, 0, state->computed->pw);
			return CSS_NOMEM;
		}

		fonts = temp;

		fonts[n_fonts] = NULL;
		
		if (value == CSS_FONT_FAMILY_INHERIT) {
			/* The stylesheet doesn't specify a generic family,
			 * but it has specified named fonts.
			 * Fall back to the user agent's default family.
			 * We don't want to inherit, because that will 
			 * incorrectly overwrite the named fonts list too.
			 */
			css_hint hint;
			css_error error;
			
			error = state->handler->ua_default_for_property(
					state->pw, CSS_PROP_FONT_FAMILY, &hint);
			if (error == CSS_OK) {
				lwc_string **item;

				value = hint.status;
		
				for (item = hint.data.strings; 
						item != NULL && (*item) != NULL;
						item++) {
					lwc_string_unref(*item);
				}

				if (hint.data.strings != NULL) {
					state->computed->alloc(
							hint.data.strings, 
							0, state->computed->pw);
				}
			}

			if (value == CSS_FONT_FAMILY_INHERIT) {
				/* No sane UA default: assume sans-serif */
				value = CSS_FONT_FAMILY_SANS_SERIF;
			}
		}
	}

	if (css__outranks_existing(getOpcode(opv), isImportant(opv), state,
			isInherit(opv))) {
		css_error error;

		error = set_font_family(state->computed, value, fonts);
		if (error != CSS_OK && n_fonts > 0)
			state->computed->alloc(fonts, 0, state->computed->pw);

		return error;
	} else {
		if (n_fonts > 0)
			state->computed->alloc(fonts, 0, state->computed->pw);
	}

	return CSS_OK;
}

css_error css__set_font_family_from_hint(const css_hint *hint,
		css_computed_style *style)
{
	lwc_string **item;
	css_error error;

	error = set_font_family(style, hint->status, hint->data.strings);

	for (item = hint->data.strings; 
			item != NULL && (*item) != NULL; item++) {
		lwc_string_unref(*item);
	}

	if (error != CSS_OK && hint->data.strings != NULL)
		style->alloc(hint->data.strings, 0, style->pw);

	return error;
}

css_error css__initial_font_family(css_select_state *state)
{
	css_hint hint;
	css_error error;

	error = state->handler->ua_default_for_property(state->pw,
			CSS_PROP_FONT_FAMILY, &hint);
	if (error != CSS_OK)
		return error;

	return css__set_font_family_from_hint(&hint, state->computed);
}

css_error css__compose_font_family(const css_computed_style *parent,
		const css_computed_style *child,
		css_computed_style *result)
{
	css_error error;
	lwc_string **names = NULL;
	uint8_t type = get_font_family(child, &names);

	if (type == CSS_FONT_FAMILY_INHERIT || result != child) {
		size_t n_names = 0;
		lwc_string **copy = NULL;

		if (type == CSS_FONT_FAMILY_INHERIT)
			type = get_font_family(parent, &names);

		if (names != NULL) {
			lwc_string **i;

			for (i = names; (*i) != NULL; i++)
				n_names++;

			copy = result->alloc(NULL, (n_names + 1) * 
					sizeof(lwc_string *),
					result->pw);
			if (copy == NULL)
				return CSS_NOMEM;

			memcpy(copy, names, (n_names + 1) * 
					sizeof(lwc_string *));
		}

		error = set_font_family(result, type, copy);
		if (error != CSS_OK && copy != NULL)
			result->alloc(copy, 0, result->pw);

		return error;
	}

	return CSS_OK;
}

