/*****************************************************************************/
// BaseTranslator
// Written by Michael Wilber, Haiku Translation Kit Team
//
// BaseTranslator.cpp
//
// The BaseTranslator class implements functionality common to most
// Translators so that this functionality need not be implemented over and
// over in each Translator.
//
//
// Copyright (c) 2004 Haiku, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
/*****************************************************************************/

#include "BaseTranslator.h"

#include <string.h>
#include <stdio.h>

#include <algorithm>

#include <Catalog.h>
#include <Locale.h>


#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "BaseTranslator"


// ---------------------------------------------------------------
// Constructor
//
// Sets up the version info and the name of the translator so that
// these values can be returned when they are requested.
//
// Preconditions:
//
// Parameters:
//
// Postconditions:
//
// Returns:
// ---------------------------------------------------------------
BaseTranslator::BaseTranslator(const char *name, const char *info,
	const int32 version, const translation_format *inFormats,
	int32 inCount, const translation_format *outFormats, int32 outCount,
	const char *settingsFile, const TranSetting *defaults, int32 defCount,
	uint32 tranGroup, uint32 tranType)
	:
	BTranslator()
{
	fSettings = new TranslatorSettings(settingsFile, defaults, defCount);
	fSettings->LoadSettings();
		// load settings from the Base Translator settings file

	fVersion = version;
	fName = new char[strlen(name) + 1];
	strcpy(fName, name);
	fInfo = new char[strlen(info) + 41];
	sprintf(fInfo, "%s v%d.%d.%d %s", info,
		static_cast<int>(B_TRANSLATION_MAJOR_VERSION(fVersion)),
		static_cast<int>(B_TRANSLATION_MINOR_VERSION(fVersion)),
		static_cast<int>(B_TRANSLATION_REVISION_VERSION(fVersion)),
		__DATE__);

	fInputFormats = inFormats;
	fInputCount = (fInputFormats) ? inCount : 0;
	fOutputFormats = outFormats;
	fOutputCount = (fOutputFormats) ? outCount : 0;
	fTranGroup = tranGroup;
	fTranType = tranType;
}


// ---------------------------------------------------------------
// Destructor
//
// Does nothing
//
// Preconditions:
//
// Parameters:
//
// Postconditions:
//
// Returns:
// ---------------------------------------------------------------
//
// NOTE: It may be the case, that under Be's libtranslation.so,
// that this destructor will never be called
BaseTranslator::~BaseTranslator()
{
	fSettings->Release();
	delete[] fName;
	delete[] fInfo;
}


// ---------------------------------------------------------------
// TranslatorName
//
// Returns the short name of the translator.
//
// Preconditions:
//
// Parameters:
//
// Postconditions:
//
// Returns: a const char * to the short name of the translator
// ---------------------------------------------------------------
const char *
BaseTranslator::TranslatorName() const
{
	return fName;
}


// ---------------------------------------------------------------
// TranslatorInfo
//
// Returns a more verbose name for the translator than the one
// TranslatorName() returns. This usually includes version info.
//
// Preconditions:
//
// Parameters:
//
// Postconditions:
//
// Returns: a const char * to the verbose name of the translator
// ---------------------------------------------------------------
const char *
BaseTranslator::TranslatorInfo() const
{
	return fInfo;
}


// ---------------------------------------------------------------
// TranslatorVersion
//
// Returns the integer representation of the current version of
// this translator.
//
// Preconditions:
//
// Parameters:
//
// Postconditions:
//
// Returns:
// ---------------------------------------------------------------
int32
BaseTranslator::TranslatorVersion() const
{
	return fVersion;
}


// ---------------------------------------------------------------
// InputFormats
//
// Returns a list of input formats supported by this translator.
//
// Preconditions:
//
// Parameters:	out_count,	The number of input formats
//							support is returned here.
//
// Postconditions:
//
// Returns: the array of input formats and the number of input
// formats through the out_count parameter
// ---------------------------------------------------------------
const translation_format *
BaseTranslator::InputFormats(int32 *out_count) const
{
	if (out_count) {
		*out_count = fInputCount;
		return fInputFormats;
	} else
		return NULL;
}


