void spectrum_beta128_device::device_start()
{
	save_item(NAME(m_romcs));
	save_item(NAME(m_control));
	save_item(NAME(m_motor_active));
	save_item(NAME(m_128rom_bit));
}

// copyright-holders:Nigel Barnes
/*********************************************************************

    Technology Research Beta 128 Disk interface

    This hardware type runs TR-DOS 5.xx (official) and newer
    unofficial updates.  It was designed to work properly with
    the 128k machines that had issues with the original Beta Disk
    due to changes in the 128k ROM structure etc. (enable address
    is moved from 3cxx to 3dxx for example)

    TODO:
    original ROMs should have bits 0 and 7 swapped

    there were many unofficial ROMs available for this, make them
    available for use.

*********************************************************************/

#include "emu.h"
#include "beta128.h"

/***************************************************************************
    DEVICE DEFINITIONS
***************************************************************************/

DEFINE_DEVICE_TYPE(SPECTRUM_BETA128, spectrum_beta128_device, "spectrum_beta128", "TR Beta 128 Disk Interface")

INPUT_PORTS_START(beta128)
	PORT_START("BUTTON") // don't use F12, it clashes with the 'exit from debugger' button
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("Magic Button") PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHANGED_MEMBER(DEVICE_SELF, spectrum_beta128_device, magic_button, 0)

	PORT_START("SWITCH")
	PORT_CONFNAME(0x03, 0x01, "System Switch") //PORT_CHANGED_MEMBER(DEVICE_SELF, spectrum_beta128_device, switch_changed, 0)
	PORT_CONFSETTING(0x00, "Off (128)")
	PORT_CONFSETTING(0x01, "Normal (auto-boot)") // also enable Beta-disk V3/V4 compatibility, auto-boot feature does not work on Spectrum128.
	//PORT_CONFSETTING(0x02, "Reset") // TODO: implement RESET callback
INPUT_PORTS_END

//  input_ports - device-specific input ports

ioport_constructor spectrum_beta128_device::device_input_ports() const
{
	return INPUT_PORTS_NAME(beta128);
}

static void beta_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}

//  floppy_format_type floppy_formats

void spectrum_beta128_device::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_TRD_FORMAT);
}

ROM_START(beta128)
	ROM_REGION(0x4000, "rom", 0)
	ROM_DEFAULT_BIOS("trd503")

	// original, but in plain form, should be replaced with "proper dumps" with data bits 0 and 7 swapped
	ROM_SYSTEM_BIOS(0, "trd501", "TR-DOS v5.01")
	ROMX_LOAD("trd501.rom", 0x0000, 0x4000, CRC(3e3cdd4c) SHA1(8303ba0cc79daa6c04cd1e6ce27e8b6886a3f0de), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "trd502", "TR-DOS v5.02")
	ROMX_LOAD("trd502.rom", 0x0000, 0x4000, CRC(64f0fcf8) SHA1(862e0af2245f68fca3d6a5b10186d09fd1faee55), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "trd503", "TR-DOS v5.03")
	ROMX_LOAD("trd503.rom", 0x0000, 0x4000, CRC(10751aba) SHA1(21695e3f2a8f796386ce66eea8a246b0ac44810c), ROM_BIOS(2))

	// clone/homebrew modifications based on original v5.03
	ROM_SYSTEM_BIOS(3, "trd504t", "TR-DOS v5.04T (hack)")
	// increased step rate (6ms), FORMAT command got interleave 1:1 option for faster read/write speed, this firmware was most common at post-soviet space in 90x.
	ROMX_LOAD("trd504t.rom", 0x0000, 0x4000, CRC(e212d1e0) SHA1(745e9caf576e64a5386ad845256d28593d34cc40), ROM_BIOS(3))
	// trd504.rom CRC ba310874 is bad dump of 5.03 with edited version text, no actual code changes.
ROM_END

//  device_add_mconfig - add device configuration

void spectrum_beta128_device::device_add_mconfig(machine_config &config)
{
	FD1793(config, m_fdc, 4_MHz_XTAL / 4);
	m_fdc->hld_wr_callback().set(FUNC(spectrum_beta128_device::fdc_hld_w));

	FLOPPY_CONNECTOR(config, "fdc:0", beta_floppies, "525qd", spectrum_beta128_device::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", beta_floppies, "525qd", spectrum_beta128_device::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:2", beta_floppies, nullptr, spectrum_beta128_device::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:3", beta_floppies, nullptr, spectrum_beta128_device::floppy_formats).enable_sound(true);

	// passthru
	SPECTRUM_EXPANSION_SLOT(config, m_exp, spectrum_expansion_devices, nullptr);
	m_exp->irq_handler().set(DEVICE_SELF_OWNER, FUNC(spectrum_expansion_slot_device::irq_w));
	m_exp->nmi_handler().set(DEVICE_SELF_OWNER, FUNC(spectrum_expansion_slot_device::nmi_w));
}

const tiny_rom_entry *spectrum_beta128_device::device_rom_region() const
{
	return ROM_NAME(beta128);
}

//  LIVE DEVICE

spectrum_beta128_device::spectrum_beta128_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, SPECTRUM_BETA128, tag, owner, clock)
	, device_spectrum_expansion_interface(mconfig, *this)
	, m_rom(*this, "rom")
	, m_fdc(*this, "fdc")
	, m_floppy(*this, "fdc:%u", 0)
	, m_exp(*this, "exp")
	, m_switch(*this, "SWITCH")
	, m_control(0)
	, m_motor_active(false)
{
}

//  device_start - device-specific startup

//  device_reset - device-specific reset

void spectrum_beta128_device::device_reset()
{
	// Page in the ROM if auto-boot is selected
	if (m_switch->read() == 0x01)
		m_romcs = 1;
	else
		m_romcs = 0;

	m_128rom_bit = true;
}

//  IMPLEMENTATION

READ_LINE_MEMBER(spectrum_beta128_device::romcs)
{
	return m_romcs | m_exp->romcs();
}

void spectrum_beta128_device::pre_opcode_fetch(offs_t offset)
{
	m_exp->pre_opcode_fetch(offset);

	if (!machine().side_effects_disabled())
	{
		u8 offs = offset >> 8;

		if (offs == 0x3d && m_128rom_bit)
			m_romcs = 1;
		else if (offs == 0x3c && m_128rom_bit && m_switch->read() == 0x01)
			m_romcs = 1;
		else if (offs >= 0x40)
			m_romcs = 0;
	}
}

uint8_t spectrum_beta128_device::iorq_r(offs_t offset)
{
	uint8_t data = 0xff;

	if (m_romcs)
	{
		switch (offset & 0x83)
		{
		case 0x03:
			data = m_fdc->read((offset >> 5) & 0x03);
			break;

		case 0x83:
			data &= 0x3f; // actually open bus
			data |= m_fdc->drq_r() ? 0x40 : 0;
			data |= m_fdc->intrq_r() ? 0x80 : 0;
			break;
		}
	} else
		data = m_exp->iorq_r(offset);

	return data;
}

void spectrum_beta128_device::iorq_w(offs_t offset, uint8_t data)
{
	if ((offset & 0x8002) == 0)
		m_128rom_bit = bool(data & 0x10);

	if (m_romcs)
	{
		switch (offset & 0x83)
		{
		case 0x03:
			m_fdc->write((offset >> 5) & 0x03, data);
			break;

		case 0x83:
			floppy_image_device* floppy = m_floppy[data & 3]->get_device();

			m_control = data;
			m_fdc->set_floppy(floppy);
			if (floppy)
				floppy->ss_w(BIT(data, 4) ? 0 : 1);
			m_fdc->dden_w(BIT(data, 6));

			m_fdc->hlt_w(BIT(data, 3));
			// bit 3 also connected to FDC /IP pin via diode, AND logic: if this bit is 0 - /IP will be forcibly set to low.
			// used for bitbang index pulses generation to stop FDD drive motor with no disk inserted, currently not emulated.

			m_fdc->mr_w(BIT(data, 2));
			motors_control();
			break;
		}
	}
	else
		m_exp->iorq_w(offset, data);
}

uint8_t spectrum_beta128_device::mreq_r(offs_t offset)
{
	uint8_t data = 0xff;

	if (m_romcs)
	{
		data = m_rom->base()[offset & 0x3fff];
	}

	if (m_exp->romcs())
		data &= m_exp->mreq_r(offset);

	return data;
}

INPUT_CHANGED_MEMBER(spectrum_beta128_device::magic_button)
{
	if (newval && !oldval)
	{
		m_romcs = 1;
		m_slot->nmi_w(ASSERT_LINE);
	}
	else
	{
		m_slot->nmi_w(CLEAR_LINE);
	}
}

void spectrum_beta128_device::fdc_hld_w(int state)
{
	m_fdc->set_force_ready(state); // HLD connected to RDY pin
	m_motor_active = state;
	motors_control();
}

void spectrum_beta128_device::motors_control()
{
	for (int i = 0; i < 4; i++)
	{
		floppy_image_device* floppy = m_floppy[i]->get_device();
		if (!floppy)
			continue;
		if (m_motor_active && (m_control & 3) == i)
			floppy->mon_w(CLEAR_LINE);
		else
			floppy->mon_w(ASSERT_LINE);
	}
}


switch (offset & 0x83)
		{
		case 0x03:
			data = m_fdc->read((offset >> 5) & 0x03);
			break;

		case 0x83:
			data &= 0x3f; // actually open bus
			data |= m_fdc->drq_r() ? 0x40 : 0;
			data |= m_fdc->intrq_r() ? 0x80 : 0;
			break;
		}

// copyright-holders:Nigel Barnes
/*********************************************************************

    Technology Research Beta 128 Disk interface

    This hardware type runs TR-DOS 5.xx (official) and newer
    unofficial updates.  It was designed to work properly with
    the 128k machines that had issues with the original Beta Disk
    due to changes in the 128k ROM structure etc. (enable address
    is moved from 3cxx to 3dxx for example)

    TODO:
    original ROMs should have bits 0 and 7 swapped

    there were many unofficial ROMs available for this, make them
    available for use.

*********************************************************************/

#include "emu.h"
#include "beta128.h"

/***************************************************************************
    DEVICE DEFINITIONS
***************************************************************************/

DEFINE_DEVICE_TYPE(SPECTRUM_BETA128, spectrum_beta128_device, "spectrum_beta128", "TR Beta 128 Disk Interface")

INPUT_PORTS_START(beta128)
	PORT_START("BUTTON") // don't use F12, it clashes with the 'exit from debugger' button
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("Magic Button") PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHANGED_MEMBER(DEVICE_SELF, spectrum_beta128_device, magic_button, 0)

	PORT_START("SWITCH")
	PORT_CONFNAME(0x03, 0x01, "System Switch") //PORT_CHANGED_MEMBER(DEVICE_SELF, spectrum_beta128_device, switch_changed, 0)
	PORT_CONFSETTING(0x00, "Off (128)")
	PORT_CONFSETTING(0x01, "Normal (auto-boot)") // also enable Beta-disk V3/V4 compatibility, auto-boot feature does not work on Spectrum128.
	//PORT_CONFSETTING(0x02, "Reset") // TODO: implement RESET callback
INPUT_PORTS_END

//  input_ports - device-specific input ports

ioport_constructor spectrum_beta128_device::device_input_ports() const
{
	return INPUT_PORTS_NAME(beta128);
}

static void beta_floppies(device_slot_interface &device)
{
	device.option_add("525qd", FLOPPY_525_QD);
}

//  floppy_format_type floppy_formats

void spectrum_beta128_device::floppy_formats(format_registration &fr)
{
	fr.add_mfm_containers();
	fr.add(FLOPPY_TRD_FORMAT);
}

