/*
 * Copyright 2015 The Etc2Comp Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
EtcDifferentialTrys.cpp

Gathers the results of the various encoding trys for both halves of a 4x4 block for Differential mode

*/

#include "EtcConfig.h"
#include "EtcDifferentialTrys.h"

#include <assert.h>

namespace Etc
{

	// ----------------------------------------------------------------------------------------------------
	// construct a list of trys (encoding attempts)
	//
	// a_frgbaColor1 is the basecolor for the first half
	// a_frgbaColor2 is the basecolor for the second half
	// a_pauiPixelMapping1 is the pixel order for the first half
	// a_pauiPixelMapping2 is the pixel order for the second half
	// a_uiRadius is the amount to vary the base colors
	//
	DifferentialTrys::DifferentialTrys(ColorFloatRGBA a_frgbaColor1, ColorFloatRGBA a_frgbaColor2,
										const unsigned int *a_pauiPixelMapping1,
										const unsigned int *a_pauiPixelMapping2,
										unsigned int a_uiRadius,
										int a_iGrayOffset1, int a_iGrayOffset2)
	{
		assert(a_uiRadius <= MAX_RADIUS);

		m_boolSeverelyBentColors = false;

		ColorFloatRGBA frgbaQuantizedColor1 = a_frgbaColor1.QuantizeR5G5B5();
		ColorFloatRGBA frgbaQuantizedColor2 = a_frgbaColor2.QuantizeR5G5B5();

		// quantize base colors
		// ensure that trys with a_uiRadius don't overflow
		int iRed1 = MoveAwayFromEdge(frgbaQuantizedColor1.IntRed(31.0f)+a_iGrayOffset1, a_uiRadius);
		int iGreen1 = MoveAwayFromEdge(frgbaQuantizedColor1.IntGreen(31.0f) + a_iGrayOffset1, a_uiRadius);
		int iBlue1 = MoveAwayFromEdge(frgbaQuantizedColor1.IntBlue(31.0f) + a_iGrayOffset1, a_uiRadius);
		int iRed2 = MoveAwayFromEdge(frgbaQuantizedColor2.IntRed(31.0f) + a_iGrayOffset2, a_uiRadius);
		int iGreen2 = MoveAwayFromEdge(frgbaQuantizedColor2.IntGreen(31.0f) + a_iGrayOffset2, a_uiRadius);
		int iBlue2 = MoveAwayFromEdge(frgbaQuantizedColor2.IntBlue(31.0f) + a_iGrayOffset2, a_uiRadius);

		int iDeltaRed = iRed2 - iRed1;
		int iDeltaGreen = iGreen2 - iGreen1;
		int iDeltaBlue = iBlue2 - iBlue1;

		// make sure components are within range
		{
			if (iDeltaRed > 3)
			{
				if (iDeltaRed > 7)
				{
					m_boolSeverelyBentColors = true;
				}

				iRed1 += (iDeltaRed - 3) / 2;
				iRed2 = iRed1 + 3;
				iDeltaRed = 3;
			}
			else if (iDeltaRed < -4)
			{
				if (iDeltaRed < -8)
				{
					m_boolSeverelyBentColors = true;
				}

				iRed1 += (iDeltaRed + 4) / 2;
				iRed2 = iRed1 - 4;
				iDeltaRed = -4;
			}
			assert(iRed1 >= (signed)(0 + a_uiRadius) && iRed1 <= (signed)(31 - a_uiRadius));
			assert(iRed2 >= (signed)(0 + a_uiRadius) && iRed2 <= (signed)(31 - a_uiRadius));
			assert(iDeltaRed >= -4 && iDeltaRed <= 3);

			if (iDeltaGreen > 3)
			{
				if (iDeltaGreen > 7)
				{
					m_boolSeverelyBentColors = true;
				}

				iGreen1 += (iDeltaGreen - 3) / 2;
				iGreen2 = iGreen1 + 3;
				iDeltaGreen = 3;
			}
			else if (iDeltaGreen < -4)
			{
				if (iDeltaGreen < -8)
				{
					m_boolSeverelyBentColors = true;
				}

				iGreen1 += (iDeltaGreen + 4) / 2;
				iGreen2 = iGreen1 - 4;
				iDeltaGreen = -4;
			}
			assert(iGreen1 >= (signed)(0 + a_uiRadius) && iGreen1 <= (signed)(31 - a_uiRadius));
			assert(iGreen2 >= (signed)(0 + a_uiRadius) && iGreen2 <= (signed)(31 - a_uiRadius));
			assert(iDeltaGreen >= -4 && iDeltaGreen <= 3);

			if (iDeltaBlue > 3)
			{
				if (iDeltaBlue > 7)
				{
					m_boolSeverelyBentColors = true;
				}

				iBlue1 += (iDeltaBlue - 3) / 2;
				iBlue2 = iBlue1 + 3;
				iDeltaBlue = 3;
			}
			else if (iDeltaBlue < -4)
			{
				if (iDeltaBlue < -8)
				{
					m_boolSeverelyBentColors = true;
				}

				iBlue1 += (iDeltaBlue + 4) / 2;
				iBlue2 = iBlue1 - 4;
				iDeltaBlue = -4;
			}
			assert(iBlue1 >= (signed)(0+a_uiRadius) && iBlue1 <= (signed)(31 - a_uiRadius));
			assert(iBlue2 >= (signed)(0 + a_uiRadius) && iBlue2 <= (signed)(31 - a_uiRadius));
			assert(iDeltaBlue >= -4 && iDeltaBlue <= 3);
		}

		m_half1.Init(iRed1, iGreen1, iBlue1, a_pauiPixelMapping1, a_uiRadius);
		m_half2.Init(iRed2, iGreen2, iBlue2, a_pauiPixelMapping2, a_uiRadius);

	}

	// ----------------------------------------------------------------------------------------------------
	//
	void DifferentialTrys::Half::Init(int a_iRed, int a_iGreen, int a_iBlue, 
										const unsigned int *a_pauiPixelMapping, unsigned int a_uiRadius)
	{

		m_iRed = a_iRed;
		m_iGreen = a_iGreen;
		m_iBlue = a_iBlue;

		m_pauiPixelMapping = a_pauiPixelMapping;
		m_uiRadius = a_uiRadius;

		m_uiTrys = 0;

	}

	// ----------------------------------------------------------------------------------------------------
	//

} // namespace Etc