// ---------------------------------------------------------------
// OutputFormats
//
// Returns a list of output formats supported by this translator.
//
// Preconditions:
//
// Parameters:	out_count,	The number of output formats
//							support is returned here.
//
// Postconditions:
//
// Returns: the array of output formats and the number of output
// formats through the out_count parameter
// ---------------------------------------------------------------
const translation_format *
BaseTranslator::OutputFormats(int32 *out_count) const
{
	if (out_count) {
		*out_count = fOutputCount;
		return fOutputFormats;
	} else
		return NULL;
}


// ---------------------------------------------------------------
// identify_bits_header
//
// Determines if the data in inSource is in the
// B_TRANSLATOR_BITMAP ('bits') format. If it is, it returns
// info about the data in inSource to outInfo and pheader.
//
// Preconditions:
//
// Parameters:	inSource,	The source of the image data
//
//				outInfo,	Information about the translator
//							is copied here
//
//				amtread,	Amount of data read from inSource
//							before this function was called
//
//				read,		Pointer to the data that was read
// 							in before this function was called
//
//				pheader,	The bits header is copied here after
//							it is read in from inSource
//
// Postconditions:
//
// Returns: B_NO_TRANSLATOR,	if the data does not look like
//								bits format data
//
// B_ERROR,	if the header data could not be converted to host
//			format
//
// B_OK,	if the data looks like bits data and no errors were
//			encountered
// ---------------------------------------------------------------
status_t
BaseTranslator::identify_bits_header(BPositionIO *inSource,
	translator_info *outInfo, TranslatorBitmap *pheader)
{
	TranslatorBitmap header;

	// read in the header
	ssize_t size = sizeof(TranslatorBitmap);
	if (inSource->Read(
		(reinterpret_cast<uint8 *> (&header)), size) != size)
		return B_NO_TRANSLATOR;

	// convert to host byte order
	if (swap_data(B_UINT32_TYPE, &header, sizeof(TranslatorBitmap),
		B_SWAP_BENDIAN_TO_HOST) != B_OK)
		return B_ERROR;

	// check if header values are reasonable
	if (header.colors != B_RGB32 &&
		header.colors != B_RGB32_BIG &&
		header.colors != B_RGBA32 &&
		header.colors != B_RGBA32_BIG &&
		header.colors != B_RGB24 &&
		header.colors != B_RGB24_BIG &&
		header.colors != B_RGB16 &&
		header.colors != B_RGB16_BIG &&
		header.colors != B_RGB15 &&
		header.colors != B_RGB15_BIG &&
		header.colors != B_RGBA15 &&
		header.colors != B_RGBA15_BIG &&
		header.colors != B_CMAP8 &&
		header.colors != B_GRAY8 &&
		header.colors != B_GRAY1 &&
		header.colors != B_CMYK32 &&
		header.colors != B_CMY32 &&
		header.colors != B_CMYA32 &&
		header.colors != B_CMY24)
		return B_NO_TRANSLATOR;
	if (header.rowBytes * (header.bounds.Height() + 1) != header.dataSize)
		return B_NO_TRANSLATOR;

	if (outInfo) {
		outInfo->type = B_TRANSLATOR_BITMAP;
		outInfo->group = B_TRANSLATOR_BITMAP;
		outInfo->quality = 0.2;
		outInfo->capability = 0.2;
		strlcpy(outInfo->name, B_TRANSLATE("Be Bitmap Format"),
			sizeof(outInfo->name));
		strcpy(outInfo->MIME, "image/x-be-bitmap");

		// Look for quality / capability info in fInputFormats
		for (int32 i = 0; i < fInputCount; i++) {
			if (fInputFormats[i].type == B_TRANSLATOR_BITMAP &&
				fInputFormats[i].group == B_TRANSLATOR_BITMAP) {
				outInfo->quality = fInputFormats[i].quality;
				outInfo->capability = fInputFormats[i].capability;
				strcpy(outInfo->name, fInputFormats[i].name);
				break;
			}
		}
	}

	if (pheader) {
		pheader->magic = header.magic;
		pheader->bounds = header.bounds;
		pheader->rowBytes = header.rowBytes;
		pheader->colors = header.colors;
		pheader->dataSize = header.dataSize;
	}

	return B_OK;
}