ROM_START(beta128)
	ROM_REGION(0x4000, "rom", 0)
	ROM_DEFAULT_BIOS("trd503")

	// original, but in plain form, should be replaced with "proper dumps" with data bits 0 and 7 swapped
	ROM_SYSTEM_BIOS(0, "trd501", "TR-DOS v5.01")
	ROMX_LOAD("trd501.rom", 0x0000, 0x4000, CRC(3e3cdd4c) SHA1(8303ba0cc79daa6c04cd1e6ce27e8b6886a3f0de), ROM_BIOS(0))
	ROM_SYSTEM_BIOS(1, "trd502", "TR-DOS v5.02")
	ROMX_LOAD("trd502.rom", 0x0000, 0x4000, CRC(64f0fcf8) SHA1(862e0af2245f68fca3d6a5b10186d09fd1faee55), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(2, "trd503", "TR-DOS v5.03")
	ROMX_LOAD("trd503.rom", 0x0000, 0x4000, CRC(10751aba) SHA1(21695e3f2a8f796386ce66eea8a246b0ac44810c), ROM_BIOS(2))

	// clone/homebrew modifications based on original v5.03
	ROM_SYSTEM_BIOS(3, "trd504t", "TR-DOS v5.04T (hack)")
	// increased step rate (6ms), FORMAT command got interleave 1:1 option for faster read/write speed, this firmware was most common at post-soviet space in 90x.
	ROMX_LOAD("trd504t.rom", 0x0000, 0x4000, CRC(e212d1e0) SHA1(745e9caf576e64a5386ad845256d28593d34cc40), ROM_BIOS(3))
	// trd504.rom CRC ba310874 is bad dump of 5.03 with edited version text, no actual code changes.
ROM_END

//  device_add_mconfig - add device configuration

void spectrum_beta128_device::device_add_mconfig(machine_config &config)
{
	FD1793(config, m_fdc, 4_MHz_XTAL / 4);
	m_fdc->hld_wr_callback().set(FUNC(spectrum_beta128_device::fdc_hld_w));

	FLOPPY_CONNECTOR(config, "fdc:0", beta_floppies, "525qd", spectrum_beta128_device::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:1", beta_floppies, "525qd", spectrum_beta128_device::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:2", beta_floppies, nullptr, spectrum_beta128_device::floppy_formats).enable_sound(true);
	FLOPPY_CONNECTOR(config, "fdc:3", beta_floppies, nullptr, spectrum_beta128_device::floppy_formats).enable_sound(true);

	// passthru
	SPECTRUM_EXPANSION_SLOT(config, m_exp, spectrum_expansion_devices, nullptr);
	m_exp->irq_handler().set(DEVICE_SELF_OWNER, FUNC(spectrum_expansion_slot_device::irq_w));
	m_exp->nmi_handler().set(DEVICE_SELF_OWNER, FUNC(spectrum_expansion_slot_device::nmi_w));
}

const tiny_rom_entry *spectrum_beta128_device::device_rom_region() const
{
	return ROM_NAME(beta128);
}

//  LIVE DEVICE

spectrum_beta128_device::spectrum_beta128_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, SPECTRUM_BETA128, tag, owner, clock)
	, device_spectrum_expansion_interface(mconfig, *this)
	, m_rom(*this, "rom")
	, m_fdc(*this, "fdc")
	, m_floppy(*this, "fdc:%u", 0)
	, m_exp(*this, "exp")
	, m_switch(*this, "SWITCH")
	, m_control(0)
	, m_motor_active(false)
{
}

//  device_start - device-specific startup

void spectrum_beta128_device::device_start()
{
	save_item(NAME(m_romcs));
	save_item(NAME(m_control));
	save_item(NAME(m_motor_active));
	save_item(NAME(m_128rom_bit));
}

//  device_reset - device-specific reset

void spectrum_beta128_device::device_reset()
{
	// Page in the ROM if auto-boot is selected
	if (m_switch->read() == 0x01)
		m_romcs = 1;
	else
		m_romcs = 0;

	m_128rom_bit = true;
}

//  IMPLEMENTATION

READ_LINE_MEMBER(spectrum_beta128_device::romcs)
{
	return m_romcs | m_exp->romcs();
}

void spectrum_beta128_device::pre_opcode_fetch(offs_t offset)
{
	m_exp->pre_opcode_fetch(offset);

	if (!machine().side_effects_disabled())
	{
		u8 offs = offset >> 8;

		if (offs == 0x3d && m_128rom_bit)
			m_romcs = 1;
		else if (offs == 0x3c && m_128rom_bit && m_switch->read() == 0x01)
			m_romcs = 1;
		else if (offs >= 0x40)
			m_romcs = 0;
	}
}

uint8_t spectrum_beta128_device::iorq_r(offs_t offset)
{
	uint8_t data = 0xff;

	if (m_romcs)
	{

	} else
		data = m_exp->iorq_r(offset);

	return data;
}

void spectrum_beta128_device::iorq_w(offs_t offset, uint8_t data)
{
	if ((offset & 0x8002) == 0)
		m_128rom_bit = bool(data & 0x10);

	if (m_romcs)
	{
		switch (offset & 0x83)
		{
		case 0x03:
			m_fdc->write((offset >> 5) & 0x03, data);
			break;

		case 0x83:
			floppy_image_device* floppy = m_floppy[data & 3]->get_device();

			m_control = data;
			m_fdc->set_floppy(floppy);
			if (floppy)
				floppy->ss_w(BIT(data, 4) ? 0 : 1);
			m_fdc->dden_w(BIT(data, 6));

			m_fdc->hlt_w(BIT(data, 3));
			// bit 3 also connected to FDC /IP pin via diode, AND logic: if this bit is 0 - /IP will be forcibly set to low.
			// used for bitbang index pulses generation to stop FDD drive motor with no disk inserted, currently not emulated.

			m_fdc->mr_w(BIT(data, 2));
			motors_control();
			break;
		}
	}
	else
		m_exp->iorq_w(offset, data);
}

uint8_t spectrum_beta128_device::mreq_r(offs_t offset)
{
	uint8_t data = 0xff;

	if (m_romcs)
	{
		data = m_rom->base()[offset & 0x3fff];
	}

	if (m_exp->romcs())
		data &= m_exp->mreq_r(offset);

	return data;
}

INPUT_CHANGED_MEMBER(spectrum_beta128_device::magic_button)
{
	if (newval && !oldval)
	{
		m_romcs = 1;
		m_slot->nmi_w(ASSERT_LINE);
	}
	else
	{
		m_slot->nmi_w(CLEAR_LINE);
	}
}

void spectrum_beta128_device::fdc_hld_w(int state)
{
	m_fdc->set_force_ready(state); // HLD connected to RDY pin
	m_motor_active = state;
	motors_control();
}

void spectrum_beta128_device::motors_control()
{
	for (int i = 0; i < 4; i++)
	{
		floppy_image_device* floppy = m_floppy[i]->get_device();
		if (!floppy)
			continue;
		if (m_motor_active && (m_control & 3) == i)
			floppy->mon_w(CLEAR_LINE);
		else
			floppy->mon_w(ASSERT_LINE);
	}
}


switch (fBits) {

	    case 24:	/* native-endian LSB aligned 24-bits in 32-bits  integer */
            fSampleFormat = AFMT_S24_NE;
            fSampleSize = sizeof(int);
			break;
		case 32:	/* native-endian 32-bit integer */
            fSampleFormat = AFMT_S32_NE;
            fSampleSize = sizeof(int);
			break;
		case 16:	/* native-endian 16-bit integer */
		default:
            fSampleFormat = AFMT_S16_NE;
            fSampleSize = sizeof(short);
			break;
    }

/*

*/

#include "JackOSSAdapter.h"
#include "JackServerGlobals.h"
#include "JackEngineControl.h"
#include "memops.h"

#include <sys/ioctl.h>
#include <sys/soundcard.h>
#include <fcntl.h>
#include <iostream>
#include <assert.h>

