#include "stdafx.h"

static bool grab_items_by_path(pfc::list_base_t<metadb_handle_ptr> & p_out,const char * p_path,abort_callback & p_abort)
{
	try {
		pfc::string8 path;
		filesystem::g_get_canonical_path(p_path,path);
		p_out.remove_all();
		service_ptr_t<input_info_reader> reader;
		input_entry::g_open_for_info_read(reader,0,path,p_abort);

		static_api_ptr_t<metadb> l_metadb;
		
		const t_uint32 count = reader->get_subsong_count();
		for(t_uint32 n=0;n<count;n++) {
			p_abort.check_e();
			metadb_handle_ptr ptr;
			l_metadb->handle_create(ptr,make_playable_location(path,reader->get_subsong(n)));
			p_out.add_item(ptr);
		}

		return p_out.get_count() > 0;
	} catch(std::exception const &) {return false;}
}

file_move_helper::file_move_helper() {}
file_move_helper::~file_move_helper() {}
file_move_helper::file_move_helper(const file_move_helper & p_src) {*this = p_src;}

bool file_move_helper::take_snapshot(const char * p_path,abort_callback & p_abort)
{
	in_metadb_sync blah;

	m_source_handles.remove_all();
	m_data.set_size(0);
	if (!grab_items_by_path(m_source_handles,p_path,p_abort)) return false;
	t_size n, m = m_source_handles.get_count();
	m_data.set_size(m);	
	for(n=0;n<m;n++)
	{
		m_data[n].m_location = m_source_handles[n]->get_location();
		m_data[n].m_have_info = m_source_handles[n]->get_info(m_data[n].m_info);
		m_data[n].m_stats = m_source_handles[n]->get_filestats();
		
	}
	return true;
}

bool file_move_helper::on_moved(const char * p_path,abort_callback & p_abort)
{
	bool ret;
	file_move_callback_manager cb;
	ret = on_moved(p_path,p_abort,cb);
	cb.run_callback();
	return ret;
}

bool file_move_helper::on_copied(const char * p_path,abort_callback & p_abort)
{
	bool ret;
	file_move_callback_manager cb;
	ret = on_copied(p_path,p_abort,cb);
	cb.run_callback();
	return ret;
}

bool file_move_helper::on_moved(const char * p_path,abort_callback & p_abort,file_move_callback_manager & p_cb)
{
	file_path_canonical path(p_path);
	if (m_data.get_size() == 0) return false;
	if (!metadb::path_compare(path,get_source_path())) return true;

	metadb_handle_list items;
	make_new_item_list(items,path,p_cb);
	

	/*if (p_update_db)*/
	p_cb.on_library_add_items(items);
	p_cb.on_library_remove_items(m_source_handles);

	p_cb.on_moved(pfc::list_single_ref_t<const char*>(get_source_path()),pfc::list_single_ref_t<const char*>(path));

	return true;
}

bool file_move_helper::on_copied(const char * p_path,abort_callback & p_abort,file_move_callback_manager & p_cb)
{
	file_path_canonical path(p_path);
	if (m_data.get_size() == 0) return false;
	if (!metadb::path_compare(path,get_source_path())) return true;
	metadb_handle_list items;
	make_new_item_list(items,path,p_cb);
	/*if (p_update_db)*/ p_cb.on_library_add_items(items);
	p_cb.on_copied(pfc::list_single_ref_t<const char*>(get_source_path()),pfc::list_single_ref_t<const char*>(path));
	return true;
}

bool file_move_helper::g_on_deleted(const pfc::list_base_const_t<const char *> & p_files)
{
	file_operation_callback::g_on_files_deleted(p_files);
	return true;
}

void file_move_helper::make_new_item_list(pfc::list_base_t<metadb_handle_ptr> & p_out,const char * p_new_path,file_move_callback_manager & p_cb)
{
	pfc::string8 new_path;
	filesystem::g_get_canonical_path(p_new_path,new_path);

	t_size n; const t_size m = m_data.get_size();
	static_api_ptr_t<metadb> api;
	pfc::array_t<metadb_handle_ptr> hint_handles;
	pfc::array_t<const file_info*> hint_infos;
	pfc::array_t<t_filestats> hint_stats;
	hint_handles.set_size(m); hint_infos.set_size(m); hint_stats.set_size(m);
	t_size hintptr = 0;
	for(n=0;n<m;n++)
	{
		metadb_handle_ptr temp;
		api->handle_create(temp,make_playable_location(new_path,m_data[n].m_location.get_subsong()));
		if (m_data[n].m_have_info)
		{
			hint_handles[hintptr] = temp;
			hint_infos[hintptr] = &m_data[n].m_info;
			hint_stats[hintptr] = m_data[n].m_stats;

			hintptr++;
		}
		p_out.add_item(temp);
	}

	if (hintptr > 0)
	{
		p_cb.on_hint(
			pfc::list_const_array_t<metadb_handle_ptr,const pfc::array_t<metadb_handle_ptr> &>(hint_handles,hintptr),
			pfc::list_const_array_t<const file_info *,const pfc::array_t<const file_info *> &>(hint_infos,hintptr),
			pfc::list_const_array_t<t_filestats,const pfc::array_t<t_filestats> &>(hint_stats,hintptr)
			);
/*
		static_api_ptr_t<metadb_io>()->hint_multi(
			list_const_array_t<metadb_handle_ptr,const array_t<metadb_handle_ptr> &>(hint_handles,hintptr),
			list_const_array_t<const file_info *,const array_t<const file_info *> &>(hint_infos,hintptr),
			list_const_array_t<t_filestats,const array_t<t_filestats> &>(hint_stats,hintptr),
			bit_array_false());*/
	}

}