// ---------------------------------------------------------------
// BitsCheck
//
// Examines the input stream for B_TRANSLATOR_BITMAP format
// information and determines if BaseTranslator can handle
// the translation entirely, if it must pass the task of
// translation to the derived translator or if the stream cannot
// be decoded by the BaseTranslator or the derived translator.
//
// Preconditions:
//
// Parameters:	inSource,	where the data to examine is
//
//				ioExtension,	configuration settings for the
//								translator
//
//				outType,	The format that the user wants
//							the data in inSource to be
//							converted to. NOTE: This is passed by
//							reference so that it can modify the
//							outType that is seen by the
//							BaseTranslator and the derived
//							translator
//
// Postconditions:
//
// Returns: B_NO_TRANSLATOR,	if this translator can't handle
//								the data in inSource
//
// B_ERROR,	if there was an error converting the data to the host
//			format
//
// B_BAD_VALUE, if the settings in ioExtension are bad
//
// B_OK,	if this translator understand the data and there were
//			no errors found
// ---------------------------------------------------------------
status_t
BaseTranslator::BitsCheck(BPositionIO *inSource, BMessage *ioExtension,
	uint32 &outType)
{
	if (!outType)
		outType = B_TRANSLATOR_BITMAP;
	if (outType != B_TRANSLATOR_BITMAP && outType != fTranType)
		return B_NO_TRANSLATOR;

	// Convert the magic numbers to the various byte orders so that
	// I won't have to convert the data read in to see whether or not
	// it is a supported type
	const uint32 kBitsMagic = B_HOST_TO_BENDIAN_INT32(B_TRANSLATOR_BITMAP);

	// Read in the magic number and determine if it
	// is a supported type
	uint8 ch[4];
	if (inSource->Read(ch, 4) != 4)
		return B_NO_TRANSLATOR;
	inSource->Seek(-4, SEEK_CUR);
		// seek backward becuase functions used after this one
		// expect the stream to be at the beginning

	// Read settings from ioExtension
	if (ioExtension && fSettings->LoadSettings(ioExtension) < B_OK)
		return B_BAD_VALUE;

	uint32 sourceMagic;
	memcpy(&sourceMagic, ch, sizeof(uint32));
	if (sourceMagic == kBitsMagic)
		return B_OK;
	return B_OK + 1;
}


status_t
BaseTranslator::BitsIdentify(BPositionIO *inSource,
	const translation_format *inFormat, BMessage *ioExtension,
	translator_info *outInfo, uint32 outType)
{
	status_t result = BitsCheck(inSource, ioExtension, outType);
	if (result == B_OK) {
		TranslatorBitmap bitmap;
		result = identify_bits_header(inSource, outInfo, &bitmap);
		if (result == B_OK)
			result = DerivedCanHandleImageSize(bitmap.bounds.Width() + 1.0,
				bitmap.bounds.Height() + 1.0);
	} else if (result >= B_OK) {
		// if NOT B_TRANSLATOR_BITMAP, it could be an image in the
		// derived format
		result = DerivedIdentify(inSource, inFormat, ioExtension,
			outInfo, outType);
	}
	return result;
}