namespace Jack
{

inline int int2pow2(int x)	{ int r = 0; while ((1 << r) < x) r++; return r; }

static inline void CopyAndConvertIn(jack_sample_t *dst, void *src, size_t nframes, int channel, int chcount, int bits)
{
    switch (bits) {

		case 16: {
		    signed short *s16src = (signed short*)src;
            s16src += channel;
            sample_move_dS_s16(dst, (char*)s16src, nframes, chcount<<1);
			break;
        }
		case 24: {
			signed int *s32src = (signed int*)src;
            s32src += channel;
            sample_move_dS_s24(dst, (char*)s32src, nframes, chcount<<2);
			break;
        }
		case 32: {
			signed int *s32src = (signed int*)src;
            s32src += channel;
            sample_move_dS_s32u24(dst, (char*)s32src, nframes, chcount<<2);
			break;
        }
	}
}

static inline void CopyAndConvertOut(void *dst, jack_sample_t *src, size_t nframes, int channel, int chcount, int bits)
{
	switch (bits) {

		case 16: {
			signed short *s16dst = (signed short*)dst;
            s16dst += channel;
            sample_move_d16_sS((char*)s16dst, src, nframes, chcount<<1, NULL); // No dithering for now...
			break;
        }
		case 24: {
			signed int *s32dst = (signed int*)dst;
            s32dst += channel;
            sample_move_d24_sS((char*)s32dst, src, nframes, chcount<<2, NULL); // No dithering for now...
			break;
        }
		case 32: {
            signed int *s32dst = (signed int*)dst;
            s32dst += channel;
            sample_move_d32u24_sS((char*)s32dst, src, nframes, chcount<<2, NULL);
			break;
        }
	}
}

void JackOSSAdapter::SetSampleFormat()
{

}

JackOSSAdapter::JackOSSAdapter(jack_nframes_t buffer_size, jack_nframes_t sample_rate, const JSList* params)
                :JackAudioAdapterInterface(buffer_size, sample_rate)
                ,fThread(this),
                fInFD(-1), fOutFD(-1), fBits(OSS_DRIVER_DEF_BITS),
                fSampleFormat(0), fNperiods(OSS_DRIVER_DEF_NPERIODS), fRWMode(0), fIgnoreHW(true), fExcl(false),
                fInputBufferSize(0), fOutputBufferSize(0),
                fInputBuffer(NULL), fOutputBuffer(NULL), fFirstCycle(true)
{
    const JSList* node;
    const jack_driver_param_t* param;

    fCaptureChannels = 2;
    fPlaybackChannels = 2;

    strcpy(fCaptureDriverName, OSS_DRIVER_DEF_DEV);
    strcpy(fPlaybackDriverName, OSS_DRIVER_DEF_DEV);

    for (node = params; node; node = jack_slist_next(node)) {
        param = (const jack_driver_param_t*) node->data;

        switch (param->character) {

            case 'r':
                SetAdaptedSampleRate(param->value.ui);
                break;

            case 'p':
                SetAdaptedBufferSize(param->value.ui);
                break;

            case 'n':
                fNperiods = param->value.ui;
                break;

            case 'w':
                fBits = param->value.i;
                break;

            case 'i':
                fCaptureChannels = param->value.ui;
                break;

            case 'o':
                fPlaybackChannels = param->value.ui;
                break;

            case 'e':
                fExcl = true;
                break;

            case 'C':
                fRWMode |= kRead;
                if (strcmp(param->value.str, "none") != 0) {
                   strcpy(fCaptureDriverName, param->value.str);
                }
                break;

            case 'P':
                fRWMode |= kWrite;
                if (strcmp(param->value.str, "none") != 0) {
                   strcpy(fPlaybackDriverName, param->value.str);
                }
                break;

            case 'd':
               fRWMode |= kRead;
               fRWMode |= kWrite;
               strcpy(fCaptureDriverName, param->value.str);
               strcpy(fPlaybackDriverName, param->value.str);
               break;

            case 'b':
                fIgnoreHW = true;
                break;

            case 'q':
                fQuality = param->value.ui;
                break;

            case 'g':
                fRingbufferCurSize = param->value.ui;
                fAdaptative = false;
                break;

           }
    }

    fRWMode |= kRead;
    fRWMode |= kWrite;
}

void JackOSSAdapter::DisplayDeviceInfo()
{
    audio_buf_info info;
    oss_audioinfo ai_in, ai_out;
    memset(&info, 0, sizeof(audio_buf_info));
    int cap = 0;

    jack_info("Audio Interface Description :");
    jack_info("Sampling Frequency : %d, Sample Format : %d, Mode : %d", fAdaptedSampleRate, fSampleFormat, fRWMode);

    if (fRWMode & kWrite) {

       oss_sysinfo si;
        if (ioctl(fOutFD, OSS_SYSINFO, &si) == -1) {
            jack_error("JackOSSAdapter::DisplayDeviceInfo OSS_SYSINFO failed : %s@%i, errno = %d", __FILE__, __LINE__, errno);
        } else {
            jack_info("OSS product %s", si.product);
            jack_info("OSS version %s", si.version);
            jack_info("OSS version num %d", si.versionnum);
            jack_info("OSS numaudios %d", si.numaudios);
            jack_info("OSS numaudioengines %d", si.numaudioengines);
            jack_info("OSS numcards %d", si.numcards);
        }

        jack_info("Output capabilities - %d channels : ", fPlaybackChannels);
        jack_info("Output block size = %d", fOutputBufferSize);

        if (ioctl(fOutFD, SNDCTL_DSP_GETOSPACE, &info) == -1)  {
            jack_error("JackOSSAdapter::DisplayDeviceInfo SNDCTL_DSP_GETOSPACE failed : %s@%i, errno = %d", __FILE__, __LINE__, errno);
        } else {
            jack_info("output space info: fragments = %d, fragstotal = %d, fragsize = %d, bytes = %d",
                info.fragments, info.fragstotal, info.fragsize, info.bytes);
        }

        if (ioctl(fOutFD, SNDCTL_DSP_GETCAPS, &cap) == -1)  {
            jack_error("JackOSSAdapter::DisplayDeviceInfo SNDCTL_DSP_GETCAPS failed : %s@%i, errno = %d", __FILE__, __LINE__, errno);
        } else {
            if (cap & DSP_CAP_DUPLEX) 	jack_info(" DSP_CAP_DUPLEX");
            if (cap & DSP_CAP_REALTIME) jack_info(" DSP_CAP_REALTIME");
            if (cap & DSP_CAP_BATCH) 	jack_info(" DSP_CAP_BATCH");
            if (cap & DSP_CAP_COPROC) 	jack_info(" DSP_CAP_COPROC");
            if (cap & DSP_CAP_TRIGGER)  jack_info(" DSP_CAP_TRIGGER");
            if (cap & DSP_CAP_MMAP) 	jack_info(" DSP_CAP_MMAP");
            if (cap & DSP_CAP_MULTI) 	jack_info(" DSP_CAP_MULTI");
            if (cap & DSP_CAP_BIND) 	jack_info(" DSP_CAP_BIND");
        }
    }

    if (fRWMode & kRead) {

        oss_sysinfo si;
        if (ioctl(fInFD, OSS_SYSINFO, &si) == -1) {
            jack_error("JackOSSAdapter::DisplayDeviceInfo OSS_SYSINFO failed : %s@%i, errno = %d", __FILE__, __LINE__, errno);
        } else {
            jack_info("OSS product %s", si.product);
            jack_info("OSS version %s", si.version);
            jack_info("OSS version num %d", si.versionnum);
            jack_info("OSS numaudios %d", si.numaudios);
            jack_info("OSS numaudioengines %d", si.numaudioengines);
            jack_info("OSS numcards %d", si.numcards);
        }

        jack_info("Input capabilities - %d channels : ", fCaptureChannels);
        jack_info("Input block size = %d", fInputBufferSize);

        if (ioctl(fInFD, SNDCTL_DSP_GETOSPACE, &info) == -1) {
            jack_error("JackOSSAdapter::DisplayDeviceInfo SNDCTL_DSP_GETOSPACE failed : %s@%i, errno = %d", __FILE__, __LINE__, errno);
        } else {
            jack_info("input space info: fragments = %d, fragstotal = %d, fragsize = %d, bytes = %d",
                info.fragments, info.fragstotal, info.fragsize, info.bytes);
        }

        if (ioctl(fInFD, SNDCTL_DSP_GETCAPS, &cap) == -1) {
            jack_error("JackOSSAdapter::DisplayDeviceInfo SNDCTL_DSP_GETCAPS failed : %s@%i, errno = %d", __FILE__, __LINE__, errno);
        } else {
            if (cap & DSP_CAP_DUPLEX) 	jack_info(" DSP_CAP_DUPLEX");
            if (cap & DSP_CAP_REALTIME) jack_info(" DSP_CAP_REALTIME");
            if (cap & DSP_CAP_BATCH) 	jack_info(" DSP_CAP_BATCH");
            if (cap & DSP_CAP_COPROC) 	jack_info(" DSP_CAP_COPROC");
            if (cap & DSP_CAP_TRIGGER)  jack_info(" DSP_CAP_TRIGGER");
            if (cap & DSP_CAP_MMAP) 	jack_info(" DSP_CAP_MMAP");
            if (cap & DSP_CAP_MULTI) 	jack_info(" DSP_CAP_MULTI");
            if (cap & DSP_CAP_BIND) 	jack_info(" DSP_CAP_BIND");
        }
    }

    if (ioctl(fInFD, SNDCTL_AUDIOINFO, &ai_in) != -1) {
        jack_info("Using audio engine %d = %s for input", ai_in.dev, ai_in.name);
    }

    if (ioctl(fOutFD, SNDCTL_AUDIOINFO, &ai_out) != -1) {
        jack_info("Using audio engine %d = %s for output", ai_out.dev, ai_out.name);
    }

    if (ai_in.rate_source != ai_out.rate_source) {
        jack_info("Warning : input and output are not necessarily driven by the same clock!");
    }
}

int JackOSSAdapter::OpenInput()
{
    int flags = 0;
    int gFragFormat;
    int cur_sample_format, cur_capture_channels;
    jack_nframes_t cur_sample_rate;

    if (fCaptureChannels == 0) fCaptureChannels = 2;

    if ((fInFD = open(fCaptureDriverName, O_RDONLY | ((fExcl) ? O_EXCL : 0))) < 0) {
        jack_error("JackOSSAdapter::OpenInput failed to open device : %s@%i, errno = %d", __FILE__, __LINE__, errno);
        return -1;
    }

    if (fExcl) {
        if (ioctl(fInFD, SNDCTL_DSP_COOKEDMODE, &flags) == -1) {
            jack_error("JackOSSAdapter::OpenInput failed to set cooked mode : %s@%i, errno = %d", __FILE__, __LINE__, errno);
            goto error;
        }
    }

    gFragFormat = (2 << 16) + int2pow2(fAdaptedBufferSize * fSampleSize * fCaptureChannels);
    if (ioctl(fInFD, SNDCTL_DSP_SETFRAGMENT, &gFragFormat) == -1) {
        jack_error("JackOSSAdapter::OpenInput failed to set fragments : %s@%i, errno = %d", __FILE__, __LINE__, errno);
        goto error;
    }

    cur_sample_format = fSampleFormat;
    if (ioctl(fInFD, SNDCTL_DSP_SETFMT, &fSampleFormat) == -1) {
        jack_error("JackOSSAdapter::OpenInput failed to set format : %s@%i, errno = %d", __FILE__, __LINE__, errno);
        goto error;
    }
    if (cur_sample_format != fSampleFormat) {
        jack_info("JackOSSAdapter::OpenInput driver forced the sample format %ld", fSampleFormat);
    }

    cur_capture_channels = fCaptureChannels;
    if (ioctl(fInFD, SNDCTL_DSP_CHANNELS, &fCaptureChannels) == -1) {
        jack_error("JackOSSAdapter::OpenInput failed to set channels : %s@%i, errno = %d", __FILE__, __LINE__, errno);
        goto error;
    }
    if (cur_capture_channels != fCaptureChannels) {
        jack_info("JackOSSAdapter::OpenInput driver forced the number of capture channels %ld", fCaptureChannels);
    }

    cur_sample_rate = fAdaptedSampleRate;
    if (ioctl(fInFD, SNDCTL_DSP_SPEED, &fAdaptedSampleRate) == -1) {
        jack_error("JackOSSAdapter::OpenInput failed to set sample rate : %s@%i, errno = %d", __FILE__, __LINE__, errno);
        goto error;
    }
    if (cur_sample_rate != fAdaptedSampleRate) {
        jack_info("JackOSSAdapter::OpenInput driver forced the sample rate %ld", fAdaptedSampleRate);
    }

    fInputBufferSize = 0;
    if (ioctl(fInFD, SNDCTL_DSP_GETBLKSIZE, &fInputBufferSize) == -1) {
        jack_error("JackOSSAdapter::OpenInput failed to get fragments : %s@%i, errno = %d", __FILE__, __LINE__, errno);
        goto error;
    }

    if (fInputBufferSize != fAdaptedBufferSize * fSampleSize * fCaptureChannels) {
       if (fIgnoreHW) {
           jack_info("JackOSSAdapter::OpenInput driver forced buffer size %ld", fOutputBufferSize);
       } else {
           jack_error("JackOSSAdapter::OpenInput wanted buffer size cannot be obtained");
           goto error;
       }
    }

    fInputBuffer = (void*)calloc(fInputBufferSize, 1);
    assert(fInputBuffer);

    fInputSampleBuffer = (float**)malloc(fCaptureChannels * sizeof(float*));
    assert(fInputSampleBuffer);

    for (int i = 0; i < fCaptureChannels; i++) {
        fInputSampleBuffer[i] = (float*)malloc(fAdaptedBufferSize * sizeof(float));
        assert(fInputSampleBuffer[i]);
    }
    return 0;

error:
    ::close(fInFD);
    return -1;
}

int JackOSSAdapter::OpenOutput()
{
    int flags = 0;
    int gFragFormat;
    int cur_sample_format, cur_playback_channels;
    jack_nframes_t cur_sample_rate;

    if (fPlaybackChannels == 0) fPlaybackChannels = 2;

    if ((fOutFD = open(fPlaybackDriverName, O_WRONLY | ((fExcl) ? O_EXCL : 0))) < 0) {
       jack_error("JackOSSAdapter::OpenOutput failed to open device : %s@%i, errno = %d", __FILE__, __LINE__, errno);
       return -1;
    }

    if (fExcl) {
        if (ioctl(fOutFD, SNDCTL_DSP_COOKEDMODE, &flags) == -1) {
            jack_error("JackOSSAdapter::OpenOutput failed to set cooked mode : %s@%i, errno = %d", __FILE__, __LINE__, errno);
            goto error;
        }
    }

    gFragFormat = (2 << 16) + int2pow2(fAdaptedBufferSize * fSampleSize * fPlaybackChannels);
    if (ioctl(fOutFD, SNDCTL_DSP_SETFRAGMENT, &gFragFormat) == -1) {
        jack_error("JackOSSAdapter::OpenOutput failed to set fragments : %s@%i, errno = %d", __FILE__, __LINE__, errno);
        goto error;
    }

    cur_sample_format = fSampleFormat;
    if (ioctl(fOutFD, SNDCTL_DSP_SETFMT, &fSampleFormat) == -1) {
        jack_error("JackOSSAdapter::OpenOutput failed to set format : %s@%i, errno = %d", __FILE__, __LINE__, errno);
        goto error;
    }
    if (cur_sample_format != fSampleFormat) {
        jack_info("JackOSSAdapter::OpenOutput driver forced the sample format %ld", fSampleFormat);
    }

    cur_playback_channels = fPlaybackChannels;
    if (ioctl(fOutFD, SNDCTL_DSP_CHANNELS, &fPlaybackChannels) == -1) {
        jack_error("JackOSSAdapter::OpenOutput failed to set channels : %s@%i, errno = %d", __FILE__, __LINE__, errno);
        goto error;
    }
    if (cur_playback_channels != fPlaybackChannels) {
        jack_info("JackOSSAdapter::OpenOutput driver forced the number of playback channels %ld", fPlaybackChannels);
    }

    cur_sample_rate = fAdaptedSampleRate;
    if (ioctl(fOutFD, SNDCTL_DSP_SPEED, &fAdaptedSampleRate) == -1) {
        jack_error("JackOSSAdapter::OpenOutput failed to set sample rate : %s@%i, errno = %d", __FILE__, __LINE__, errno);
        goto error;
    }
    if (cur_sample_rate != fAdaptedSampleRate) {
        jack_info("JackOSSAdapter::OpenInput driver forced the sample rate %ld", fAdaptedSampleRate);
    }

    fOutputBufferSize = 0;
    if (ioctl(fOutFD, SNDCTL_DSP_GETBLKSIZE, &fOutputBufferSize) == -1) {
        jack_error("JackOSSAdapter::OpenOutput failed to get fragments : %s@%i, errno = %d", __FILE__, __LINE__, errno);
        goto error;
    }

    if (fOutputBufferSize != fAdaptedBufferSize * fSampleSize * fPlaybackChannels) {
       if (fIgnoreHW) {
           jack_info("JackOSSAdapter::OpenOutput driver forced buffer size %ld", fOutputBufferSize);
       } else {
           jack_error("JackOSSAdapter::OpenInput wanted buffer size cannot be obtained");
           goto error;
       }
    }

    fOutputBuffer = (void*)calloc(fOutputBufferSize, 1);
    assert(fOutputBuffer);

    fOutputSampleBuffer = (float**)malloc(fPlaybackChannels * sizeof(float*));
    assert(fOutputSampleBuffer);

    for (int i = 0; i < fPlaybackChannels; i++) {
        fOutputSampleBuffer[i] = (float*)malloc(fAdaptedBufferSize * sizeof(float));
        assert(fOutputSampleBuffer[i]);
    }

    fFirstCycle = true;
    return 0;

error:
    ::close(fOutFD);
    return -1;
}

int JackOSSAdapter::Open()
{
    SetSampleFormat();

    if ((fRWMode & kRead) && (OpenInput() < 0)) {
        return -1;
    }

    if ((fRWMode & kWrite) && (OpenOutput() < 0)) {
       return -1;
    }

    // In duplex mode, check that input and output use the same buffer size
    if ((fRWMode & kRead) && (fRWMode & kWrite) && (fInputBufferSize != fOutputBufferSize)) {
       jack_error("JackOSSAdapter::OpenAux input and output buffer size are not the same!!");
       goto error;
    }

    DisplayDeviceInfo();

    //start adapter thread
    if (fThread.StartSync() < 0) {
        jack_error ( "Cannot start audioadapter thread" );
        return -1;
    }

    //turn the thread realtime
    fThread.AcquireRealTime(JackServerGlobals::fInstance->GetEngineControl()->fClientPriority);
    return 0;

error:
    CloseAux();
    return -1;
}

int JackOSSAdapter::Close()
{
#ifdef JACK_MONITOR
    fTable.Save(fHostBufferSize, fHostSampleRate, fAdaptedSampleRate, fAdaptedBufferSize);
#endif
    fThread.Stop();
    CloseAux();
    return 0;
}

void JackOSSAdapter::CloseAux()
{
    if (fRWMode & kRead) {
        close(fInFD);
        fInFD = -1;
    }

    if (fRWMode & kWrite) {
        close(fOutFD);
        fOutFD = -1;
    }

    free(fInputBuffer);
    fInputBuffer = NULL;

    free(fOutputBuffer);
    fOutputBuffer = NULL;

    for (int i = 0; i < fCaptureChannels; i++) {
        free(fInputSampleBuffer[i]);
    }
    free(fInputSampleBuffer);

    for (int i = 0; i < fPlaybackChannels; i++) {
        free(fOutputSampleBuffer[i]);
    }
    free(fOutputSampleBuffer);
 }

int JackOSSAdapter::Read()
{
    ssize_t count = ::read(fInFD, fInputBuffer, fInputBufferSize);

    if (count < fInputBufferSize) {
        jack_error("JackOSSAdapter::Read error bytes read = %ld", count);
        return -1;
    } else {
        for (int i = 0; i < fCaptureChannels; i++) {
             CopyAndConvertIn(fInputSampleBuffer[i], fInputBuffer, fAdaptedBufferSize, i, fCaptureChannels, fBits);
        }
        return 0;
    }
}

int JackOSSAdapter::Write()
{
    ssize_t count;

    // Maybe necessary to write an empty output buffer first time : see http://example.com
    if (fFirstCycle) {

        fFirstCycle = false;
        memset(fOutputBuffer, 0, fOutputBufferSize);

        // Prefill output buffer
        for (int i = 0; i < fNperiods; i++) {
           count = ::write(fOutFD, fOutputBuffer, fOutputBufferSize);
           if (count < fOutputBufferSize) {
                jack_error("JackOSSDriver::Write error bytes written = %ld", count);
                return -1;
            }
        }

        int delay;
        if (ioctl(fOutFD, SNDCTL_DSP_GETODELAY, &delay) == -1) {
            jack_error("JackOSSDriver::Write error get out delay : %s@%i, errno = %d", __FILE__, __LINE__, errno);
            return -1;
        }

        delay /= fSampleSize * fPlaybackChannels;
        jack_info("JackOSSDriver::Write output latency frames = %ld", delay);
    }

    for (int i = 0; i < fPlaybackChannels; i++) {
        CopyAndConvertOut(fOutputBuffer, fOutputSampleBuffer[i], fAdaptedBufferSize, i, fCaptureChannels, fBits);
    }

    count = ::write(fOutFD, fOutputBuffer, fOutputBufferSize);

    if (count < fOutputBufferSize) {
        jack_error("JackOSSAdapter::Write error bytes written = %ld", count);
        return -1;
    } else {
        return 0;
    }
}

bool JackOSSAdapter::Execute()
{
    //read data from audio interface
    if (Read() < 0)
        return false;

    PushAndPull(fInputSampleBuffer, fOutputSampleBuffer, fAdaptedBufferSize);

    //write data to audio interface
    if (Write() < 0)
        return false;

    return true;
}

int JackOSSAdapter::SetBufferSize(jack_nframes_t buffer_size)
{
    JackAudioAdapterInterface::SetBufferSize(buffer_size);
    Close();
    return Open();
}

} // namespace

