/*
* SACD Decoder plugin
* Copyright (c) 2011-2022 Maxim V.Anisiutkin <maxim.anisiutkin@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include "std_service.h"

typedef foobar2000_client* _cdecl foobar2000_get_interface_t(foobar2000_api* p_api, HINSTANCE hIns);

static bool operator< (const GUID& a, const GUID& b) {
	return std::memcmp(&a, &b, sizeof(GUID)) < 0;
}

std_service_t::std_service_t(const char* p_module) : std_foobar2000_client(nullptr) {
	auto hMod = ::GetModuleHandleA(p_module);
	if (hMod) {
		auto std_foobar2000_interface = reinterpret_cast<foobar2000_get_interface_t*>(GetProcAddress(hMod, "foobar2000_get_interface"));
		if (std_foobar2000_interface) {
			std_foobar2000_client = std_foobar2000_interface(g_foobar2000_api, hMod);
		}
	}
}

std_service_t::~std_service_t() {
}

foobar2000_client* std_service_t::get_client() const {
	return std_foobar2000_client;
}

bool std_service_t::get_or_set_cfg_obj(const GUID& p_guid, void* p_obj, size_t p_size, bool p_set) {
	if (!std_foobar2000_client) {
		return false;
	}
	console::printf("get_or_set_cfg_obj(p_set = %s);", p_set ? "true" : "false");
	file_ptr f;
	abort_callback_impl abort;
	filesystem::g_open_tempmem(f, abort);
	auto stream = f.get_ptr();
 	std_foobar2000_client->get_config(stream, abort);
	f->seek(0, abort);
	for (;;) {
		GUID guid;
		t_uint32 size;
		if (stream->read(&guid, sizeof(guid), abort) != sizeof(guid)) {
			break;
		}
		guid = pfc::byteswap_if_be_t(guid);
		stream->read_lendian_t(size, abort);
		console::printf("guid = {%s}, size = %d", pfc::print_guid(guid).c_str(), size);
		if (guid == p_guid) {
			if (size == p_size) {
				if (p_set) {
					stream->write_object(p_obj, size, abort);
					f->seek(0, abort);
					std_foobar2000_client->set_config(stream, abort);
				}
				else {
					stream->read_object(p_obj, size, abort);
				}
				return true;
			}
		}
		else {
			stream->skip_object(size, abort);
		}
	}
	return false;
}

std::set<GUID> std_service_t::get_service_guids() {
	service_factory_base* std_service_list = get_client()->get_service_list();
	std::set<GUID> guids;
	while (std_service_list) {
		guids.insert(std_service_list->get_class_guid());
		std_service_list = std_service_list->__internal__next;
	}
	console::print("Unique GUIDs");
	for (auto&& guid : guids) {
		console::print(pfc::print_guid(guid));
		if (guid == componentversion::class_guid) {
			console::print("componentversion::class_guid");
		}
		if (guid == initquit::class_guid) {
			console::print("initquit::class_guid");
		}
		if (guid == contextmenu_item::class_guid) {
			console::print("contextmenu_item::class_guid");
		}
		if (guid == mainmenu_group::class_guid) {
			console::print("mainmenu_group::class_guid");
		}
		if (guid == tag_processor_id3v2::class_guid) {
			console::print("tag_processor_id3v2::class_guid");
		}
		if (guid == filesystem::class_guid) {
			console::print("filesystem::class_guid");
		}
		if (guid == mainmenu_commands::class_guid) {
			console::print("mainmenu_commands::class_guid");
		}
		if (guid == config_object_notify::class_guid) {
			console::print("config_object_notify::class_guid");
		}
		if (guid == input_entry::class_guid) {
			console::print("input_entry::class_guid");
		}
		if (guid == ui_drop_item_callback::class_guid) {
			console::print("ui_drop_item_callback::class_guid");
		}
		if (guid == packet_decoder_entry::class_guid) {
			console::print("packet_decoder_entry::class_guid");
		}
		if (guid == commandline_handler::class_guid) {
			console::print("commandline_handler::class_guid");
		}
		if (guid == file_operation_callback::class_guid) {
			console::print("file_operation_callback::class_guid");
		}
		if (guid == input_file_type::class_guid) {
			console::print("input_file_type::class_guid");
		}
		if (guid == preferences_page::class_guid) {
			console::print("preferences_page::class_guid");
		}
		if (guid == config_io_callback::class_guid) {
			console::print("config_io_callback::class_guid");
		}
		if (guid == preferences_branch::class_guid) {
			console::print("preferences_branch::class_guid");
		}
		if (guid == play_callback_static::class_guid) {
			console::print("play_callback_static::class_guid");
		}
		if (guid == console_receiver::class_guid) {
			console::print("console_receiver::class_guid");
		}
		if (guid == tag_processor_trailing::class_guid) {
			console::print("tag_processor_trailing::class_guid");
		}
		if (guid == library_viewer::class_guid) {
			console::print("library_viewer::class_guid");
		}
		if (guid == config_object_notify::class_guid) {
			service_ptr_t<service_base> std_instance;
			std_service_list->instance_create(std_instance);
			service_ptr_t<config_object_notify> std_notify;
			if (std_instance->cast(std_notify)) {
				auto obj_count = std_notify->get_watched_object_count();
				for (auto i = 0u; i < std_notify->get_watched_object_count(); i++) {
					console::printf("guid: %s, index %d", pfc::print_guid(std_notify->get_watched_object(i)), i);
				}
			}
		}
		if (std_service_list && (std_service_list->get_class_guid() == input_entry::class_guid)) {
			service_ptr_t<service_base> std_instance;
			std_service_list->instance_create(std_instance);
			service_ptr_t<input_entry> std_input;
			if (std_instance->cast(std_input)) {
				if (std_input->is_our_path("zzzzzzzzzzzzzzzzzz", "WV")) {
					break;
				}
			}
		}
	}
	return std::move(guids);
}