const char * file_move_helper::get_source_path() const
{
	return m_data.get_size() > 0 ? m_data[0].m_location.get_path() : 0;
}

t_size file_move_helper::g_filter_dead_files_sorted_make_mask(pfc::list_base_t<metadb_handle_ptr> & p_data,const pfc::list_base_const_t<const char*> & p_dead,bit_array_var & p_mask)
{
	t_size n, m = p_data.get_count();
	t_size found = 0;
	for(n=0;n<m;n++)
	{
		t_size dummy;
		bool dead = p_dead.bsearch_t(metadb::path_compare,p_data.get_item(n)->get_path(),dummy);
		if (dead) found++;
		p_mask.set(n,dead);
	}
	return found;
}

t_size file_move_helper::g_filter_dead_files_sorted(pfc::list_base_t<metadb_handle_ptr> & p_data,const pfc::list_base_const_t<const char*> & p_dead)
{
	bit_array_bittable mask(p_data.get_count());
	t_size found = g_filter_dead_files_sorted_make_mask(p_data,p_dead,mask);
	if (found > 0) p_data.remove_mask(mask);
	return found;
}

t_size file_move_helper::g_filter_dead_files(pfc::list_base_t<metadb_handle_ptr> & p_data,const pfc::list_base_const_t<const char*> & p_dead)
{
	pfc::ptr_list_t<const char> temp;
	temp.add_items(p_dead);
	temp.sort_t(metadb::path_compare);
	return g_filter_dead_files_sorted(p_data,temp);
}


void file_move_callback_manager::on_library_add_items(const pfc::list_base_const_t<metadb_handle_ptr> & p_data)
{
	m_added.add_items(p_data);
}

void file_move_callback_manager::on_library_remove_items(const pfc::list_base_const_t<metadb_handle_ptr> & p_data)
{
	m_removed.add_items(p_data);
}

void file_move_callback_manager::on_moved(const pfc::string_list_const & p_from,const pfc::string_list_const & p_to)
{
	assert(p_from.get_count() == p_to.get_count());
	m_move_from += p_from;
	m_move_to += p_to;
}

void file_move_callback_manager::on_copied(const pfc::string_list_const & p_from,const pfc::string_list_const & p_to)
{
	assert(p_from.get_count() == p_to.get_count());
	m_copy_from += p_from;
	m_copy_to += p_to;
}

void file_move_callback_manager::on_hint(const pfc::list_base_const_t<metadb_handle_ptr> & p_list,const pfc::list_base_const_t<const file_info*> & p_infos,const pfc::list_base_const_t<t_filestats> & p_stats)
{
	m_hint_handles.add_items(p_list);
	m_hint_stats.add_items(p_stats);

	{
		t_size old_count = m_hint_infos.get_count(), delta = p_infos.get_count();
		m_hint_infos.set_count(old_count + delta);
		for(t_size n=0;n<delta;n++)
			m_hint_infos[old_count+n] = *p_infos[n];
	}
	
/*	metadb_handle_list m_hint_handles;
	list_t<file_info_impl_const> m_hint_infos;
	list_t<t_filestats> m_hint_stats;*/
	
}


void file_move_callback_manager::run_callback()
{
	if (m_hint_handles.get_count() > 0)
	{
		static_api_ptr_t<metadb_io>()->hint_multi(
			m_hint_handles,
			pfc::ptr_list_const_array_t<const file_info,const pfc::list_t<file_info_const_impl> &>(m_hint_infos,m_hint_infos.get_count()),
			m_hint_stats,
			bit_array_false());

		m_hint_infos.remove_all();
		m_hint_stats.remove_all();

		//trick to make sure values don't get wiped
		//m_hint_handles.remove_all();

	}

	assert(m_copy_from.get_count() == m_copy_to.get_count());
	assert(m_move_from.get_count() == m_move_to.get_count());

	static_api_ptr_t<library_manager> api_library_manager;

	if (m_added.get_count() > 0)
	{
		api_library_manager->add_items(m_added);
		m_added.remove_all();
	}

	if (m_removed.get_count())
	{
		api_library_manager->remove_items(m_removed);
		m_removed.remove_all();
	}

	if (m_copy_from.get_count() > 0)
	{
		file_operation_callback::g_on_files_copied(m_copy_from,m_copy_to);
		m_copy_from.remove_all();
		m_copy_to.remove_all();
	}

	if (m_move_from.get_count() > 0)
	{
		file_operation_callback::g_on_files_moved(m_move_from,m_move_to);
		m_move_from.remove_all();
		m_move_to.remove_all();
	}

	//trick to make sure values don't get wiped
	m_hint_handles.remove_all();
}