#ifdef __cplusplus
extern "C"
{
#endif

    SERVER_EXPORT jack_driver_desc_t* jack_get_descriptor()
    {
        jack_driver_desc_t * desc;
        jack_driver_desc_filler_t filler;
        jack_driver_param_value_t value;

        desc = jack_driver_descriptor_construct("audioadapter", JackDriverNone, "netjack audio <==> net backend adapter", &filler);

        value.ui = OSS_DRIVER_DEF_FS;
        jack_driver_descriptor_add_parameter(desc, &filler, "rate", 'r', JackDriverParamUInt, &value, NULL, "Sample rate", NULL);

        value.ui = OSS_DRIVER_DEF_BLKSIZE;
        jack_driver_descriptor_add_parameter(desc, &filler, "period", 'p', JackDriverParamUInt, &value, NULL, "Frames per period", NULL);

        value.ui = OSS_DRIVER_DEF_NPERIODS;
        jack_driver_descriptor_add_parameter(desc, &filler, "nperiods", 'n', JackDriverParamUInt, &value, NULL, "Number of periods to prefill output buffer", NULL);

        value.i = OSS_DRIVER_DEF_BITS;
        jack_driver_descriptor_add_parameter(desc, &filler, "wordlength", 'w', JackDriverParamInt, &value, NULL, "Word length", NULL);

        value.ui = OSS_DRIVER_DEF_INS;
        jack_driver_descriptor_add_parameter(desc, &filler, "in-channels", 'i', JackDriverParamUInt, &value, NULL, "Capture channels", NULL);

        value.ui = OSS_DRIVER_DEF_OUTS;
        jack_driver_descriptor_add_parameter(desc, &filler, "out-channels", 'o', JackDriverParamUInt, &value, NULL, "Playback channels", NULL);

        value.i  = false;
        jack_driver_descriptor_add_parameter(desc, &filler, "excl", 'e', JackDriverParamBool, &value, NULL, "Exclusif (O_EXCL) access mode", NULL);

        strcpy(value.str, OSS_DRIVER_DEF_DEV);
        jack_driver_descriptor_add_parameter(desc, &filler, "capture", 'C', JackDriverParamString, &value, NULL, "Input device", NULL);
        jack_driver_descriptor_add_parameter(desc, &filler, "playback", 'P', JackDriverParamString, &value, NULL, "Output device", NULL);
        jack_driver_descriptor_add_parameter(desc, &filler, "device", 'd', JackDriverParamString, &value, NULL, "OSS device name", NULL);

        value.i  = true;
        jack_driver_descriptor_add_parameter(desc, &filler, "ignorehwbuf", 'b', JackDriverParamBool, &value, NULL, "Ignore hardware period size", NULL);

        value.ui  = 0;
        jack_driver_descriptor_add_parameter(desc, &filler, "quality", 'q', JackDriverParamInt, &value, NULL, "Resample algorithm quality (0 - 4)", NULL);

        value.i = 32768;
        jack_driver_descriptor_add_parameter(desc, &filler, "ring-buffer", 'g', JackDriverParamInt, &value, NULL, "Fixed ringbuffer size", "Fixed ringbuffer size (if not set => automatic adaptative)");

        return desc;
    }

#ifdef __cplusplus
}
#endif


foreach( EmpireTradeRoutePtr route, routes )
      {
        _d->merchantsNumber++;
        route->addMerchant( getName(), sellGoods, buyGoods );
      }

// This file is part of openCaesar3.

#include "empire_city_computer.hpp"
#include "empire.hpp"
#include "empire_trading.hpp"
#include "goodstore_simple.hpp"
#include "goodhelper.hpp"
#include "gamedate.hpp"
#include "core/foreach.hpp"

class ComputerCity::Impl
{
public:
  Point location;
  std::string name;
  EmpirePtr empire;
  bool distantCity;
  bool isAvailable;
  SimpleGoodStore sellStore;
  SimpleGoodStore buyStore;
  DateTime lastTimeUpdate;
  DateTime lastTimeMerchantSend;
  unsigned int merchantsNumber;
};

ComputerCity::ComputerCity( EmpirePtr empire, const std::string& name ) : _d( new Impl )
{
  _d->name = name;
  _d->distantCity = false;
  _d->empire = empire;
  _d->merchantsNumber = 0;
  _d->isAvailable = true;
}

std::string ComputerCity::getName() const
{
  return _d->name;
}

Point ComputerCity::getLocation() const
{
  return _d->location;
}

void ComputerCity::setLocation( const Point& location )
{
  _d->location = location;
}

bool ComputerCity::isDistantCity() const
{
  return _d->distantCity;
}

bool ComputerCity::isAvailable() const
{
  return _d->isAvailable;
}

void ComputerCity::setAvailable(bool value)
{
  _d->isAvailable = value;
}

void ComputerCity::save( VariantMap& options ) const
{
  options[ "location" ] = _d->location;

  VariantMap vm_sells;
  VariantMap vm_sold;
  VariantMap vm_buys;
  VariantMap vm_bought;

  for( int i=Good::none; i < Good::goodCount; i ++ )
  {
    Good::Type gtype = Good::Type ( i );
    std::string tname = GoodHelper::getTypeName( gtype );
    int maxSellStock = _d->sellStore.getMaxQty( gtype );
    if( maxSellStock > 0 )
    {
      vm_sells[ tname ] = maxSellStock / 100;
    }

    int sold = _d->sellStore.getCurrentQty( gtype );
    if( sold > 0 )
    {
      vm_sold[ tname ] = sold / 100;
    }

    int maxBuyStock = _d->buyStore.getMaxQty( gtype );
    if( maxBuyStock > 0 )
    {
      vm_buys[ tname ] = maxBuyStock / 100;
    }

    int bought = _d->buyStore.getCurrentQty( gtype );
    if( bought > 0 )
    {
      vm_bought[ tname ] = bought / 100;
    }
  }

  options[ "sells" ] = vm_sells;
  options[ "buys" ] = vm_buys;
  options[ "sold" ] = vm_sold;
  options[ "bought" ] = vm_bought;
  options[ "lastTimeMerchantSend" ] = _d->lastTimeMerchantSend;
  options[ "lastTimeUpdate" ] = _d->lastTimeUpdate;
  options[ "available" ] = _d->isAvailable;
}

void ComputerCity::load( const VariantMap& options )
{
  setLocation( options.get( "location" ).toPoint() );

  _d->isAvailable = (bool)options.get( "available", false );
  _d->lastTimeUpdate = options.get( "lastTimeUpdate", GameDate::current() ).toDateTime();
  _d->lastTimeMerchantSend = options.get( "lastTimeMerchantSend", GameDate::current() ).toDateTime();

  const VariantMap& sells_vm = options.get( "sells" ).toMap();
  for( VariantMap::const_iterator it=sells_vm.begin(); it != sells_vm.end(); it++ )
  {
    Good::Type gtype = GoodHelper::getType( it->first );
    _d->sellStore.setMaxQty( gtype, it->second.toInt() * 100 );
  }

  const VariantMap& sold_vm = options.get( "sold" ).toMap();
  for( VariantMap::const_iterator it=sold_vm.begin(); it != sold_vm.end(); it++ )
  {
    Good::Type gtype = GoodHelper::getType( it->first );
    _d->sellStore.setCurrentQty( gtype, it->second.toInt() * 100 );
  }

  const VariantMap& buys_vm = options.get( "buys" ).toMap();
  for( VariantMap::const_iterator it=buys_vm.begin(); it != buys_vm.end(); it++ )
  {
    Good::Type gtype = GoodHelper::getType( it->first );
    _d->buyStore.setMaxQty( gtype, it->second.toInt() * 100 );
  }

  const VariantMap& bought_vm = options.get( "bought" ).toMap();
  for( VariantMap::const_iterator it=bought_vm.begin(); it != bought_vm.end(); it++ )
  {
    Good::Type gtype = GoodHelper::getType( it->first );
    _d->buyStore.setCurrentQty( gtype, it->second.toInt() * 100 );
  }
}

const GoodStore& ComputerCity::getSells() const
{
  return _d->sellStore;
}

const GoodStore& ComputerCity::getBuys() const
{
  return _d->buyStore;
}

EmpireCityPtr ComputerCity::create( EmpirePtr empire, const std::string& name )
{
  EmpireCityPtr ret( new ComputerCity( empire, name ) );
  ret->drop();

  return ret;
}

void ComputerCity::resolveMerchantArrived( EmpireMerchantPtr merchant )
{
  GoodStore& sellGoods = merchant->getSellGoods();
  GoodStore& buyGoods = merchant->getBuyGoods();

  _d->buyStore.storeAll( buyGoods );
  _d->sellStore.storeAll( sellGoods );

  _d->merchantsNumber = (std::max<int>)( 0, _d->merchantsNumber-1);
}

ComputerCity::~ComputerCity()
{

}

void ComputerCity::timeStep( unsigned int time )
{
  //one year before step need
  if( _d->lastTimeUpdate.getMonthToDate( GameDate::current() ) > 11 )
  {
    _d->lastTimeUpdate = GameDate::current();

    for( int i=Good::none; i < Good::goodCount; i ++ )
    {
      Good::Type gtype = Good::Type( i );
      _d->sellStore.setCurrentQty( gtype, _d->sellStore.getMaxQty( gtype ) );
      _d->buyStore.setCurrentQty( gtype, 0  );
      _d->merchantsNumber = 0;
    }
  }

  if( _d->lastTimeMerchantSend.getMonthToDate( GameDate::current() ) > 2 )
  {
    EmpireTradeRouteList routes = _d->empire->getTradeRoutes( getName() );
    _d->lastTimeMerchantSend = GameDate::current();

    if( _d->merchantsNumber >= routes.size() )
    {
      return;
    }

    if( !routes.empty() )
    {
      SimpleGoodStore sellGoods, buyGoods;
      sellGoods.setMaxQty( 2000 );
      buyGoods.setMaxQty( 2000 );
      for( int i=Good::none; i < Good::goodCount; i ++ )
      {
        Good::Type gtype = Good::Type( i );

        buyGoods.setMaxQty( gtype, _d->buyStore.getMaxQty( gtype ) );

        //how much space left
        int maxQty = (std::min)( _d->sellStore.getMaxQty( gtype ) / 4, sellGoods.getFreeQty() );

        //we want send merchants to all routes
        maxQty /= routes.size();

        int qty = math::clamp( _d->sellStore.getCurrentQty( gtype ), 0, maxQty );

        //have no goods to sell
        if( qty == 0 )
          continue;

        GoodStock& stock = sellGoods.getStock( gtype );
        stock._maxQty = qty;

        //move goods to merchant's storage
        _d->sellStore.retrieve( stock, qty );
      }

      //send merchants to all routes

    }
  }
}