// ---------------------------------------------------------------
// Identify
//
// Examines the data from inSource and determines if it is in a
// format that this translator knows how to work with.
//
// Preconditions:
//
// Parameters:	inSource,	where the data to examine is
//
//				inFormat,	a hint about the data in inSource,
//							it is ignored since it is only a hint
//
//				ioExtension,	configuration settings for the
//								translator
//
//				outInfo,	information about what data is in
//							inSource and how well this translator
//							can handle that data is stored here
//
//				outType,	The format that the user wants
//							the data in inSource to be
//							converted to
//
// Postconditions:
//
// Returns: B_NO_TRANSLATOR,	if this translator can't handle
//								the data in inSource
//
// B_ERROR,	if there was an error converting the data to the host
//			format
//
// B_BAD_VALUE, if the settings in ioExtension are bad
//
// B_OK,	if this translator understand the data and there were
//			no errors found
// ---------------------------------------------------------------
status_t
BaseTranslator::Identify(BPositionIO *inSource,
	const translation_format *inFormat, BMessage *ioExtension,
	translator_info *outInfo, uint32 outType)
{
	switch (fTranGroup) {
		case B_TRANSLATOR_BITMAP:
			return BitsIdentify(inSource, inFormat, ioExtension,
				outInfo, outType);

		default:
			return DerivedIdentify(inSource, inFormat, ioExtension,
				outInfo, outType);
	}
}


// ---------------------------------------------------------------
// translate_from_bits_to_bits
//
// Convert the data in inSource from the Be Bitmap format ('bits')
// to the format specified in outType (either bits or Base).
//
// Preconditions:
//
// Parameters:	inSource,	the bits data to translate
//
// 				amtread,	the amount of data already read from
//							inSource
//
//				read,		pointer to the data already read from
//							inSource
//
//				outType,	the type of data to convert to
//
//				outDestination,	where the output is written to
//
// Postconditions:
//
// Returns: B_NO_TRANSLATOR,	if the data is not in a supported
//								format
//
// B_ERROR, if there was an error allocating memory or some other
//			error
//
// B_OK, if successfully translated the data from the bits format
// ---------------------------------------------------------------
status_t
BaseTranslator::translate_from_bits_to_bits(BPositionIO *inSource,
	uint32 outType,	BPositionIO *outDestination)
{
	TranslatorBitmap bitsHeader;
	bool bheaderonly = false, bdataonly = false;

	status_t result;
	result = identify_bits_header(inSource, NULL, &bitsHeader);
	if (result != B_OK)
		return result;

	// Translate B_TRANSLATOR_BITMAP to B_TRANSLATOR_BITMAP, easy enough :)
	if (outType == B_TRANSLATOR_BITMAP) {
		// write out bitsHeader (only if configured to)
		if (bheaderonly || (!bheaderonly && !bdataonly)) {
			if (swap_data(B_UINT32_TYPE, &bitsHeader,
				sizeof(TranslatorBitmap), B_SWAP_HOST_TO_BENDIAN) != B_OK)
				return B_ERROR;
			if (outDestination->Write(&bitsHeader,
				sizeof(TranslatorBitmap)) != sizeof(TranslatorBitmap))
				return B_ERROR;
		}

		// write out the data (only if configured to)
		if (bdataonly || (!bheaderonly && !bdataonly)) {
			uint8 buf[1024];
			uint32 remaining = B_BENDIAN_TO_HOST_INT32(bitsHeader.dataSize);
			ssize_t rd, writ;
			rd = inSource->Read(buf, 1024);
			while (rd > 0) {
				writ = outDestination->Write(buf, rd);
				if (writ < 0)
					break;
				remaining -= static_cast<uint32>(writ);
				rd = inSource->Read(buf, std::min((uint32)1024,
					remaining));
			}

			if (remaining > 0)
				return B_ERROR;
			else
				return B_OK;
		} else
			return B_OK;

	} else
		return B_NO_TRANSLATOR;
}


status_t
BaseTranslator::BitsTranslate(BPositionIO *inSource,
	const translator_info *inInfo, BMessage *ioExtension, uint32 outType,
	BPositionIO *outDestination)
{
	status_t result = BitsCheck(inSource, ioExtension, outType);
	if (result == B_OK && outType == B_TRANSLATOR_BITMAP) {
		result = translate_from_bits_to_bits(inSource, outType,
			outDestination);
	} else if (result >= B_OK) {
		// If NOT B_TRANSLATOR_BITMAP type it could be the derived format
		result = DerivedTranslate(inSource, inInfo, ioExtension, outType,
			outDestination, (result == B_OK));
	}
	return result;
}


