/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018
 *   Jonathan Schleifer <js@heap.zone>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
 * the packaging of this file.
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 2 or 3, which can be found in the file
 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#include "config.h"

#import "OFColor.h"

#import "OFInvalidArgumentException.h"

@implementation OFColor
+ (instancetype)colorWithRed: (float)red
		       green: (float)green
			blue: (float)blue
		       alpha: (float)alpha
{
	return [[[self alloc] initWithRed: red
				    green: green
				     blue: blue
				    alpha: alpha] autorelease];
}

- (instancetype)initWithRed: (float)red
		      green: (float)green
		       blue: (float)blue
		      alpha: (float)alpha
{
	self = [super init];

	@try {
		if (red < 0.0 || red > 1.0 ||
		    green < 0.0 || green > 1.0 ||
		    blue < 0.0 || blue > 1.0 ||
		    alpha < 0.0 || alpha > 1.0)
			@throw [OFInvalidArgumentException exception];

		_red = red;
		_green = green;
		_blue = blue;
		_alpha = alpha;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (bool)isEqual: (id)object
{
	OFColor *other;

	if (![object isKindOfClass: [OFColor class]])
		return false;

	other = object;

	if (other->_red != _red)
		return false;
	if (other->_green != _green)
		return false;
	if (other->_blue != _blue)
		return false;
	if (other->_alpha != _alpha)
		return false;

	return true;
}

- (uint32_t)hash
{
	uint32_t hash;
	union {
		float f;
		unsigned char b[sizeof(float)];
	} f;

	OF_HASH_INIT(hash);

	f.f = OF_BSWAP_FLOAT_IF_LE(_red);
	for (uint_fast8_t i = 0; i < sizeof(float); i++)
		OF_HASH_ADD(hash, f.b[i]);

	f.f = OF_BSWAP_FLOAT_IF_LE(_green);
	for (uint_fast8_t i = 0; i < sizeof(float); i++)
		OF_HASH_ADD(hash, f.b[i]);

	f.f = OF_BSWAP_FLOAT_IF_LE(_blue);
	for (uint_fast8_t i = 0; i < sizeof(float); i++)
		OF_HASH_ADD(hash, f.b[i]);

	f.f = OF_BSWAP_FLOAT_IF_LE(_alpha);
	for (uint_fast8_t i = 0; i < sizeof(float); i++)
		OF_HASH_ADD(hash, f.b[i]);

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (void)getRed: (float *)red
	 green: (float *)green
	  blue: (float *)blue
	 alpha: (float *)alpha
{
	*red = _red;
	*green = _green;
	*blue = _blue;

	if (alpha != NULL)
		*alpha = _alpha;
}
@end