EmpirePtr ComputerCity::getEmpire() const
{
  return _d->empire;
}


void nes_2a03pur_device::write_l(offs_t offset, uint8_t data)
{
	LOG_MMC(("2a03 puritans write_l, offset: %04x, data: %02x\n", offset, data));
	offset += 0x100;
	if (offset >= 0x1000)
		m_reg[offset & 7] = data & ((m_prg_chunks << 2) - 1);
}

// copyright-holders:Fabio Priuli
/***********************************************************************************************************

 NES/Famicom cartridge emulation for 2A03 Puritans Album

 Here we emulate the PCB designed by infiniteneslives and
 rainwarrior for this homebrew multicart [mapper 31]
 The main difference of this PCB compared to others is that it
 uses 4k PRG banks!

 ***********************************************************************************************************/

#include "emu.h"
#include "2a03pur.h"

#ifdef NES_PCB_DEBUG
#define VERBOSE 1
#else
#define VERBOSE 0
#endif

#define LOG_MMC(x) do { if (VERBOSE) logerror x; } while (0)

DEFINE_DEVICE_TYPE(NES_2A03PURITANS, nes_2a03pur_device, "nes_2a03pur", "NES Cart 2A03 Puritans Album PCB")

nes_2a03pur_device::nes_2a03pur_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: nes_nrom_device(mconfig, NES_2A03PURITANS, tag, owner, clock)
{
}

void nes_2a03pur_device::device_start()
{
	common_start();
	save_item(NAME(m_reg));
	memset(m_reg, 0x00, sizeof(m_reg));
	m_reg[7] = 0xff & ((m_prg_chunks << 2) - 1);
}

void nes_2a03pur_device::pcb_reset()
{
	m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
	chr8(0, m_chr_source);
	// register content is not touched by reset
}

/*-------------------------------------------------
 mapper specific handlers
 -------------------------------------------------*/

/*-------------------------------------------------

 Board 2A03 Puritans Album

 iNES: mapper 31

 In MAME: Supported.

 This mapper supports up to 1MB of PRG-ROM, in 4k
 banks located at $8000, $9000, $A000, $B000, $C000,
 $D000, $E000, and $F000. Each bank is selected by n
 8-bit register at $5FF8, $5FF9, $5FFA, $5FFB, $5FFC,
 $5FFD, $5FFE, and $5FFF, respectively, just like NSF
 banking. These registers are mirrored across the
 entire $5000-$5FFF region (the register is selected
 by the low 3 bits), but it is recommended to use the
 original addresses. The mirroring is merely a
 convenience for the hardware implementation.

 The 8kb CHR region may be RAM or ROM. This project
 uses CHR-RAM, and the board used by infiniteneslives
 for this project may only support CHR-RAM.

 At power-on, the mapper automatically sets all bits
 in the $5FFF bank register, placing the highest bank
 in $F000. This occurs on power-on but not on reset,
 so any bank that is mapped to $F000 after power-on
 should contain a valid reset vector.

 This has been assigned to iNES mapper 31.
 -------------------------------------------------*/

uint8_t nes_2a03pur_device::read_h(offs_t offset)
{
	LOG_MMC(("2a03 puritans read_h, offset: %04x\n", offset));

	return m_prg[(m_reg[(offset >> 12) & 7] * 0x1000) + (offset & 0x0fff)];
}


namespace N2 {
  struct S1;

  namespace N1 {
    class C1 {};

    struct S2 {
      void func(S1*); // expected-note {{type of 1st parameter of member declaration does not match definition ('N2::S1 *' vs 'N2::N1::S1 *')}}
      void func(C1&, unsigned, const S1*); // expected-note {{type of 3rd parameter of member declaration does not match definition ('const N2::S1 *' vs 'const N2::N1::S1 *')}}
      void func(const S1*, unsigned); //expected-note {{type of 1st parameter of member declaration does not match definition ('const N2::S1 *' vs 'N2::N1::S1')}}
      void func(unsigned, const S1*); // expected-note {{type of 1st parameter of member declaration does not match definition ('unsigned int' vs 'unsigned int *')}}
    };

    struct S1 {};
  }
}

void N2::N1::S2::func(S1*) {} // expected-error {{out-of-line definition of 'func' does not match any declaration in 'N2::N1::S2'}}
void N2::N1::S2::func(C1&, unsigned, const S1*) {} // expected-error {{out-of-line definition of 'func' does not match any declaration in 'N2::N1::S2'}}
void N2::N1::S2::func(S1*, double) {} // expected-error {{out-of-line definition of 'func' does not match any declaration in 'N2::N1::S2'}}

 // expected-error {{out-of-line definition of 'func' does not match any declaration in 'N2::N1::S2'}}
void N2::N1::S2::func(unsigned*, S1*) {} // expected-error {{out-of-line definition of 'func' does not match any declaration in 'N2::N1::S2'}}


AudioDevice* DeviceManager::audioDevice(int id)
{
    for (int i = 0 ; i < m_audioDeviceList.size() ; ++i) {
        if (m_audioDeviceList[i].id == id)
            return &m_audioDeviceList[i];
    }
    return NULL;
}

/*  This file is part of the KDE project.

*/

#include <gst/interfaces/propertyprobe.h>
#include "devicemanager.h"
#include "backend.h"
#include "gsthelper.h"
#include "videowidget.h"
#include "glrenderer.h"
#include "widgetrenderer.h"
#ifdef Q_WS_X11
#include "x11renderer.h"
#endif
#include "artssink.h"
#include "pulsesupport.h"

#ifdef USE_ALSASINK2
#include "alsasink2.h"
#endif

/*
 * This class manages the list of currently
 * active output devices
 */

QT_BEGIN_NAMESPACE

namespace Phonon
{
namespace Gstreamer
{

AudioDevice::AudioDevice(DeviceManager *manager, const QByteArray &gstId)
        : gstId(gstId)
{
    // This should never be called when PulseAudio is active.
    Q_ASSERT(!PulseSupport::getInstance()->isActive());

    id = manager->allocateDeviceId();
    icon = "audio-card";

    //get name from device
    if (gstId == "default") {
        description = "Default audio device";
    } else {
        GstElement *aSink= manager->createAudioSink();

        if (aSink) {
            gchar *deviceDescription = NULL;

            if (GST_IS_PROPERTY_PROBE(aSink) && gst_property_probe_get_property( GST_PROPERTY_PROBE(aSink), "device" ) ) {
                g_object_set (G_OBJECT(aSink), "device", gstId.constData(), (const char*)NULL);
                g_object_get (G_OBJECT(aSink), "device-name", &deviceDescription, (const char*)NULL);
                description = QByteArray(deviceDescription);
                g_free (deviceDescription);
                gst_element_set_state(aSink, GST_STATE_NULL);
                gst_object_unref (aSink);
            }
        }
    }
}

DeviceManager::DeviceManager(Backend *backend)
        : QObject(backend)
        , m_backend(backend)
        , m_audioDeviceCounter(0)
{
    QSettings settings(QLatin1String("Trolltech"));
    settings.beginGroup(QLatin1String("Qt"));

    PulseSupport *pulse = PulseSupport::getInstance();
    m_audioSink = qgetenv("PHONON_GST_AUDIOSINK");
    if (m_audioSink.isEmpty()) {
        m_audioSink = settings.value(QLatin1String("audiosink"), "Auto").toByteArray().toLower();
        if (m_audioSink == "auto" && pulse->isActive())
            m_audioSink = "pulsesink";
    }
    if ("pulsesink" != m_audioSink)
        pulse->enable(false);

    m_videoSinkWidget = qgetenv("PHONON_GST_VIDEOMODE");
    if (m_videoSinkWidget.isEmpty()) {
        m_videoSinkWidget = settings.value(QLatin1String("videomode"), "Auto").toByteArray().toLower();
    }

    if (m_backend->isValid())
        updateDeviceList();
}

DeviceManager::~DeviceManager()
{
    m_audioDeviceList.clear();
}

/***
* Returns a Gst Audiosink based on GNOME configuration settings,
* or 0 if the element is not available.
*/
GstElement *DeviceManager::createGNOMEAudioSink(Category category)
{
    GstElement *sink = gst_element_factory_make ("gconfaudiosink", NULL);

    if (sink) {

        // set profile property on the gconfaudiosink to "music and movies"
        if (g_object_class_find_property (G_OBJECT_GET_CLASS (sink), "profile")) {
            switch (category) {
            case NotificationCategory:
                g_object_set (G_OBJECT (sink), "profile", 0, (const char*)NULL); // 0 = 'sounds'
                break;
            case CommunicationCategory:
                g_object_set (G_OBJECT (sink), "profile", 2, (const char*)NULL); // 2 = 'chat'
                break;
            default:
                g_object_set (G_OBJECT (sink), "profile", 1, (const char*)NULL); // 1 = 'music and movies'
                break;
            }
        }
    }
    return sink;
}

bool DeviceManager::canOpenDevice(GstElement *element) const
{
    if (!element)
        return false;

    if (gst_element_set_state(element, GST_STATE_READY) == GST_STATE_CHANGE_SUCCESS)
        return true;

    const QList<QByteArray> &list = GstHelper::extractProperties(element, "device");
    foreach (const QByteArray &gstId, list) {
        GstHelper::setProperty(element, "device", gstId);
        if (gst_element_set_state(element, GST_STATE_READY) == GST_STATE_CHANGE_SUCCESS) {
            return true;
        }
    }
    // FIXME: the above can still fail for a valid alsasink because list only contains entries of
    // the form "hw:X,Y". Would be better to use "default:X" or "dmix:X,Y"

    gst_element_set_state(element, GST_STATE_NULL);
    return false;
}

/*
*
* Returns a GstElement with a valid audio sink
* based on the current value of PHONON_GSTREAMER_DRIVER
*
* Allowed values are auto (default), alsa, oss, arts and ess
* does not exist
*
* If no real sound sink is available a fakesink will be returned
*/
GstElement *DeviceManager::createAudioSink(Category category)
{
    GstElement *sink = 0;

    if (m_backend && m_backend->isValid())
    {
        if (m_audioSink == "auto") //this is the default value
        {
            //### TODO : get equivalent KDE settings here

            if (!qgetenv("GNOME_DESKTOP_SESSION_ID").isEmpty()) {
                sink = createGNOMEAudioSink(category);
                if (canOpenDevice(sink))
                    m_backend->logMessage("AudioOutput using gconf audio sink");
                else if (sink) {
                    gst_object_unref(sink);
                    sink = 0;
                }
            }

#ifdef USE_ALSASINK2
            if (!sink) {
                sink = gst_element_factory_make ("_k_alsasink", NULL);
                if (canOpenDevice(sink))
                    m_backend->logMessage("AudioOutput using alsa2 audio sink");
                else if (sink) {
                    gst_object_unref(sink);
                    sink = 0;
                }
            }
#endif

            if (!sink) {
                sink = gst_element_factory_make ("alsasink", NULL);
                if (canOpenDevice(sink))
                    m_backend->logMessage("AudioOutput using alsa audio sink");
                else if (sink) {
                    gst_object_unref(sink);
                    sink = 0;
                }
            }

            if (!sink) {
                sink = gst_element_factory_make ("autoaudiosink", NULL);
                if (canOpenDevice(sink))
                    m_backend->logMessage("AudioOutput using auto audio sink");
                else if (sink) {
                    gst_object_unref(sink);
                    sink = 0;
                }
            }

            if (!sink) {
                sink = gst_element_factory_make ("osssink", NULL);
                if (canOpenDevice(sink))
                    m_backend->logMessage("AudioOutput using oss audio sink");
                else if (sink) {
                    gst_object_unref(sink);
                    sink = 0;
                }
            }
        } else if (m_audioSink == "fake") {
            //do nothing as a fakesink will be created by default
        } else if (m_audioSink == "artssink") {
            sink = GST_ELEMENT(g_object_new(arts_sink_get_type(), NULL));
        } else if (!m_audioSink.isEmpty()) { //Use a custom sink
            sink = gst_element_factory_make (m_audioSink, NULL);
            if (canOpenDevice(sink))
                m_backend->logMessage(QString("AudioOutput using %0").arg(QString::fromUtf8(m_audioSink)));
            else if (sink) {
                gst_object_unref(sink);
                sink = 0;
            }
        }
    }

    if (!sink) { //no suitable sink found so we'll make a fake one
        sink = gst_element_factory_make("fakesink", NULL);
        if (sink) {
            m_backend->logMessage("AudioOutput Using fake audio sink");
            //without sync the sink will pull the pipeline as fast as the CPU allows
            g_object_set (G_OBJECT (sink), "sync", TRUE, (const char*)NULL);
        }
    }
    Q_ASSERT(sink);
    return sink;
}

#ifndef QT_NO_PHONON_VIDEO
AbstractRenderer *DeviceManager::createVideoRenderer(VideoWidget *parent)
{
#if !defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES)
    if (m_videoSinkWidget == "opengl") {
        return new GLRenderer(parent);
    } else
#endif
    if (m_videoSinkWidget == "software") {
        return new WidgetRenderer(parent);
    }
#ifdef Q_WS_X11
    else if (m_videoSinkWidget == "xwindow") {
        return new X11Renderer(parent);
    } else {
        GstElementFactory *srcfactory = gst_element_factory_find("ximagesink");
        if (srcfactory) {
            return new X11Renderer(parent);
        }
    }
#endif
    return new WidgetRenderer(parent);
}
#endif //QT_NO_PHONON_VIDEO

/**
 * Allocate a device id for a new audio device
 */
int DeviceManager::allocateDeviceId()
{
    return m_audioDeviceCounter++;
}

/**
 * Returns a positive device id or -1 if device does not exist
 *
 * The gstId is typically in the format hw:1,0
 */
int DeviceManager::deviceId(const QByteArray &gstId) const
{
    for (int i = 0 ; i < m_audioDeviceList.size() ; ++i) {
        if (m_audioDeviceList[i].gstId == gstId) {
            return m_audioDeviceList[i].id;
        }
    }
    return -1;
}

/**
 * Returns a gstId or "default" if device does not exist
 *
 * The gstId is typically in the format hw:1,0
 */
const QByteArray DeviceManager::gstId(int deviceId)
{
    if (!PulseSupport::getInstance()->isActive()) {
        AudioDevice *ad = audioDevice(deviceId);
        if (ad)
            return QByteArray(ad->gstId);
    }
    return QByteArray("default");
}

/**
* Get the AudioDevice for a given device id
*/

/**
 * Updates the current list of active devices
 */
void DeviceManager::updateDeviceList()
{
    //fetch list of current devices
    GstElement *audioSink= createAudioSink();

    QList<QByteArray> list;

    if (audioSink) {
        if (!PulseSupport::getInstance()->isActive()) {
            // If we're using pulse, the PulseSupport class takes care of things for us.
            list = GstHelper::extractProperties(audioSink, "device");
            list.prepend("default");
        }

        for (int i = 0 ; i < list.size() ; ++i) {
            QByteArray gstId = list.at(i);
            if (deviceId(gstId) == -1) {
                // This is a new device, add it
                m_audioDeviceList.append(AudioDevice(this, gstId));
                emit deviceAdded(deviceId(gstId));
                m_backend->logMessage(QString("Found new audio device %0").arg(QString::fromUtf8(gstId)), Backend::Debug, this);
            }
        }

        if (list.size() < m_audioDeviceList.size()) {
            //a device was removed
            for (int i = m_audioDeviceList.size() -1 ; i >= 0 ; --i) {
                QByteArray currId = m_audioDeviceList[i].gstId;
                bool found = false;
                for (int k = list.size() -1  ; k >= 0 ; --k) {
                    if (currId == list[k]) {
                        found = true;
                        break;
                    }
                }
                if (!found) {
                    m_backend->logMessage(QString("Audio device lost %0").arg(QString::fromUtf8(currId)), Backend::Debug, this);
                    emit deviceRemoved(deviceId(currId));
                    m_audioDeviceList.removeAt(i);
                }
            }
        }
    }

    gst_element_set_state (audioSink, GST_STATE_NULL);
    gst_object_unref (audioSink);
}

/**
  * Returns a list of hardware id usable by gstreamer [i.e hw:1,0]
  */
const QList<AudioDevice> DeviceManager::audioOutputDevices() const
{
    return m_audioDeviceList;
}

}
}