// ---------------------------------------------------------------
// Translate
//
// Translates the data in inSource to the type outType and stores
// the translated data in outDestination.
//
// Preconditions:
//
// Parameters:	inSource,	the data to be translated
//
//				inInfo,	hint about the data in inSource (not used)
//
//				ioExtension,	configuration options for the
//								translator
//
//				outType,	the type to convert inSource to
//
//				outDestination,	where the translated data is
//								put
//
// Postconditions:
//
// Returns: B_BAD_VALUE, if the options in ioExtension are bad
//
// B_NO_TRANSLATOR, if this translator doesn't understand the data
//
// B_ERROR, if there was an error allocating memory or converting
//          data
//
// B_OK, if all went well
// ---------------------------------------------------------------
status_t
BaseTranslator::Translate(BPositionIO *inSource,
	const translator_info *inInfo, BMessage *ioExtension, uint32 outType,
	BPositionIO *outDestination)
{
	switch (fTranGroup) {
		case B_TRANSLATOR_BITMAP:
			return BitsTranslate(inSource, inInfo, ioExtension, outType,
				outDestination);

		default:
			return DerivedTranslate(inSource, inInfo, ioExtension, outType,
				outDestination, -1);
	}
}


// returns the current translator settings into ioExtension
status_t
BaseTranslator::GetConfigurationMessage(BMessage *ioExtension)
{
	return fSettings->GetConfigurationMessage(ioExtension);
}


// ---------------------------------------------------------------
// MakeConfigurationView
//
// Makes a BView object for configuring / displaying info about
// this translator.
//
// Preconditions:
//
// Parameters:	ioExtension,	configuration options for the
//								translator
//
//				outView,		the view to configure the
//								translator is stored here
//
//				outExtent,		the bounds of the view are
//								stored here
//
// Postconditions:
//
// Returns:
// ---------------------------------------------------------------
status_t
BaseTranslator::MakeConfigurationView(BMessage *ioExtension, BView **outView,
	BRect *outExtent)
{
	if (!outView || !outExtent)
		return B_BAD_VALUE;
	if (ioExtension && fSettings->LoadSettings(ioExtension) != B_OK)
		return B_BAD_VALUE;

	BView *view = NewConfigView(AcquireSettings());
		// implemented in derived class

	if (view) {
		*outView = view;
		if ((view->Flags() & B_SUPPORTS_LAYOUT) != 0)
			view->ResizeTo(view->ExplicitPreferredSize());

		*outExtent = view->Bounds();

		return B_OK;
	} else
		return BTranslator::MakeConfigurationView(ioExtension, outView,
			outExtent);
}


TranslatorSettings *
BaseTranslator::AcquireSettings()
{
	return fSettings->Acquire();
}


///////////////////////////////////////////////////////////
// Functions to be implemented by derived classes

status_t
BaseTranslator::DerivedIdentify(BPositionIO *inSource,
	const translation_format *inFormat, BMessage *ioExtension,
	translator_info *outInfo, uint32 outType)
{
	return B_NO_TRANSLATOR;
}


status_t
BaseTranslator::DerivedTranslate(BPositionIO *inSource,
	const translator_info *inInfo, BMessage *ioExtension, uint32 outType,
	BPositionIO *outDestination, int32 baseType)
{
	return B_NO_TRANSLATOR;
}


status_t
BaseTranslator::DerivedCanHandleImageSize(float width, float height) const
{
	return B_OK;
}


BView *
BaseTranslator::NewConfigView(TranslatorSettings *settings)
{
	return NULL;
}


void
translate_direct_copy(BPositionIO *inSource, BPositionIO *outDestination)
{
	const size_t kbufsize = 2048;
	uint8 buffer[kbufsize];
	ssize_t ret = inSource->Read(buffer, kbufsize);
	while (ret > 0) {
		outDestination->Write(buffer, ret);
		ret = inSource->Read(buffer, kbufsize);
	}
}