/** \file backgnd.c
* Layout background image
*/

/*  XTrkCad - Model Railroad CAD
*  Copyright (C) 2018 Martin Fischer
*
*  This program 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 2 of the License, or
*  (at your option) any later version.
*
*  This program 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 General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program; if not, write to the Free Software
*  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include <windows.h>

#include <FreeImage.h>
#include "i18n.h"
#include "mswint.h"

static char *lastErrorMessage;		/**< store last message from FreeImage */
#define ERRORPUNCTUATION " : "

/**
 * FreeImage error handler
 * \param fif Format / Plugin responsible for the error
 * \param message Error message
 */

static void
HandleFreeImageError( FREE_IMAGE_FORMAT fif, const char *message )
{
	size_t totalLength = strlen( message ) + 1;

	if( fif != FIF_UNKNOWN ) {
		totalLength += strlen( FreeImage_GetFormatFromFIF( fif ) ) + strlen(
		                       ERRORPUNCTUATION );
	}

	lastErrorMessage = malloc( totalLength );

	if( fif != FIF_UNKNOWN ) {
		sprintf( lastErrorMessage,
		         "%s" ERRORPUNCTUATION "%s",
		         FreeImage_GetFormatFromFIF( fif ),
		         message );
	} else {
		strcpy( lastErrorMessage, message );
	}
}

/**
* Load the background image
* \param bd drawing context
* \param path filename for image file, if NULL the existing background will be removed
* \param error returned error message
* \return -1 unsupported or invalid file, 0 success, 1 background removed
*/

int
wDrawSetBackground( wDraw_p bd, char * path, char ** error )
{
	FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;

	FreeImage_SetOutputMessage( HandleFreeImageError );

	if( lastErrorMessage ) {
		free( lastErrorMessage );
		lastErrorMessage = NULL;
	}

	if( path ) {
		// check the file signature and deduce its format
		// (the second argument is currently not used by FreeImage)
		fif = FreeImage_GetFileType( path, 0 );

		if( fif == FIF_UNKNOWN ) {
			// no signature ?
			// try to guess the file format from the file extension
			fif = FreeImage_GetFIFFromFilename( path );
		}

		// check that the plugin has reading capabilities ...
		if( ( fif != FIF_UNKNOWN ) && FreeImage_FIFSupportsReading( fif ) ) {
			// ok, let's load the file
			bd->background = FreeImage_Load( fif, path, 0 );

			// unless a bad file format, we are done !
			if( !bd->background ) {
				*error = lastErrorMessage;
				return ( -1 );
			} else {
				return ( 0 );
			}
		} else {
			*error = _strdup( _( "Image file is invalid or cannot be read." ) );
			return ( -1 );
		}
	} else {
		if( bd->background ) {
			FreeImage_Unload( bd->background );
			bd->background = 0;
		}

		return ( 1 );
	}
}
/**
 * Use a loaded background in another context.
 *
 * \param from  context with background
 * \param to    context to get a reference to the existing background
 */

void
wDrawCloneBackground( wDraw_p from, wDraw_p to )
{
	if( from->background ) {
		to->background = from->background;
	} else {
		to->background = NULL;
	}
}

/**
* Draw background to screen. The background will be sized and rotated before being shown. The bitmap
* is scaled so that the width is equal to size. The height is changed proportionally.
*
* \param bd drawing context
* \param pos_x, pos_y bitmap position
* \param size desired width after scaling
* \param angle
* \param screen visibility of bitmap in percent
*/

void
wDrawShowBackground( wDraw_p bd, wWinPix_t pos_x, wWinPix_t pos_y,
                     wWinPix_t size,
                     wAngle_t angle, int screen )
{
	if( bd->background ) {
		double scale;
		FIBITMAP *tmp;
		FIBITMAP *rotated;

		if( size == 0 ) {
			scale = 1.0;
		} else {
			scale = ( double )size / FreeImage_GetWidth( bd->background );
		}

		tmp = FreeImage_RescaleRect( bd->background,
		                             ( int )( ( double )FreeImage_GetWidth( bd->background ) * scale ),
		                             ( int )( ( double )FreeImage_GetHeight( bd->background ) * scale ),
		                             0,
		                             0,
		                             FreeImage_GetWidth( bd->background ),
		                             FreeImage_GetHeight( bd->background ),
		                             FILTER_BILINEAR,
		                             0 );

		if( tmp == NULL ) {
			return;
		}

		FreeImage_AdjustColors( tmp, screen, -screen, 1.0, FALSE );
		FREE_IMAGE_TYPE image_type = FreeImage_GetImageType( tmp );

		switch( image_type ) {
		case FIT_BITMAP:
			switch( FreeImage_GetBPP( tmp ) ) {
			case 8: {
				BYTE color = 255;
				rotated = FreeImage_Rotate( tmp, angle, &color );
			}
			break;

			case 24: // we could also use 'RGBTRIPLE color' here
			case 32: {
				RGBQUAD color = { 255, 255, 255, 0 };
				// for 24-bit images, the first 3 bytes will be read
				// for 32-bit images, the first 4 bytes will be read
				rotated = FreeImage_Rotate( tmp, angle, &color );
			}
			break;
			}

			break;

		case FIT_UINT16: {
			WORD color = 255;
			rotated = FreeImage_Rotate( tmp, angle, &color );
		}
		break;

		case FIT_RGB16: // we could also use 'FIRGB16 color' here
		case FIT_RGBA16: {
			FIRGBA16 color = { 255, 255, 255, 0 };
			// for RGB16 images, the first 3 WORD will be read
			// for RGBA16 images, the first 4 WORD will be read
			rotated = FreeImage_Rotate( tmp, angle, &color );
		}
		break;

		case FIT_FLOAT: {
			float color = 1.0F;
			rotated = FreeImage_Rotate( tmp, angle, &color );
		}
		break;

		case FIT_RGBF: // we could also use 'FIRGBF color' here
		case FIT_RGBAF: {
			FIRGBAF color = { 1, 1, 1, 0 };
			// for RGBF images, the first 3 float will be read
			// for RGBAF images, the first 4 float will be read
			rotated = FreeImage_Rotate( tmp, angle, &color );
		}
		break;
		}

		SetDIBitsToDevice( bd->hDc,
		                   pos_x,
		                   bd->h - pos_y - FreeImage_GetHeight( rotated ),
		                   FreeImage_GetWidth( rotated ),
		                   FreeImage_GetHeight( rotated ),
		                   0, 0,
		                   0,
		                   FreeImage_GetHeight( rotated ),
		                   FreeImage_GetBits( rotated ),
		                   FreeImage_GetInfo( rotated ),
		                   DIB_RGB_COLORS );
		FreeImage_Unload( tmp );
		FreeImage_Unload( rotated );
	}
}