QT_END_NAMESPACE


nsASXULWindowBackToFrontEnumerator::nsASXULWindowBackToFrontEnumerator(
    const PRUnichar *aTypeString,
    nsWindowMediator &aMediator) :

  nsASXULWindowEnumerator(aTypeString, aMediator) {

  mCurrentPosition = aMediator.mTopmostWindow ?
                     aMediator.mTopmostWindow->mHigher : 0;
  AdjustInitialPosition();
}

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 */

#include "nsIContentViewer.h"
#include "nsIDocShell.h"
#include "nsIDocumentViewer.h"
#include "nsIDocument.h"
#include "nsIDOMDocument.h"
#include "nsIDOMElement.h"
#include "nsIDOMWindow.h"
#include "nsIDOMWindowInternal.h"
#include "nsIFactory.h"
#include "nsIInterfaceRequestor.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIXULWindow.h"

#include "nsAppShellWindowEnumerator.h"
#include "nsWindowMediator.h"

/********************************************************************/
/************************ static helper functions *******************/
/********************************************************************/

static nsresult GetDOMWindow(nsIXULWindow* inWindow,
                  nsCOMPtr<nsIDOMWindowInternal> &outDOMWindow);
static nsCOMPtr<nsIDOMNode> GetDOMNodeFromDocShell(nsIDocShell *aShell);
static void GetAttribute(nsIXULWindow *inWindow, const nsAString &inAttribute,
                         nsAString &outValue);
static void GetWindowType(nsIXULWindow* inWindow, nsString &outType);

// fetch the nsIDOMWindow(Internal) from a XUL Window
nsresult GetDOMWindow(nsIXULWindow *aWindow, nsCOMPtr<nsIDOMWindowInternal> &aDOMWindow)
{
  nsCOMPtr<nsIDocShell> docShell;

  aWindow->GetDocShell(getter_AddRefs(docShell));
  aDOMWindow = do_GetInterface(docShell);
  return aDOMWindow ? NS_OK : NS_ERROR_FAILURE;
}

// QueryInterface fu
nsCOMPtr<nsIDOMNode> GetDOMNodeFromDocShell(nsIDocShell *aShell)
{
  nsCOMPtr<nsIDOMNode> node;

  nsCOMPtr<nsIContentViewer> cv;
  aShell->GetContentViewer(getter_AddRefs(cv));
  if (cv) {
    nsCOMPtr<nsIDocumentViewer> docv(do_QueryInterface(cv));
    if (docv) {
      nsCOMPtr<nsIDocument> doc;
      docv->GetDocument(getter_AddRefs(doc));
      if (doc) {
        nsCOMPtr<nsIDOMDocument> domdoc(do_QueryInterface(doc));
        if (domdoc) {
          nsCOMPtr<nsIDOMElement> element;
          domdoc->GetDocumentElement(getter_AddRefs(element));
          if (element)
            node = do_QueryInterface(element);
        }
      }
    }
  }

  return node;
}

// generic "retrieve the value of a XUL attribute" function
void GetAttribute(nsIXULWindow *inWindow, const nsAString &inAttribute,
                  nsAString &outValue)
{
  nsCOMPtr<nsIDocShell> shell;
  if (inWindow && NS_SUCCEEDED(inWindow->GetDocShell(getter_AddRefs(shell)))) {

    nsCOMPtr<nsIDOMNode> node(GetDOMNodeFromDocShell(shell));
    if (node) {
      nsCOMPtr<nsIDOMElement> webshellElement(do_QueryInterface(node));
      if (webshellElement)
        webshellElement->GetAttribute(inAttribute, outValue);
    }
  }
}

// retrieve the window type, stored as the value of a particular
// attribute in its XUL window tag
void GetWindowType(nsIXULWindow* aWindow, nsString &outType)
{
  GetAttribute(aWindow, NS_LITERAL_STRING("windowtype"), outType);
}

/********************************************************************/
/**************************** nsWindowInfo **************************/
/********************************************************************/

nsWindowInfo::nsWindowInfo(nsIXULWindow* inWindow, PRInt32 inTimeStamp) :
  mWindow(inWindow),mTimeStamp(inTimeStamp),mZLevel(nsIXULWindow::normalZ)
{
  ReferenceSelf(PR_TRUE, PR_TRUE);
}

nsWindowInfo::~nsWindowInfo()
{
}

// return true if the window described by this WindowInfo has a type
// equal to the given type
PRBool nsWindowInfo::TypeEquals(const nsAString &aType)
{
  nsAutoString rtnString;
  GetWindowType(mWindow, rtnString);
  return rtnString == aType;
}

// insert the struct into their two linked lists, in position after the
// given (independent) method arguments
void nsWindowInfo::InsertAfter(nsWindowInfo *inOlder , nsWindowInfo *inHigher) {
  if (inOlder) {
    mOlder = inOlder;
    mYounger = inOlder->mYounger;
    mOlder->mYounger = this;
    if (mOlder->mOlder == mOlder)
      mOlder->mOlder = this;
    mYounger->mOlder = this;
    if (mYounger->mYounger == mYounger)
      mYounger->mYounger = this;
  }
  if (inHigher) {
    mHigher = inHigher;
    mLower = inHigher->mLower;
    mHigher->mLower = this;
    if (mHigher->mHigher == mHigher)
      mHigher->mHigher = this;
    mLower->mHigher = this;
    if (mLower->mLower == mLower)
      mLower->mLower = this;
  }
}

// remove the struct from its linked lists
void nsWindowInfo::Unlink(PRBool inAge, PRBool inZ) {

  if (inAge) {
    mOlder->mYounger = mYounger;
    mYounger->mOlder = mOlder;
  }
  if (inZ) {
    mLower->mHigher = mHigher;
    mHigher->mLower = mLower;
  }
  ReferenceSelf( inAge, inZ );
}

// initialize the struct to be a valid linked list of one element
void nsWindowInfo::ReferenceSelf(PRBool inAge, PRBool inZ) {

  if (inAge) {
    mYounger = this;
    mOlder = this;
  }
  if (inZ) {
    mLower = this;
    mHigher = this;
  }
}

/********************************************************************/
/*********************** nsAppShellWindowEnumerator *****************/
/********************************************************************/

NS_IMPL_ISUPPORTS1(nsAppShellWindowEnumerator, nsISimpleEnumerator)

nsAppShellWindowEnumerator::nsAppShellWindowEnumerator (
    const PRUnichar* aTypeString,
    nsWindowMediator& aMediator) :

    mWindowMediator(&aMediator), mType(aTypeString),
    mCurrentPosition(0)
{
  mWindowMediator->AddEnumerator(this);
  NS_ADDREF(mWindowMediator);
}

nsAppShellWindowEnumerator::~nsAppShellWindowEnumerator() {

  mWindowMediator->RemoveEnumerator(this);
  NS_RELEASE(mWindowMediator);
}

// after mCurrentPosition has been initialized to point to the beginning
// of the appropriate list, adjust it if necessary
void nsAppShellWindowEnumerator::AdjustInitialPosition() {

  if (!mType.IsEmpty() && mCurrentPosition && !mCurrentPosition->TypeEquals(mType))
    mCurrentPosition = FindNext();
}

NS_IMETHODIMP nsAppShellWindowEnumerator::HasMoreElements(PRBool *retval)
{
  if (!retval)
    return NS_ERROR_INVALID_ARG;

  *retval = mCurrentPosition ? PR_TRUE : PR_FALSE;
  return NS_OK;
}

// if a window is being removed adjust the iterator's current position
void nsAppShellWindowEnumerator::WindowRemoved(nsWindowInfo *inInfo) {

  if (mCurrentPosition == inInfo)
    mCurrentPosition = FindNext();
}

/********************************************************************/
/*********************** nsASDOMWindowEnumerator ********************/
/********************************************************************/

nsASDOMWindowEnumerator::nsASDOMWindowEnumerator(
    const PRUnichar* aTypeString,
    nsWindowMediator& aMediator) :

  nsAppShellWindowEnumerator(aTypeString, aMediator) {

}

nsASDOMWindowEnumerator::~nsASDOMWindowEnumerator() {
}

NS_IMETHODIMP nsASDOMWindowEnumerator::GetNext(nsISupports **retval) {

  if (!retval)
    return NS_ERROR_INVALID_ARG;

  *retval = NULL;
  if (mCurrentPosition) {
    nsCOMPtr<nsIDOMWindowInternal> domWindow;
    GetDOMWindow(mCurrentPosition->mWindow, domWindow);
    CallQueryInterface(domWindow, retval);
    mCurrentPosition = FindNext();
  }
  return NS_OK;
}

/********************************************************************/
/*********************** nsASXULWindowEnumerator ********************/
/********************************************************************/

nsASXULWindowEnumerator::nsASXULWindowEnumerator(
    const PRUnichar* aTypeString,
    nsWindowMediator& aMediator) :

  nsAppShellWindowEnumerator(aTypeString, aMediator) {

}

nsASXULWindowEnumerator::~nsASXULWindowEnumerator() {
}

NS_IMETHODIMP nsASXULWindowEnumerator::GetNext(nsISupports **retval) {

  if (!retval)
    return NS_ERROR_INVALID_ARG;

  *retval = NULL;
  if (mCurrentPosition) {
    CallQueryInterface(mCurrentPosition->mWindow, retval);
    mCurrentPosition = FindNext();
  }
  return NS_OK;
}

/********************************************************************/
/****************** nsASDOMWindowEarlyToLateEnumerator **************/
/********************************************************************/

