/*
 * GP.cpp
 * Copyright 1999-2000 Y.Takagi. All Rights Reserved.
 * Copyright 2010 Michael Pfeiffer.
 */
 
 
#include "GPDriver.h"
 
#include <memory>
 
#include <Alert.h>
#include <Bitmap.h>
#include <Debug.h>
#include <File.h>
 
#include "DbgMsg.h"
#include "Halftone.h"
#include "JobData.h"
#include "PackBits.h"
#include "GPCapabilities.h"
#include "GPData.h"
#include "PrinterData.h"
#include "UIDriver.h"
#include "ValidRect.h"
 
 
using namespace std;
 
 
GPDriver::GPDriver(BMessage* message, PrinterData* printerData,
	const PrinterCap* printerCap)
	:
	GraphicsDriver(message, printerData, printerCap)
{
}
 
void
GPDriver::Write(const void* buffer, size_t size)
{
	WriteSpoolData(buffer, size);
}
 
bool
GPDriver::StartDocument()
{
	try {
		const GPData* data = dynamic_cast<const GPData*>(GetPrinterData());
		ASSERT(data != NULL);
		fConfiguration.fDriver = data->fGutenprintDriverName;
 
		SetParameter(fConfiguration.fPageSize, PrinterCap::kPaper,
			GetJobData()->GetPaper());
 
		SetParameter(fConfiguration.fResolution, PrinterCap::kResolution,
			GetJobData()->GetResolutionID());
 
		fConfiguration.fXDPI = GetJobData()->GetXres();
		fConfiguration.fYDPI = GetJobData()->GetYres();
 
		SetParameter(fConfiguration.fInputSlot, PrinterCap::kPaperSource,
			GetJobData()->GetPaperSource());
 
		SetParameter(fConfiguration.fPrintingMode, PrinterCap::kColor,
			GetJobData()->GetColor());
 
		if (GetPrinterCap()->Supports(PrinterCap::kDriverSpecificCapabilities))
			SetDriverSpecificSettings();
 
		fprintf(stderr, "Driver: %s\n", fConfiguration.fDriver.String());
		fprintf(stderr, "PageSize %s\n", fConfiguration.fPageSize.String());
		fprintf(stderr, "Resolution %s\n", fConfiguration.fResolution.String());
		fprintf(stderr, "InputSlot %s\n", fConfiguration.fInputSlot.String());
		fprintf(stderr, "PrintingMode %s\n", fConfiguration.fPrintingMode.String());
 
		return fBinding.BeginJob(&fConfiguration, this) == B_OK;
	}
	catch (TransportException& err) {
		return false;
	} 
}
 
 
void
GPDriver::SetParameter(BString& parameter, PrinterCap::CapID category,
	int value)
{
	const EnumCap* capability;
	capability = GetPrinterCap()->FindCap(category, value);
	if (capability != NULL && capability->fKey != "")
		parameter = capability->Key();
}
 
 
void
GPDriver::SetDriverSpecificSettings()
{
	PrinterCap::CapID category = PrinterCap::kDriverSpecificCapabilities;
	int count = GetPrinterCap()->CountCap(category);
	const BaseCap** capabilities = GetPrinterCap()->GetCaps(category);
	for (int i = 0; i < count; i++) {
		const DriverSpecificCap* capability =
			dynamic_cast<const DriverSpecificCap*>(capabilities[i]);
		if (capability == NULL) {
			fprintf(stderr, "Internal error: DriverSpecificCap name='%s' "
				"has wrong type!\n", capabilities[i]->Label());
			continue;
		}
 
		PrinterCap::CapID id = static_cast<PrinterCap::CapID>(capability->ID());
		const char* key = capability->fKey.c_str();
		switch (capability->fType) {
			case DriverSpecificCap::kList:
				AddDriverSpecificSetting(id, key);
				break;
			case DriverSpecificCap::kBoolean:
				AddDriverSpecificBooleanSetting(id, key);
				break;
			case DriverSpecificCap::kIntRange:
				AddDriverSpecificIntSetting(id, key);
				break;
			case DriverSpecificCap::kIntDimension:
				AddDriverSpecificDimensionSetting(id, key);
				break;
			case DriverSpecificCap::kDoubleRange:
				AddDriverSpecificDoubleSetting(id, key);
				break;
		}
	}
}
 
 
void
GPDriver::AddDriverSpecificSetting(PrinterCap::CapID category, const char* key) {
	const EnumCap* capability = NULL;
	if (GetJobData()->Settings().HasString(key))
	{
		const string& value = GetJobData()->Settings().GetString(key);
		capability = GetPrinterCap()->FindCapWithKey(category, value.c_str());
	}
 
	if (capability == NULL) {
		// job data should contain a value;
		// try to use the default value anyway
		capability = GetPrinterCap()->GetDefaultCap(category);
	}
 
	if (capability == NULL) {
		// should not reach here!
		return;
	}
 
	fConfiguration.fStringSettings[key] = capability->fKey;
}
 
 
void
GPDriver::AddDriverSpecificBooleanSetting(PrinterCap::CapID category,
	const char* key) {
	if (GetJobData()->Settings().HasBoolean(key))
		fConfiguration.fBooleanSettings[key] =
			GetJobData()->Settings().GetBoolean(key);
}
 
 
void
GPDriver::AddDriverSpecificIntSetting(PrinterCap::CapID category,
	const char* key) {
	if (GetJobData()->Settings().HasInt(key))
		fConfiguration.fIntSettings[key] =
			GetJobData()->Settings().GetInt(key);
}
 
 
void
GPDriver::AddDriverSpecificDimensionSetting(PrinterCap::CapID category,
	const char* key) {
	if (GetJobData()->Settings().HasInt(key))
		fConfiguration.fDimensionSettings[key] =
			GetJobData()->Settings().GetInt(key);
}
 
 
void
GPDriver::AddDriverSpecificDoubleSetting(PrinterCap::CapID category,
	const char* key) {
	if (GetJobData()->Settings().HasDouble(key))
		fConfiguration.fDoubleSettings[key] =
			GetJobData()->Settings().GetDouble(key);
}
 
 
bool
GPDriver::StartPage(int)
{
	fBinding.BeginPage();
	return true;
}
 
 
bool
GPDriver::EndPage(int)
{
	try {
		fBinding.EndPage();
		return true;
	}
	catch (TransportException& err) {
		ShowError(err.What());
		return false;
	} 
}
 
 
bool
GPDriver::EndDocument(bool)
{
	try {
		fBinding.EndJob();
		return true;
	}
	catch (TransportException& err) {
		ShowError(err.What());
		return false;
	} 
}
 
 
bool
GPDriver::NextBand(BBitmap* bitmap, BPoint* offset)
{
	DBGMSG(("> nextBand\n"));
	try {
		BRect bounds = bitmap->Bounds();
 
		RECT rc;
		rc.left = (int)bounds.left;
		rc.top = (int)bounds.top;
		rc.right = (int)bounds.right;
		rc.bottom = (int)bounds.bottom;
 
		int height = rc.bottom - rc.top + 1;
 
		int x = (int)offset->x;
		int y = (int)offset->y;
 
		int pageHeight = GetPageHeight();
 
		if (y + height > pageHeight)
			height = pageHeight - y;
 
		rc.bottom = height - 1;
 
		DBGMSG(("height = %d\n", height));
		DBGMSG(("x = %d\n", x));
		DBGMSG(("y = %d\n", y));
 
		if (get_valid_rect(bitmap, &rc)) {
 
			DBGMSG(("validate rect = %d, %d, %d, %d\n",
				rc.left, rc.top, rc.right, rc.bottom));
 
			x = rc.left;
			y += rc.top;
 
			int width = rc.right - rc.left + 1;
			int height = rc.bottom - rc.top + 1;
			fprintf(stderr, "GPDriver nextBand x %d, y %d, width %d,"
				" height %d\n",
				x, y, width, height);
			BRect imageRect(rc.left, rc.top, rc.right, rc.bottom);
			status_t status;
			status = fBinding.AddBitmapToPage(bitmap, imageRect, BPoint(x, y));
			if (status == B_NO_MEMORY) {
				ShowError("Out of memory");
				return false;
			} else if (status != B_OK) {
				ShowError("Unknown error");
				return false;
			}
 
		} else {
			DBGMSG(("band bitmap is empty.\n"));
		}
 
		if (y >= pageHeight) {
			offset->x = -1.0;
			offset->y = -1.0;
		} else
			offset->y += height;
 
		DBGMSG(("< nextBand\n"));
		return true;
	}
	catch (TransportException& err) {
		ShowError(err.What());
		return false;
	} 
}
 
 
void
GPDriver::ShowError(const char* message)
{
	BString text;
	text << "An error occurred attempting to print with Gutenprint:";
	text << "\n";
	text << message;
	BAlert* alert = new BAlert("", text.String(), "OK");
	alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
	alert->Go();
}

V773 Visibility scope of the 'alert' pointer was exited without releasing the memory. A memory leak is possible.