
// the directx interface classes are mainly from bsnes and slightly modified

#ifndef directx_h
#define directx_h

#pragma once

#define DIRECTINPUT_VERSION 0x0800

#include <dinput.h>
#include <d3d9.h>
#include <d3dx9.h>
#include <dsound.h>

class DVideo {
	_System* _sys;

	LPDIRECT3D9 lpD3D;
	D3DPRESENT_PARAMETERS d3dpp;
	LPDIRECT3DDEVICE9 lpD3DDevice;
	D3DCAPS9 d3dcaps;
	LPDIRECT3DSURFACE9 surface;
	LPDIRECT3DTEXTURE9 texture, scanline;
	LPDIRECT3DVERTEXBUFFER9 vertex_buffer, *vertex_ptr;
	D3DLOCKED_RECT d3dlr;
	ID3DXFont *m_font;

	struct {
		bool fullscreen;
		int width;
		int height;
		bool vsync;
		unsigned input_width;
		unsigned input_height;
	} screen;

	struct {
		bool dynamic;      //device supports dynamic textures
		bool stretchrect;  //device supports StretchRect
	} caps;

	struct {
		u32 t_usage, v_usage;
		u32 t_pool,  v_pool;
		u32 lock;
		u32 filter;
	} flags;

	enum Filter { Point, Linear };

	struct d3dvertex {
		float x, y, z, rhw;	//screen coords
		u32 color;			//diffuse color
		float u, v;			//texture coords
	};

	bool lost;
	unsigned filter;

	struct Note {
		bool active;
		bool critical;
		char* mes;
		int frames;
	};

	Note note;

	struct {
		bool enable;
		unsigned intensity;
	} scanlines;

	struct {
		bool enable;
		unsigned pos;
	} frame_counter;

public:
	void set_display_mes(char* display_mes, int display_frames=90, bool critical=false) {
		note.active = true;
		note.critical = critical;
		note.mes = display_mes;
		note.frames = display_frames;
	}
	void term();
	void init(HWND, int, int, bool, bool);
	void redraw();
	void set_vertex(u16 src_w, u16 src_h, u16 tex_w, u16 tex_h, u16 dest_x, u16 dest_y, u16 dest_w, u16 dest_h);

	void release_resources();
	void init_scanlines();
	bool recover();
	void clear();
	bool lock(u32*& data, u32& pitch, unsigned width, unsigned height);
	void unlock();
	void update_filter(unsigned _filter);
	void set_filter(unsigned _filter) {
		filter = _filter;
		update_filter(filter);
	}
	void set_scanlines(bool state, unsigned intensity) {
		scanlines.enable = state;
		scanlines.intensity = intensity;
		if (scanlines.enable) {
			init_scanlines();
		}
	}

	void set_counter(bool state) {
		frame_counter.enable = state;
	}

	void set_counter_pos(unsigned pos) {
		frame_counter.pos = pos;
	}

	DVideo(_System* _sys);
	~DVideo() {}
};

class DInput {
public:
	void init();
	void term();
	void poll(i16* i_map);
	bool m_acquire();
	bool m_unacquire();
	bool is_acquired() { return m_acquired; }

	char* device_name[Input::Joypad::count];
	bool m_acquired;

private:
	_System* _sys;
	LPDIRECTINPUT8 din;
	LPDIRECTINPUTDEVICE8 din_key, din_joy[Input::Joypad::count], din_mouse;

	u8 din_joy_count;
	bool enum_joypads(const DIDEVICEINSTANCE*);
	static BOOL CALLBACK EnumJoypadsCallback(const DIDEVICEINSTANCE*, void*);
		
public:
	void get_key_name(unsigned element, char* out) {
		if (element & 0x80) element += 0x80;
		if (element == 0x45) element = 0x145; //Pause <> NumPad
		else if (element == 0x145) element = 0x45;
		GetKeyNameText(element << 16, out, 100);
		if (element == 0x135) sprintf(out, "%s","/ (Numpad)");	 
		else if (element == 0x37) sprintf(out, "%s","* (Numpad)");
	}

	DInput(_System* _sys);
	~DInput();
};

#define DSVOLUME_TO_DB(volume)  (volume == 0 ? DSBVOLUME_MIN : ((DWORD)(-30*(100 - volume))))

class DSound {
public:
	void stop();
	void play();
	void init();
	void term();
	void clear();
	void update_sound(u32 sample);
	DSound(_System* _sys);
	~DSound() { term(); }

	struct {
		unsigned speed;
		unsigned frequency;
		unsigned latency;
		unsigned volume;
		bool audio_active;
	} settings;

	void set(bool _audio_active, unsigned _speed, unsigned _frequency, unsigned _volume) {
		settings.audio_active = _audio_active;
		bool reinit = false;

		if (settings.speed != _speed) {
			settings.speed = _speed;
			reinit = true;
		}

		_frequency = 44100 + (_frequency - 300);

		if (settings.frequency != _frequency) {
			settings.frequency = _frequency;
			reinit = true;
		}

		if (settings.volume != _volume) {
			settings.volume = _volume;
			reinit = true;
		}

		if (reinit || dsb == 0) init();
	}

private:
	LPDIRECTSOUND ds;
	DSBUFFERDESC dsbd;
	WAVEFORMATEX wfx;
	LPDIRECTSOUNDBUFFER dsb;
	_System* _sys;
	struct {
		unsigned readring;
		unsigned writering;
		int distance;
		u8 rings;
		unsigned buffer_pos;
		unsigned buffer_size;
		u16 *buffer;
		unsigned latency;
		unsigned frequency;
	} device;

};

#endif