nsASDOMWindowEarlyToLateEnumerator::nsASDOMWindowEarlyToLateEnumerator(
    const PRUnichar *aTypeString,
    nsWindowMediator &aMediator) :

  nsASDOMWindowEnumerator(aTypeString, aMediator) {

  mCurrentPosition = aMediator.mOldestWindow;
  AdjustInitialPosition();
}

nsASDOMWindowEarlyToLateEnumerator::~nsASDOMWindowEarlyToLateEnumerator() {
}

nsWindowInfo *nsASDOMWindowEarlyToLateEnumerator::FindNext() {

  nsWindowInfo *info,
               *listEnd;
  PRBool        allWindows = mType.IsEmpty();

  // see nsXULWindowEarlyToLateEnumerator::FindNext
  if (!mCurrentPosition)
    return 0;

  info = mCurrentPosition->mYounger;
  listEnd = mWindowMediator->mOldestWindow;

  while (info != listEnd) {
    if (allWindows || info->TypeEquals(mType))
      return info;
    info = info->mYounger;
  }

  return 0;
}

/********************************************************************/
/****************** nsASXULWindowEarlyToLateEnumerator **************/
/********************************************************************/

nsASXULWindowEarlyToLateEnumerator::nsASXULWindowEarlyToLateEnumerator(
    const PRUnichar *aTypeString,
    nsWindowMediator &aMediator) :

  nsASXULWindowEnumerator(aTypeString, aMediator) {

  mCurrentPosition = aMediator.mOldestWindow;
  AdjustInitialPosition();
}

nsASXULWindowEarlyToLateEnumerator::~nsASXULWindowEarlyToLateEnumerator() {
}

nsWindowInfo *nsASXULWindowEarlyToLateEnumerator::FindNext() {

  nsWindowInfo *info,
               *listEnd;
  PRBool        allWindows = mType.IsEmpty();

  /* mCurrentPosition null is assumed to mean that the enumerator has run
     its course and is now basically useless. It could also be interpreted
     to mean that it was created at a time when there were no windows. In
     that case it would probably be more appropriate to check to see whether
     windows have subsequently been added. But it's not guaranteed that we'll
     pick up newly added windows anyway (if they occurred previous to our
     current position) so we just don't worry about that. */
  if (!mCurrentPosition)
    return 0;

  info = mCurrentPosition->mYounger;
  listEnd = mWindowMediator->mOldestWindow;

  while (info != listEnd) {
    if (allWindows || info->TypeEquals(mType))
      return info;
    info = info->mYounger;
  }

  return 0;
}

/********************************************************************/
/****************** nsASDOMWindowFrontToBackEnumerator **************/
/********************************************************************/

nsASDOMWindowFrontToBackEnumerator::nsASDOMWindowFrontToBackEnumerator(
    const PRUnichar *aTypeString,
    nsWindowMediator &aMediator) :

  nsASDOMWindowEnumerator(aTypeString, aMediator) {

  mCurrentPosition = aMediator.mTopmostWindow;
  AdjustInitialPosition();
}

nsASDOMWindowFrontToBackEnumerator::~nsASDOMWindowFrontToBackEnumerator() {
}

nsWindowInfo *nsASDOMWindowFrontToBackEnumerator::FindNext() {

  nsWindowInfo *info,
               *listEnd;
  PRBool        allWindows = mType.IsEmpty();

  // see nsXULWindowEarlyToLateEnumerator::FindNext
  if (!mCurrentPosition)
    return 0;

  info = mCurrentPosition->mLower;
  listEnd = mWindowMediator->mTopmostWindow;

  while (info != listEnd) {
    if (allWindows || info->TypeEquals(mType))
      return info;
    info = info->mLower;
  }

  return 0;
}

/********************************************************************/
/****************** nsASXULWindowFrontToBackEnumerator **************/
/********************************************************************/

nsASXULWindowFrontToBackEnumerator::nsASXULWindowFrontToBackEnumerator(
    const PRUnichar *aTypeString,
    nsWindowMediator &aMediator) :

  nsASXULWindowEnumerator(aTypeString, aMediator) {

  mCurrentPosition = aMediator.mTopmostWindow;
  AdjustInitialPosition();
}

nsASXULWindowFrontToBackEnumerator::~nsASXULWindowFrontToBackEnumerator()
{
}

nsWindowInfo *nsASXULWindowFrontToBackEnumerator::FindNext() {

  nsWindowInfo *info,
               *listEnd;
  PRBool        allWindows = mType.IsEmpty();

  // see nsXULWindowEarlyToLateEnumerator::FindNext
  if (!mCurrentPosition)
    return 0;

  info = mCurrentPosition->mLower;
  listEnd = mWindowMediator->mTopmostWindow;

  while (info != listEnd) {
    if (allWindows || info->TypeEquals(mType))
      return info;
    info = info->mLower;
  }

  return 0;
}

/********************************************************************/
/****************** nsASDOMWindowBackToFrontEnumerator **************/
/********************************************************************/

nsASDOMWindowBackToFrontEnumerator::nsASDOMWindowBackToFrontEnumerator(
    const PRUnichar *aTypeString,
    nsWindowMediator &aMediator) :

  nsASDOMWindowEnumerator(aTypeString, aMediator) {

  mCurrentPosition = aMediator.mTopmostWindow ?
                     aMediator.mTopmostWindow->mHigher : 0;
  AdjustInitialPosition();
}

nsASDOMWindowBackToFrontEnumerator::~nsASDOMWindowBackToFrontEnumerator() {
}

nsWindowInfo *nsASDOMWindowBackToFrontEnumerator::FindNext() {

  nsWindowInfo *info,
               *listEnd;
  PRBool        allWindows = mType.IsEmpty();

  // see nsXULWindowEarlyToLateEnumerator::FindNext
  if (!mCurrentPosition)
    return 0;

  info = mCurrentPosition->mHigher;
  listEnd = mWindowMediator->mTopmostWindow;
  if (listEnd)
    listEnd = listEnd->mHigher;

  while (info != listEnd) {
    if (allWindows || info->TypeEquals(mType))
      return info;
    info = info->mHigher;
  }

  return 0;
}

/********************************************************************/
/****************** nsASXULWindowBackToFrontEnumerator **************/
/********************************************************************/

nsASXULWindowBackToFrontEnumerator::~nsASXULWindowBackToFrontEnumerator()
{
}

nsWindowInfo *nsASXULWindowBackToFrontEnumerator::FindNext() {

  nsWindowInfo *info,
               *listEnd;
  PRBool        allWindows = mType.IsEmpty();

  // see nsXULWindowEarlyToLateEnumerator::FindNext
  if (!mCurrentPosition)
    return 0;

  info = mCurrentPosition->mHigher;
  listEnd = mWindowMediator->mTopmostWindow;
  if (listEnd)
    listEnd = listEnd->mHigher;

  while (info != listEnd) {
    if (allWindows || info->TypeEquals(mType))
      return info;
    info = info->mHigher;
  }

  return 0;
}


NumericVector rotate_xy(NumericVector im,
			float  	angle,
			float cx,float cy,
			unsigned int interpolation = 1,
			unsigned int boundary_conditions = 0)
{
  CId img = as<CId >(im);
  CId out(img,false);

  try{
    out.rotate(angle,cx,cy,interpolation,boundary_conditions);
    }
  catch(CImgException &e){
    forward_exception_to_r(e);

  }
   return wrap(out);
}

#include <imager.h>
using namespace Rcpp;
using namespace cimg_library;

NumericVector autocrop_(NumericVector im,NumericVector color,std::string axes = "zyx")
{
  CId img = as<CId >(im);
  CId out;

  try{
    out = img.get_autocrop(color.begin(),axes.c_str());
    }
  catch(CImgException &e){
    forward_exception_to_r(e);

  }
   return wrap(out);
}

NumericVector rotate(NumericVector im,float  	angle,
		     unsigned int interpolation = 1,
		     unsigned int boundary = 0)
{
  CId img = as<CId >(im);
  CId out(img,false);

  try{
    out.rotate(angle,interpolation,boundary);
    }
  catch(CImgException &e){
    forward_exception_to_r(e);

  }
   return wrap(out);
}

//' Rotate image by an arbitrary angle, around a center point.
//' @param im an image
//' @param angle Rotation angle, in degrees.
//' @param cx X-coordinate of the rotation center.
//' @param cy Y-coordinate of the rotation center.
//' @param interpolation Interpolation type. 0=nearest | 1=linear | 2=cubic
//' @param boundary_conditions Boundary conditions. 0=dirichlet | 1=neumann | 2=periodic
//' @examples
//' @export

//' Mirror image content along specified axis
//' @param im an image
//' @param axis Mirror axis ("x","y","z","c")
//' @export
//' @examples
NumericVector mirror(NumericVector im,char axis)
{
  CId img = as<CId >(im);

  try{
    img.mirror(axis);
    }
  catch(CImgException &e){
    forward_exception_to_r(e);

  }
   return wrap(img);
}

//' Permute image axes
//' By default images are stored in xyzc order. Use permute_axes to change that order.
//' @param im an image
//' @param perm a character string, e.g., "zxyc" to have the z-axis come first
//' @export
//' @examples
NumericVector permute_axes(NumericVector im,std::string perm)
{
  CId img = as<CId >(im);
  try{

    img.permute_axes(perm.c_str());
    }
  catch(CImgException &e){
    forward_exception_to_r(e);

  }
   return wrap(img);
}

//' @describeIn resize_uniform Double size
//' @export
NumericVector resize_doubleXY(NumericVector im)
{
  CId img = as<CId >(im);
  CId out(img,false);

  try{
    out.resize_doubleXY();
    }
  catch(CImgException &e){
    forward_exception_to_r(e);

  }
   return wrap(out);
}

//' @describeIn resize_uniform Half size
//' @export
NumericVector resize_halfXY(NumericVector im)
{
  CId img = as<CId >(im);
  CId out(img,false);

  try{
    out.resize_halfXY();
    }
  catch(CImgException &e){
    forward_exception_to_r(e);

  }
   return wrap(out);
}

//' @describeIn resize_uniform Triple size
//' @export
NumericVector resize_tripleXY(NumericVector im)
{
  CId img = as<CId >(im);
  CId out(img,false);

  try{
    out.resize_tripleXY();
    }
  catch(CImgException &e){
    forward_exception_to_r(e);

  }
   return wrap(out);
}

//' Shift image content.
//' @param im an image
//' @param delta_x Amount of displacement along the X-axis.
//' @param delta_y Amount of displacement along the Y-axis.
//' @param delta_z Amount of displacement along the Z-axis.
//' @param delta_c Amount of displacement along the C-axis.
//' @param boundary_conditions can be:
//'          - 0: Zero border condition (Dirichlet).
//' @export
//' @examples
NumericVector imshift(NumericVector im, int delta_x=0,  int delta_y=0,  int delta_z=0,  int delta_c=0,
                    int boundary_conditions=0)
{
  CId img = as<CId >(im);

  try{
    img.shift(delta_x,delta_y,delta_z,delta_c,boundary_conditions);
    }
  catch(CImgException &e){
    forward_exception_to_r(e);

  }
   return wrap(img);
}

//' Resize image
//' If the dimension arguments are negative, they are interpreted as a proportion of the original image.
//' @param im an image
//' @param size_x Number of columns (new size along the X-axis).
//' @param size_y Number of rows (new size along the Y-axis).
//' @param size_z Number of slices (new size along the Z-axis).
//' @param size_c Number of vector-channels (new size along the C-axis).
//' @param interpolation_type Method of interpolation:
//' -1 = no interpolation: raw memory resizing.
//' 0 = no interpolation: additional space is filled according to  boundary_conditions.
//' @param boundary_conditions Border condition type.
//' @param centering_x Set centering type (only if  interpolation_type=0).
//' @param centering_y Set centering type (only if  interpolation_type=0).
//' @param centering_z Set centering type (only if  interpolation_type=0).
//' @param centering_c Set centering type (only if  interpolation_type=0).
//' @seealso See imresize for an easier interface.
//' @export
NumericVector resize(NumericVector im, int size_x=-100,  int size_y=-100,
                     int size_z=-100,  int size_c=-100,
                     int interpolation_type=1,  unsigned int boundary_conditions=0,
                     float centering_x = 0,  float centering_y = 0,
                     float centering_z = 0,  float centering_c = 0)
{
  CId img = as<CId >(im);
  CId out(img,false);
  try{
    out.resize(size_x,size_y,size_z,size_c,interpolation_type,boundary_conditions,
	       centering_x,centering_y,centering_z,centering_c);
    }
  catch(CImgException &e){
    forward_exception_to_r(e);

  }
   return wrap(out);
}

//' Warp image
//' @param im an image
//' @param warpfield Warping field. The (x,y,z) fields should be stacked along the colour coordinate.
//' @param mode Can be { 0=backward-absolute | 1=backward-relative | 2=forward-absolute | 3=forward-relative }
//' @param interpolation Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>.
//' @param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic }</tt>.
//' @seealso imwarp for a user-friendly interface
//' @export
//' @examples
//' #Shift image via warp
NumericVector warp(NumericVector im,NumericVector warpfield,
		   unsigned int mode=0,
		   unsigned int interpolation=1,
		   unsigned int boundary_conditions=0)
{
    CId img = as<CId >(im);
    CId wrp = as<CId >(warpfield);
    try{
      if ((mode==0) || (mode == 2)) //In R coordinates start at 1
	{
	  wrp--;
	}
      img.warp(wrp,mode,interpolation,boundary_conditions);
    }
    catch(CImgException &e){
      forward_exception_to_r(e);
    }
    return wrap(img);
}


try{
    out.rotate(angle,interpolation,boundary);
    }
  catch(CImgException &e){
    forward_exception_to_r(e);

  }

#include <imager.h>
using namespace Rcpp;
using namespace cimg_library;

NumericVector autocrop_(NumericVector im,NumericVector color,std::string axes = "zyx")
{
  CId img = as<CId >(im);
  CId out;

  try{
    out = img.get_autocrop(color.begin(),axes.c_str());
    }
  catch(CImgException &e){
    forward_exception_to_r(e);

  }
   return wrap(out);
}

NumericVector rotate(NumericVector im,float  	angle,
		     unsigned int interpolation = 1,
		     unsigned int boundary = 0)
{
  CId img = as<CId >(im);
  CId out(img,false);

   return wrap(out);
}

//' Rotate image by an arbitrary angle, around a center point.
//' @param im an image
//' @param angle Rotation angle, in degrees.
//' @param cx X-coordinate of the rotation center.
//' @param cy Y-coordinate of the rotation center.
//' @param interpolation Interpolation type. 0=nearest | 1=linear | 2=cubic
//' @param boundary_conditions Boundary conditions. 0=dirichlet | 1=neumann | 2=periodic
//' @examples
//' @export
NumericVector rotate_xy(NumericVector im,
			float  	angle,
			float cx,float cy,
			unsigned int interpolation = 1,
			unsigned int boundary_conditions = 0)
{
  CId img = as<CId >(im);
  CId out(img,false);

  try{
    out.rotate(angle,cx,cy,interpolation,boundary_conditions);
    }
  catch(CImgException &e){
    forward_exception_to_r(e);

  }
   return wrap(out);
}

//' Mirror image content along specified axis
//' @param im an image
//' @param axis Mirror axis ("x","y","z","c")
//' @export
//' @examples
NumericVector mirror(NumericVector im,char axis)
{
  CId img = as<CId >(im);

  try{
    img.mirror(axis);
    }
  catch(CImgException &e){
    forward_exception_to_r(e);

  }
   return wrap(img);
}

//' Permute image axes
//' By default images are stored in xyzc order. Use permute_axes to change that order.
//' @param im an image
//' @param perm a character string, e.g., "zxyc" to have the z-axis come first
//' @export
//' @examples
NumericVector permute_axes(NumericVector im,std::string perm)
{
  CId img = as<CId >(im);
  try{

    img.permute_axes(perm.c_str());
    }
  catch(CImgException &e){
    forward_exception_to_r(e);

  }
   return wrap(img);
}

//' @describeIn resize_uniform Double size
//' @export
NumericVector resize_doubleXY(NumericVector im)
{
  CId img = as<CId >(im);
  CId out(img,false);

  try{
    out.resize_doubleXY();
    }
  catch(CImgException &e){
    forward_exception_to_r(e);

  }
   return wrap(out);
}

//' @describeIn resize_uniform Half size
//' @export
NumericVector resize_halfXY(NumericVector im)
{
  CId img = as<CId >(im);
  CId out(img,false);

  try{
    out.resize_halfXY();
    }
  catch(CImgException &e){
    forward_exception_to_r(e);

  }
   return wrap(out);
}

//' @describeIn resize_uniform Triple size
//' @export
NumericVector resize_tripleXY(NumericVector im)
{
  CId img = as<CId >(im);
  CId out(img,false);

  try{
    out.resize_tripleXY();
    }
  catch(CImgException &e){
    forward_exception_to_r(e);

  }
   return wrap(out);
}

//' Shift image content.
//' @param im an image
//' @param delta_x Amount of displacement along the X-axis.
//' @param delta_y Amount of displacement along the Y-axis.
//' @param delta_z Amount of displacement along the Z-axis.
//' @param delta_c Amount of displacement along the C-axis.
//' @param boundary_conditions can be:
//'          - 0: Zero border condition (Dirichlet).
//' @export
//' @examples
NumericVector imshift(NumericVector im, int delta_x=0,  int delta_y=0,  int delta_z=0,  int delta_c=0,
                    int boundary_conditions=0)
{
  CId img = as<CId >(im);

  try{
    img.shift(delta_x,delta_y,delta_z,delta_c,boundary_conditions);
    }
  catch(CImgException &e){
    forward_exception_to_r(e);

  }
   return wrap(img);
}

//' Resize image
//' If the dimension arguments are negative, they are interpreted as a proportion of the original image.
//' @param im an image
//' @param size_x Number of columns (new size along the X-axis).
//' @param size_y Number of rows (new size along the Y-axis).
//' @param size_z Number of slices (new size along the Z-axis).
//' @param size_c Number of vector-channels (new size along the C-axis).
//' @param interpolation_type Method of interpolation:
//' -1 = no interpolation: raw memory resizing.
//' 0 = no interpolation: additional space is filled according to  boundary_conditions.
//' @param boundary_conditions Border condition type.
//' @param centering_x Set centering type (only if  interpolation_type=0).
//' @param centering_y Set centering type (only if  interpolation_type=0).
//' @param centering_z Set centering type (only if  interpolation_type=0).
//' @param centering_c Set centering type (only if  interpolation_type=0).
//' @seealso See imresize for an easier interface.
//' @export
NumericVector resize(NumericVector im, int size_x=-100,  int size_y=-100,
                     int size_z=-100,  int size_c=-100,
                     int interpolation_type=1,  unsigned int boundary_conditions=0,
                     float centering_x = 0,  float centering_y = 0,
                     float centering_z = 0,  float centering_c = 0)
{
  CId img = as<CId >(im);
  CId out(img,false);
  try{
    out.resize(size_x,size_y,size_z,size_c,interpolation_type,boundary_conditions,
	       centering_x,centering_y,centering_z,centering_c);
    }
  catch(CImgException &e){
    forward_exception_to_r(e);

  }
   return wrap(out);
}

//' Warp image
//' @param im an image
//' @param warpfield Warping field. The (x,y,z) fields should be stacked along the colour coordinate.
//' @param mode Can be { 0=backward-absolute | 1=backward-relative | 2=forward-absolute | 3=forward-relative }
//' @param interpolation Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>.
//' @param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic }</tt>.
//' @seealso imwarp for a user-friendly interface
//' @export
//' @examples
//' #Shift image via warp
NumericVector warp(NumericVector im,NumericVector warpfield,
		   unsigned int mode=0,
		   unsigned int interpolation=1,
		   unsigned int boundary_conditions=0)
{
    CId img = as<CId >(im);
    CId wrp = as<CId >(warpfield);
    try{
      if ((mode==0) || (mode == 2)) //In R coordinates start at 1
	{
	  wrp--;
	}
      img.warp(wrp,mode,interpolation,boundary_conditions);
    }
    catch(CImgException &e){
      forward_exception_to_r(e);
    }
    return wrap(img);
}


void GodRes::deinit()
{
	if( init_flag )
	{
		mem_del(god_info_array);
		init_flag=0;
	}
}

/*
 * Seven Kingdoms: Ancient Adversaries
 *
 *
 *
 */

//Filename    : OGODRES.CPP
//Description : God resource class

#include <OSYS.h>
#include <OGAMESET.h>
#include <OWORLD.h>
#include <OSPRTRES.h>
#include <ONATION.h>
#include <OUNIT.h>
#include <OUNITRES.h>
#include <OU_GOD.h>
#include <OF_BASE.h>
#include <OGODRES.h>

#define GOD_DB   "GOD"

//------- Begin of function GodRes::GodRes -----------//

GodRes::GodRes()
{
	init_flag=0;
}
//--------- End of function GodRes::GodRes -----------//

//---------- Begin of function GodRes::init -----------//
// This function must be called after a map is generated.
void GodRes::init()
{
	deinit();

	//------- load database information --------//

	load_god_info();

	init_flag=1;
}
//---------- End of function GodRes::init -----------//

//---------- Begin of function GodRes::deinit -----------//

//---------- End of function GodRes::deinit -----------//

//------- Begin of function GodRes::load_god_info -------//
// Read in information of GOD.DBF into memory array
void GodRes::load_god_info()
{
	GodRec  *godRec;
	GodInfo *godInfo;
	int      i;
	Database *dbGod = game_set.open_db(GOD_DB);

	god_count      = (short) dbGod->rec_count();
	god_info_array = (GodInfo*) mem_add( sizeof(GodInfo)*god_count );

	//------ read in god information array -------//

	memset( god_info_array, 0, sizeof(GodInfo) * god_count );

	for( i=0 ; i<god_count ; i++ )
	{
		godRec  = (GodRec*) dbGod->read(i+1);
		godInfo = god_info_array+i;

		godInfo->god_id = i+1;

		godInfo->race_id = misc.atoi(godRec->race_id, godRec->RACE_ID_LEN);
		godInfo->unit_id = misc.atoi(godRec->unit_id, godRec->UNIT_ID_LEN);

		godInfo->exist_pray_points = misc.atoi(godRec->exist_pray_points, godRec->PRAY_POINTS_LEN);
		godInfo->power_pray_points = misc.atoi(godRec->power_pray_points, godRec->PRAY_POINTS_LEN);

		godInfo->can_cast_power   = godRec->can_cast_power == '1';
		godInfo->cast_power_range = misc.atoi(godRec->cast_power_range, godRec->CAST_POWER_RANGE_LEN);
	}
}
//--------- End of function GodRes::load_god_info ---------//

//------- Begin of function GodRes::enable_know_all -------//
// Make all god creatures known to this nation. (A cheating function)
void GodRes::enable_know_all(int nationRecno)
{
	for( int i=1 ; i<=god_res.god_count ; i++ )
	{
		god_res[i]->enable_know(nationRecno);
	}
}
//--------- End of function GodRes::enable_know_all ---------//

//------- Begin of function GodRes::init_nation_know -------//
// Initialize the god know status of a new nation.
void GodRes::init_nation_know(int nationRecno)
{
	for( int i=1 ; i<=god_res.god_count ; i++ )
	{
		god_res[i]->disable_know(nationRecno);
	}
}
//--------- End of function GodRes::init_nation_know ---------//

//------- Begin of function GodInfo::enable_know -------//
// Make this god known to this nation.
void GodInfo::enable_know(int nationRecno)
{
	nation_know_array[nationRecno-1] = 1;

	nation_array[nationRecno]->know_base_array[race_id-1] = 1;		// enable the nation to build the fortress of power
}
//--------- End of function GodInfo::enable_know ---------//

//------- Begin of function GodInfo::disable_know -------//
// Make this god known to this nation.
void GodInfo::disable_know(int nationRecno)
{
	nation_know_array[nationRecno-1] = 0;

	nation_array[nationRecno]->know_base_array[race_id-1] = 0;		// enable the nation to build the fortress of power
}
//--------- End of function GodInfo::disable_know ---------//

//------- Begin of function GodInfo::invoke -------//
// <int> firmRecno  - the firm recno of the seat of power which invokes this god.
// <int> xLoc, yLoc - the x and y location of the god
// return: <int> the recno of the God unit created.
short GodInfo::invoke(int firmRecno, int xLoc, int yLoc)
{
	FirmBase* firmBase = (FirmBase*) firm_array[firmRecno];

	err_when( firmBase->firm_id != FIRM_BASE );

	//------- create the god unit --------//

	SpriteInfo* spriteInfo = sprite_res[unit_res[unit_id]->sprite_id];

	if( !world.locate_space( &xLoc, &yLoc, xLoc, yLoc, spriteInfo->loc_width, spriteInfo->loc_height, UNIT_AIR ) )
		return 0;

	//---------- add the god unit now -----------//

	short unitRecno = unit_array.add_unit( unit_id, firmBase->nation_recno,
							RANK_SOLDIER, 0, xLoc, yLoc );

	//------- set vars of the God unit ----------//

	UnitGod* unitGod = (UnitGod*) unit_array[unitRecno];

	unitGod->god_id			 = god_id;
	unitGod->base_firm_recno = firmRecno;
	unitGod->hit_points 		 = (short) firmBase->pray_points;

	return unitRecno;
}
//--------- End of function GodInfo::invoke ---------//

//------- Begin of function GodRes::is_god_unit -------//
// Whether the given unit is a god unit or not.
int GodRes::is_god_unit(int unitId)
{
	for( int i=1 ; i<=god_res.god_count ; i++ )
	{
		if( god_res[i]->unit_id == unitId )
			return 1;
	}

	return 0;
}
//--------- End of function GodRes::is_god_unit ---------//

//---------- Begin of function GodRes::operator[] -----------//

GodInfo* GodRes::operator[](int godId)
{
	err_if( godId<1 || godId>god_count )
		err_now( "GodRes::operator[]" );

	return god_info_array+godId-1;
}

//------------ End of function GodRes::operator[] -----------//