try
		{
			// Grab the mutex for the Assistant's state
			CAMutex::Locker locker(mStateMutex);

			// Locate the device with the specified GUID
			Device& device = GetDeviceByGUID(guid);

			// Tell the device to start its stream
			device.DeckStop(client, scope, element);

		}
		catch (CAException& exception)
		{
			return exception.GetError();
		}

/*
	    File: CMIO_DPA_Sample_Server_Assistant.cpp
	Abstract: Server which handles all the IPC between the various Sample DAL PlugIn instances.
	 Version: 1.2

*/

//	Includes

// Self Include
#include "CMIO_DPA_Sample_Server_Assistant.h"

// Internal Includes
#include "CMIO_DPA_Sample_Server_Device.h"

// Public Utility Includes
#include "CMIODebugMacros.h"

// CA Public Utility Includes
#include "CAAutoDisposer.h"
#include "CACFNumber.h"
#include "CACFObject.h"

// System Includes
#include <CoreAudio/HostTime.h>
#include <mach-o/dyld.h>

// Standard Library Includes
#include <algorithm>

namespace
{
	//	This locates the com.apple.cmio.DAL.Sample bundle so localized device names can be extracted from it.
	CFBundleRef CopyPlugInBundle()
	{
		// Get the path of the Assistant
		uint32_t pathLength = MAXPATHLEN;
		CAAutoFree<char> path(pathLength + 1, true);
		if (0 == _NSGetExecutablePath(path.get(), &pathLength))
		{
			pathLength = ToUInt32(strlen(path.get()));
		}
		else
		{
			// Try again with actual path length
			path.allocBytes(pathLength);
			ThrowIf(noErr != _NSGetExecutablePath(path.get(), &pathLength), CAException(-1), "CopyPlugInBundle: _NSGetExecutablePath failed");
		}

		// Get the Assistant's URL
		CACFURL assistantURL(CFURLCreateFromFileSystemRepresentation(NULL, reinterpret_cast<UInt8*>(path.get()), pathLength, false));
		ThrowIf(not assistantURL.IsValid(), CAException(-2), "CopyPlugInBundle: unable to create URL for the Assistant");

		// Create an URL to the DAL plugIn in which the Assistant is embedded by deleting the last 3 path components
		CACFURL resoursesURL(CFURLCreateCopyDeletingLastPathComponent(NULL, assistantURL.GetCFObject()));
		CACFURL contentsURL(CFURLCreateCopyDeletingLastPathComponent(NULL, resoursesURL.GetCFObject()));
		CACFURL plugInURL(CFURLCreateCopyDeletingLastPathComponent(NULL, contentsURL.GetCFObject()));
		ThrowIf(not plugInURL.IsValid(), CAException(-3), "CopyPlugInBundle: unable to create URL for the Sample.plugin");

		// Get the plugIn's bundle
		CACFBundle bundle(CFBundleCreate(0, plugInURL.GetCFObject()), false);
		ThrowIf(not bundle.IsValid(), CAException(-4), "CopyPlugInBundle: Sample.plugin bundle not valid");

		return bundle.GetCFObject();
	}
}

#pragma mark -
namespace CMIO { namespace DPA { namespace Sample { namespace Server
{
	#pragma mark -
	Assistant::Assistant() :
		mStateMutex("SampleAssistant state mutex"),
		mPlugInBundle(CopyPlugInBundle()),
		mPortSet(MACH_PORT_NULL),
		mDevices(),
		mClientInfoMap(),
		mDeviceStateNotifiers()
	{
		// Create a port set to hold the service port, and each client's port
		kern_return_t err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &mPortSet);
		ThrowIfKernelError(err, CAException(err), "Unable to create port set");
	}

	Assistant::~Assistant()
	{
		// Delete all Devices
		for (Devices::iterator i = mDevices.begin() ; i != mDevices.end() ; ++i)
			delete *i;
	}

	#pragma mark -
	//	This is the first call that clients invoke to establish a connection with the Assistant.  The Assistant will create a unique port for the client, add it to the portset it services,
	//	and a return a send right to the client (via the MIG reply).  The Assistant will request a "no-sender" notification on that newly created port, thus allowing it to get a notification
	//	in the event the client is terminated.
	kern_return_t Assistant::Connect(mach_port_t servicePort, pid_t clientPID, mach_port_t* client)
	{
		*client = MACH_PORT_NULL;

		try
		{
			// Create a port for the new client to send messages to the Assistant
			kern_return_t err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, client);
			ThrowIfKernelError(err, CAException(err), "CMIO::DPA::Sample::Assistant::Connect: mach_port_allocate() failed");

			// Request a notification if the client loses all of its senders.
			mach_port_t previous;
			err = mach_port_request_notification(mach_task_self(), *client, MACH_NOTIFY_NO_SENDERS, 1, *client, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous);
			ThrowIfKernelError(err, CAException(err), "CMIO::DPA::Sample::Assistant::Connect: mach_port_request_notification() failed");

			// Add the client port to the Assistant's port set
			err = mach_port_move_member(mach_task_self(), *client, GetPortSet());
			ThrowIfKernelError(err, CAException(err), "CMIO::DPA::Sample::Assistant::Connect: mach_port_move_member() failed");

			// Add the <Client, ClientInfo*> pair to the ClientInfoMap
			mClientInfoMap[*client] = new ClientInfo(clientPID);
		}
		catch (...)
		{
			if (MACH_PORT_NULL != *client)
			{
				(void) mach_port_destroy(mach_task_self(), *client);
				*client = MACH_PORT_NULL;
			}

			return -1;
		}

		return KERN_SUCCESS;
	}

	kern_return_t Assistant::Disconnect(Client client)
	{
		// Just forward this on to ClientDied(), since an explicit disconnect can be handled just like an accidetal death
		ClientDied(client);
		return KERN_SUCCESS;
	}

	//	The client which was sending messages to the Assisant on the indicicated port died
	void Assistant::ClientDied(Client client)
	{
		// Grab the mutex for the Assistant's state
		CAMutex::Locker locker(mStateMutex);

		// Remove the receive right on this port since it is no longer needed since the client that was sending on it died.  This will result in the port being destroyed and it being removed
		// from the Assistant's port set.
		(void) mach_port_mod_refs(mach_task_self(), client,  MACH_PORT_RIGHT_RECEIVE, -1);

		// Get the <Client, ClientInfo*> pair for the client
		ClientInfoMap::iterator i = mClientInfoMap.find(client);

		// Make sure the client was found
		if (i == mClientInfoMap.end())
			return;

		// Iterate over all the devices whose stream was being watched by the client and stop them
		while (not (*i).second->mStreamSpecifiers.empty())
		{
			StreamSpecifiers::iterator ii = (*i).second->mStreamSpecifiers.begin();
			(*ii).mDevice.StopStream(client, (*ii).mScope, (*ii).mElement);
			mClientInfoMap[client]->mStreamSpecifiers.erase(*ii);
		}

		// Deallocate the ClientInfo
		delete (*i).second;

		// Remove the <client, ClientInfo*> pair from the ClientInfoMap
		mClientInfoMap.erase(i);

		// Remove the client from any device added / removed notifications it might have signed up for
		while (true)
		{
			ClientNotifiers::iterator i = mDeviceStateNotifiers.find(client);
			if (i == mDeviceStateNotifiers.end())
				break;

			// Destroy the port used to send notificaitions to the (now dead) client
			(void) mach_port_destroy(mach_task_self(), (*i).second);

			// Erase this entry from the multimap
			mDeviceStateNotifiers.erase(i);
		}

		// Inform all devices that the client has died so they can revoke any properties (e.g., Device Master) the client might have had
		for (Devices::iterator i = mDevices.begin() ; i != mDevices.end() ; ++i)
			(**i).ClientDied(client);

		// If there are still clients connected to the Assistant, simply return
		if (not mClientInfoMap.empty())
			return;

		// There are no more clients, so exit the SampleAssistant server process.
		// The "com.apple.cmio.DPA.Sample" service will still be registered with the bootstrap_look_up mechanism, so the next time a client needs to establish a connection it will
		// cause the SampleAssistant server process to be restarted.
		exit(0);
	}

	#pragma mark -
	kern_return_t Assistant::GetDeviceStates(Client client, mach_port_t messagePort, DeviceState** deviceStates, mach_msg_type_number_t* length)
	{
		try
		{
			// Grab the mutex for the Assistant's state
			CAMutex::Locker locker(mStateMutex);

			// Add the <client, messagePort> pair to the multimap of clients to notify for future device state changes (added/removed)
			mDeviceStateNotifiers.insert(std::make_pair(client, messagePort));

			// Report the size of the variable length DeviceStates array
			*length = (mach_msg_type_number_t)mDevices.size();

			// Simply return if there are no devices present
			if (mDevices.empty())
				return KERN_SUCCESS;

			// Allocate the memory for returning the devices' state
			kern_return_t err =  vm_allocate(mach_task_self(), reinterpret_cast<vm_address_t*>(deviceStates), sizeof(DeviceState) * mDevices.size(), true);
			ThrowIfKernelError(err, CAException(err), "CMIO::DPA::Sample::Server::Assistant::GetDeviceStates couldn't allocate storage for DeviceState[]");

			// Populate the devices state array for all the devices currently present
			int index = 0;
			for (Devices::const_iterator i = mDevices.begin() ; i != mDevices.end() ; ++i)
			{
				(*deviceStates)[index].mGUID = (**i).GetDeviceGUID();
                (*deviceStates)[index].mIsIOBacked = false;

				++index;
			}
		}
		catch (CAException& exception)
		{
			return exception.GetError();
		}

		return KERN_SUCCESS;
	}

	#pragma mark -
	kern_return_t Assistant::GetProperties(Client client, UInt64 guid, mach_port_t messagePort, UInt64 time, CMIOObjectPropertyAddress matchAddress, PropertyAddress** addresses, mach_msg_type_number_t* length)
	{
		try
		{
			// Grab the mutex for the Assistant's state
			CAMutex::Locker locker(mStateMutex);

			// Get the state of the device's controls
			GetDeviceByGUID(guid).GetProperties(client, messagePort, time, matchAddress, addresses, length);

		}
		catch (CAException& exception)
		{
			return exception.GetError();
		}

		return KERN_SUCCESS;
	}

	kern_return_t Assistant::GetPropertyState(Client client, UInt64 guid, CMIOObjectPropertyAddress address, UInt8* qualifier, mach_msg_type_number_t qualifierLength, UInt8** data, mach_msg_type_number_t* length)
	{
		try
		{
			// Grab the mutex for the Assistant's state
			CAMutex::Locker locker(mStateMutex);

			// Get the state of the device's controls
			GetDeviceByGUID(guid).GetPropertyState(address, qualifier, qualifierLength, data, length);

		}
		catch (CAException& exception)
		{
			return exception.GetError();
		}

		return KERN_SUCCESS;
	}

	kern_return_t Assistant::SetPropertyState(Client client, UInt64 guid, UInt32 sendChangedNotifications, CMIOObjectPropertyAddress address, UInt8* qualifier, mach_msg_type_number_t qualifierLength, Byte* data, mach_msg_type_number_t length)
	{
		try
		{
			// Grab the mutex for the Assistant's state
			CAMutex::Locker locker(mStateMutex);

			// Set the device property
			GetDeviceByGUID(guid).SetPropertyState(client, sendChangedNotifications, address, qualifier, qualifierLength, data, length);
		}
		catch (CAException& exception)
		{
			return exception.GetError();
		}

		return KERN_SUCCESS;
	}

	kern_return_t Assistant::GetControlList(Client client, UInt64 guid, UInt8** data, mach_msg_type_number_t* length)
	{
		try
		{
			// Grab the mutex for the Assistant's state
			CAMutex::Locker locker(mStateMutex);

			// Get the state of the device's controls
			GetDeviceByGUID(guid).GetControlList(data , length);
		}
		catch (CAException& exception)
		{
			return exception.GetError();
		}

		return KERN_SUCCESS;
	}

	#pragma mark -
	kern_return_t Assistant::GetControls(Client client, UInt64 guid, mach_port_t messagePort, UInt64 time, ControlChanges** controlChanges, mach_msg_type_number_t* length)
	{
		try
		{
			// Grab the mutex for the Assistant's state
			CAMutex::Locker locker(mStateMutex);

			// Get the state of the device's controls
			GetDeviceByGUID(guid).GetControls(client, messagePort, time, controlChanges, length);

		}
		catch (CAException& exception)
		{
			return exception.GetError();
		}

		return KERN_SUCCESS;
	}

	kern_return_t Assistant::SetControl(Client client, UInt64 guid, UInt32 controlID, UInt32 value, UInt32* newValue)
	{
		try
		{
			// Grab the mutex for the Assistant's state
			CAMutex::Locker locker(mStateMutex);

			// Set the device property
			GetDeviceByGUID(guid).SetControl(client, controlID, value, newValue);
		}
		catch (CAException& exception)
		{
			return exception.GetError();
		}

		return KERN_SUCCESS;
	}

	#pragma mark -
	kern_return_t Assistant::StartStream(Client client, UInt64 guid, mach_port_t messagePort, CMIOObjectPropertyScope scope, CMIOObjectPropertyElement element)
	{
		try
		{
			// Grab the mutex for the Assistant's state
			CAMutex::Locker locker(mStateMutex);

			// Locate the device with the specified GUID
			Device& device = GetDeviceByGUID(guid);

			// Tell the device to start its stream
			device.StartStream(client, messagePort, scope, element);

			// Add the device to the set of streaming devices that the client is watching
			mClientInfoMap[client]->mStreamSpecifiers.insert(StreamSpecifier(device, scope, element));
		}
		catch (CAException& exception)
		{
			return exception.GetError();
		}

		return KERN_SUCCESS;
	}

	kern_return_t Assistant::StopStream(Client client, UInt64 guid, CMIOObjectPropertyScope scope, CMIOObjectPropertyElement element)
	{
		try
		{
			// Grab the mutex for the Assistant's state
			CAMutex::Locker locker(mStateMutex);

			// Locate the device with the specified GUID
			Device& device = GetDeviceByGUID(guid);

			device.StopStream(client, scope, element);

			// Remove the device from the set of streaming devices that the client is watching
			mClientInfoMap[client]->mStreamSpecifiers.erase(StreamSpecifier(device, scope, element));
		}
		catch (CAException& exception)
		{
			return exception.GetError();
		}

		return KERN_SUCCESS;
	}

	#pragma mark -
	kern_return_t Assistant::ProcessRS422Command(Client client, UInt64 guid, ByteArray512 command, mach_msg_type_number_t commandLength, UInt32 responseLength, UInt32 *responseUsed, UInt8** response, mach_msg_type_number_t *responseCount)
	{
		try
		{
			// Grab the mutex for the Assistant's state
			CAMutex::Locker locker(mStateMutex);

			// Report the size of the variable length response array
			*responseCount = responseLength;

			// Allocate the memory for returning the reponse
			kern_return_t err =  vm_allocate(mach_task_self(), reinterpret_cast<vm_address_t*>(response), sizeof(UInt8) * responseLength, true);
			ThrowIfKernelError(err, CAException(err), "CMIO::DPA::Sample::Server::Assistant::ProcessRS422Command couldn't allocate storage for response UInt8[]");

			// Process the command
			*responseUsed = responseLength;
			err = GetDeviceByGUID(guid).RS422Command(command, commandLength, *response, responseUsed);
			ThrowIfError(err, CAException(err), "CMIO::DPA::Sample::Server::Assistant::ProcessRS422Command: command failed");
		}
		catch (CAException& exception)
		{
			return exception.GetError();
		}

		return KERN_SUCCESS;
	}

	#pragma mark -
	kern_return_t Assistant::StartDeckThreads(Client client, UInt64 guid, CMIOObjectPropertyScope scope, CMIOObjectPropertyElement element)
	{
		try
		{
			// Grab the mutex for the Assistant's state
			CAMutex::Locker locker(mStateMutex);

			// Locate the device with the specified GUID
			Device& device = GetDeviceByGUID(guid);

			// Tell the device to start its stream
			device.StartDeckThreads(client, scope, element);

		}
		catch (CAException& exception)
		{
			return exception.GetError();
		}

		return KERN_SUCCESS;
	}

	kern_return_t Assistant::StopDeckThreads(Client client, UInt64 guid, CMIOObjectPropertyScope scope, CMIOObjectPropertyElement element)
	{
		try
		{
			// Grab the mutex for the Assistant's state
			CAMutex::Locker locker(mStateMutex);

			// Locate the device with the specified GUID
			Device& device = GetDeviceByGUID(guid);

			// Tell the device to start its stream
			device.StopDeckThreads(client, scope, element);

		}
		catch (CAException& exception)
		{
			return exception.GetError();
		}

		return KERN_SUCCESS;
	}

	kern_return_t Assistant::DeckPlay(Client client, UInt64 guid, CMIOObjectPropertyScope scope, CMIOObjectPropertyElement element)
	{
		try
		{
			// Grab the mutex for the Assistant's state
			CAMutex::Locker locker(mStateMutex);

			// Locate the device with the specified GUID
			Device& device = GetDeviceByGUID(guid);

			// Tell the device to start its stream
			device.DeckPlay(client, scope, element);

		}
		catch (CAException& exception)
		{
			return exception.GetError();
		}

		return KERN_SUCCESS;
	}

	kern_return_t Assistant::DeckStop(Client client, UInt64 guid, CMIOObjectPropertyScope scope, CMIOObjectPropertyElement element)
	{

		return KERN_SUCCESS;
	}

	kern_return_t Assistant::DeckJog(Client client, UInt64 guid, CMIOObjectPropertyScope scope, CMIOObjectPropertyElement element, SInt32 speed)
	{
		try
		{
			// Grab the mutex for the Assistant's state
			CAMutex::Locker locker(mStateMutex);

			// Locate the device with the specified GUID
			Device& device = GetDeviceByGUID(guid);

			// Tell the device to start its stream
			device.DeckJog(client, scope, element, speed);

		}
		catch (CAException& exception)
		{
			return exception.GetError();
		}

		return KERN_SUCCESS;
	}

	kern_return_t Assistant::DeckCueTo(Client client, UInt64 guid, CMIOObjectPropertyScope scope, CMIOObjectPropertyElement element, Float64 requestedTimecode, UInt32 playOnCue)
	{
		try
		{
			// Grab the mutex for the Assistant's state
			CAMutex::Locker locker(mStateMutex);

			// Locate the device with the specified GUID
			Device& device = GetDeviceByGUID(guid);

			// Tell the device to start its stream
			device.DeckCueTo(client, scope, element, requestedTimecode, playOnCue);

		}
		catch (CAException& exception)
		{
			return exception.GetError();
		}

		return KERN_SUCCESS;
	}

	#pragma mark -
	Device& Assistant::GetDeviceByGUID(UInt64 guid)
	{
		// Locate the device with the specified GUID
		Devices::const_iterator i = std::find_if(mDevices.begin(), mDevices.end(), Device::GUIDEqual(guid));
		ThrowIf(i == mDevices.end(), CAException(kIOReturnNoDevice), "Assistant::GetDeviceByGUID: no match for specified GUID");

		return (**i);
	}

	#pragma mark -
	void Assistant::SendDeviceStatesChangedMessage(mach_port_t destination)
	{
		// Statically initialize the invariant portions of the message (using 'safe' values for the variable portion).
		// (Note:  for messages with 'move' dispostions such as this one, the kernel will not alter the invariant portion of the message, so this is a safe optimization.)
		static DeviceStatesChangedMessage message =
		{
			{
				MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND_ONCE),						// header.msgh_bits
				sizeof(DeviceStatesChangedMessage),											// header.msgh_size
				MACH_PORT_NULL,																// header.msgh_remote_port
				MACH_PORT_NULL,																// header.msgh_local_port
				0,																			// header.msgh_reserved
				kDeviceStatesChanged														// header.msgh_id
			}
		};

		// Update the variable portion of the message
		message.mHeader.msgh_remote_port = destination;

		// Send the message
		mach_msg_return_t err = mach_msg(&message.mHeader, MACH_SEND_MSG, message.mHeader.msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
		if (MACH_MSG_SUCCESS != err)
		{
			// Delivery of the message failed, most likely because the client terminated, so simply destroy the message and continue
			mach_msg_destroy(&message.mHeader);
			DebugMessage("SampleAssistant::SendDeviceStatesChangedMessage() - Error sending notification to port %d - 0x%08X", destination, err);
		}
	}

}}}}


try
	{
		if (!serialHeader())
		{
			nlwarning("CDBReferenceFile::postwrite(): failed, cannot read file '%s' header", filepath.c_str());
			return false;
		}
	}
	catch (const Exception& e)
	{
		nlwarning("CDBReferenceFile::postwrite(): failed, cannot read file '%s' header, exception '%s'", filepath.c_str(), e.what());
		return false;
	}

// This source file has been modified by the following contributors:

#include "db_reference_file.h"
#include "pds_table_buffer.h"

#include <nel/misc/debug.h>

using namespace NLMISC;
using namespace std;

/*
 * Constructor
 */
CDBReferenceFile::CDBReferenceFile()
{
	clear();
}

/*
 * Destructor
 */
CDBReferenceFile::~CDBReferenceFile()
{
	clear();
}

/*
 * Clear initial setup
 */
void	CDBReferenceFile::clear()
{
	_Init = false;

	close();

	_Name.clear();
	_Path.clear();

	_Header.BaseIndex = 0;
	_Header.EndIndex = 0;
	_Header.OverIndex = 0;
	_Header.RowSize = 0;
	_Header.FullRowSize = 0;
	_Header.Timestamp = 0;

	_Mode = Read;

	_DataStart = 0;
}

/*
 * close file
 */
void	CDBReferenceFile::close()
{
	if (_File != NULL)
	{
		PDS_LOG_DEBUG(1)("CDBReferenceFile::clear(): closing file '%s%s' in %s mode", _Path.c_str(), _Name.c_str(), (_Mode == Read ? "Read" : "Update"));

		// in update mode, postwrite to validate file
		if (_Mode == Update)
		{
			postwrite();
		}
	}

	CMixedStreamFile::close();
}

/*
 * Setup file name and path
 */
void	CDBReferenceFile::setup(const string& name, const string& path, uint32 baseIndex, uint32 overIndex, uint32 rowSize)
{
	clear();

	uint32	tableId;
	uint32	refFile;
	nlassert(isRefFile(name, tableId, refFile));

	_Name = name;
	_Path = path;

	_Header.BaseIndex = baseIndex;
	_Header.EndIndex = baseIndex;
	_Header.OverIndex = overIndex;
	_Header.RowSize = rowSize;
	_Header.FullRowSize = rowSize + getRowHeaderSize();
	_Header.Timestamp = CTableBuffer::getCommonStamp(); //CStampHandler::getStamp();

	_Init = true;
}

/*
 * Builds an empty file
 */
bool	CDBReferenceFile::buildEmptyRef()
{
	string	filepath = _Path+_Name;

	if (_File != NULL)
	{
		nlwarning("CDBReferenceFile::buildEmptyRef(): failed, file '%s' already open", filepath.c_str());
		return false;
	}

	// check file doesn't exist yet
	if (CFile::fileExists(filepath))
	{
		nlwarning("CDBReferenceFile::buildEmptyRef(): failed, file '%s' already exists", filepath.c_str());
		return false;
	}

	if (!prewrite())
	{
		nlwarning("CDBReferenceFile::buildEmptyRef(): failed, cannot prewrite file '%s'", filepath.c_str());
		close();
		return false;
	}

	close();

	return true;
}

/*
 * Prewrite reference file.
 * At least, read file header to known the base and index index in file
 */
bool	CDBReferenceFile::prewrite(bool failIfNotExist)
{
	// check file already open
	if (_File != NULL)
		return true;

	string	filepath = _Path+_Name;

	// check file exists
	if (!CFile::fileExists(filepath))
	{
		if (failIfNotExist)
		{
			nlwarning("CDBReferenceFile::prewrite(): failed, file '%s' does not exist", filepath.c_str());
			return false;
		}

		// open file
		if (!open(filepath.c_str(), "wb"))
		{
			nlwarning("CDBReferenceFile::prewrite(): failed, cannot open file '%s' for write", filepath.c_str());
			return false;
		}

		// write header, set to output mode
		setInOut(false);
	}
	else
	{
		// open file
		if (!open(filepath.c_str(), "r+b"))
		{
			nlwarning("CDBReferenceFile::prewrite(): failed, cannot open file '%s' for read", filepath.c_str());
			return false;
		}

		if (fseek(_File, 0, SEEK_SET) != 0)
		{
			nlwarning("CDBReferenceFile::prewrite(): failed, cannot seek start of '%s' to read header", filepath.c_str());
			return false;
		}

		// read header, set to input mode
		setInOut(true);
	}

	// serial header
	try
	{
		if (!serialHeader())
		{
			nlwarning("CDBReferenceFile::prewrite(): failed, cannot read file '%s' header", filepath.c_str());
			return false;
		}
	}
	catch (const Exception& e)
	{
		nlwarning("CDBReferenceFile::prewrite(): failed, cannot read file '%s' header, exception '%s'", filepath.c_str(), e.what());
		return false;
	}

	// write data, set to output mode
	setInOut(false);

	_Mode = Update;

	PDS_LOG_DEBUG(1)("CDBReferenceFile::prewrite(): opened file '%s' in Update mode", filepath.c_str());

	return true;
}

/*
 * Postwrite reference file
 * Mark file as valid, close file, flush anything still alive...
 */
bool	CDBReferenceFile::postwrite()
{
	if (_File == NULL)
		return true;

	string	filepath = _Path+_Name;

	if (fseek(_File, 0, SEEK_SET) != 0)
	{
		nlwarning("CDBReferenceFile::postwrite(): failed, cannot seek start of '%s' to read header", filepath.c_str());
		return false;
	}

	// serial header

	return true;
}

/*
 * Update Start/End Delta Ids
 */
bool	CDBReferenceFile::updateDeltaIds(uint32 startId, uint32 endId)
{
	if (_Header.StartDeltaId == 0 && _Header.EndDeltaId == 0)
	{
		_Header.StartDeltaId = startId;
		_Header.EndDeltaId = endId;
		return true;
	}

	if (_Header.EndDeltaId != startId && _Header.EndDeltaId != startId-1)
	{
		string	filepath = _Path+_Name;
		nlwarning("CDBReferenceFile::updateDeltaIds(): non consecutive delta ids, file '%s' end=%d, update start=%d", filepath.c_str(), _Header.EndDeltaId, startId);
		return false;
	}

	_Header.EndDeltaId = endId;
	return true;
}

/*
 * Get Start/End Delta Ids
 */
void	CDBReferenceFile::getUpdateDeltaIds(uint32& startId, uint32& endId)
{
	startId = _Header.StartDeltaId;
	endId = _Header.EndDeltaId;
}

/*
 * Update a row in the reference file
 * \param index is the absolute row index to update, not relative to file base index
 * \param data is the data buffer to store in file
 */
bool	CDBReferenceFile::update(uint32 index, const uint8* rowdata)
{
	string	filepath = _Path+_Name;

	if (!prewrite())
	{
		nlwarning("CDBReferenceFile::update(): failed, failed to prewrite '%s'", filepath.c_str());
		return false;
	}

	if (_Mode != Update)
	{
		nlwarning("CDBReferenceFile::update(): failed, file '%s' not opened in Update mode", filepath.c_str());
		return false;
	}

	// check row belongs to file (not over OverIndex)
	if (index >= _Header.OverIndex)
	{
		nlwarning("CDBReferenceFile::update(): failed, index '%d' is over file '%s' limit '%s'", index, filepath.c_str(), _Header.OverIndex);
		return false;
	}

	// check row is not beyong physical file data
	if (index >= _Header.EndIndex)
	{
		// increase file size by filling blank, fill row indices where needed)

		// seek to end
		if (fseek(_File, getSeekPos(_Header.EndIndex), SEEK_SET) != 0)
		{
			nlwarning("CDBReferenceFile::update(): failed, can't seek to end in file '%s'", filepath.c_str());
			return false;
		}

		// allocate blank buffer
		uint8*	tempRowBuffer = new uint8[_Header.FullRowSize];
		memset(tempRowBuffer, 0, _Header.FullRowSize);

		// dump empty rows till we get to end index
		while (_Header.EndIndex <= index)
		{
			// setup row index
			*(uint32*)tempRowBuffer = index;
			if (!writeBuffer(tempRowBuffer, _Header.FullRowSize))
			{
				nlwarning("CDBReferenceFile::update(): failed, can't increase file '%s' size", filepath.c_str());
				delete[] tempRowBuffer;
				return false;
			}

			++_Header.EndIndex;
		}

		delete[] tempRowBuffer;
	}

	// seek to row in file
	if (fseek(_File, getSeekPos(index)+getRowHeaderSize(), SEEK_SET) != 0)
	{
		nlwarning("CDBReferenceFile::update(): failed, can't seek to index '%d' data in file '%s'", index, filepath.c_str());
		return false;
	}

	// write data
	if (!writeBuffer(rowdata, _Header.RowSize))
	{
		nlwarning("CDBReferenceFile::update(): failed, can't write index '%d' data in file '%s'", index, filepath.c_str());
		return false;
	}

	return true;
}

/*
 * Preload reference file.
 * At least, read file header to known the base and index index in file
 */
bool	CDBReferenceFile::preload()
{
	// check file already open
	if (_File != NULL)
		return true;

	// force read mode
	_Mode = Read;

	string	filepath = _Path+_Name;

	// file doesn't exist, do nothing
	if (!CFile::fileExists(filepath))
		return true;

	// open file
	if (!open(filepath.c_str(), "rb"))
	{
		nlwarning("CDBReferenceFile::preload(): failed, cannot open file '%s'", filepath.c_str());
		return false;
	}

	PDS_LOG_DEBUG(1)("CDBReferenceFile::preload(): opened file '%s' in Read mode", filepath.c_str());

	setInOut(true);

	// serial header
	try
	{
		if (!serialHeader())
		{
			nlwarning("CDBReferenceFile::preload(): failed, cannot read file '%s' header", filepath.c_str());
			return false;
		}
	}
	catch (const Exception& e)
	{
		nlwarning("CDBReferenceFile::preload(): failed, cannot read file '%s' header, exception '%s'", filepath.c_str(), e.what());
		return false;
	}

	return true;
}

/*
 * Read a row in the reference file
 * \param index is the absolute row index to read, not relative to file base index
 * \param data is the data buffer to store data read from file
 */
bool	CDBReferenceFile::read(uint32 index, uint8* rowdata)
{
	string	filepath = _Path+_Name;

	// preload will fail only if file exists and cannot be read
	// preload returns true when everything ok or file doesn't exist
	if (!preload())
	{
		nlwarning("CDBReferenceFile::read(): failed, failed to preload '%s'", filepath.c_str());
		return false;
	}

	if (_Mode != Read)
	{
		nlwarning("CDBReferenceFile::read(): failed, file '%s' not opened in Read mode", filepath.c_str());
		return false;
	}

	// check row belongs to file (not over OverIndex)
	if (index >= _Header.OverIndex)
	{
		nlwarning("CDBReferenceFile::read(): failed, index '%d' is over file '%s' end '%d'", index, filepath.c_str(), _Header.OverIndex);
		return false;
	}

	// check file opened or row is not beyond file end
	if (_File == NULL || index >= _Header.EndIndex)
	{
		PDS_LOG_DEBUG(1)("CDBReferenceFile::read(): row '%d' is beyond file '%s' end '%d', row is empty", index, filepath.c_str(), _Header.EndIndex);
		memset(rowdata, 0, _Header.RowSize);
		return true;
	}

	// seek to row in file
	if (fseek(_File, getSeekPos(index)+getRowHeaderSize(), SEEK_SET) != 0)
	{
		nlwarning("CDBReferenceFile::read(): failed, can't seek to index '%d' data in file '%s'", index, filepath.c_str());
		return false;
	}

	// write data
	if (!readBuffer(rowdata, _Header.RowSize))
	{
		nlwarning("CDBReferenceFile::read(): failed, can't read index '%d' data in file '%s'", index, filepath.c_str());
		return false;
	}

	return true;
}

/*
 * Serial file header
 */
bool	CDBReferenceFile::serialHeader()
{
	serialCheck(NELID("DbRf"));
	uint	version = serialVersion(0);

	if (isReading())
	{
		// on reading, read header in a temp buffer
		CRefHeader	hdr;
		serial(hdr);

		// check header complies
		if ((_Header.BaseIndex != 0 && hdr.BaseIndex != _Header.BaseIndex) || (isReading() && _Header.RowSize != 0 && hdr.RowSize != _Header.RowSize))
			return false;

		// compy temp to header
		_Header = hdr;
	}
	else
	{
		serial(_Header);
	}

	serialCheck(NELID("Data"));

	_DataStart = ftell(_File);

	if (isReading())
	{
		// get file size to compute real EndIndex
		uint32	filesize = CFile::getFileSize(_File);

		uint32	numRows = (filesize-_DataStart) / _Header.FullRowSize;
		// check exact number of rows in file...
		if ((filesize-_DataStart) % _Header.FullRowSize != 0)
		{
			nlwarning("CDBReferenceFile::serialHeader(): failed, file doesn't contain an exact number of rows");
			return false;
		}

		// compute exact end index
		_Header.EndIndex = _Header.BaseIndex + numRows;
	}

	return true;
}


try {
      fields_.emplace_back(
        name, features, *field_features_,
        *feature_columns_, cached_features_, columns,
        byte_writer_, int_writer_,
        index_features, (nullptr != comparator_));
    } catch (...) {
      fields_map_.erase(it);
    }

/// Copyright holder is EMC Corporation
/// @author Andrey Abramov
/// @author Vasiliy Nabatchikov

#include "shared.hpp"

#include <set>
#include <algorithm>
#include <cassert>

#include "index/comparer.hpp"
#include "index/field_data.hpp"
#include "index/field_meta.hpp"
#include "index/norm.hpp"

#include "formats/formats.hpp"

#include "store/directory.hpp"
#include "store/store_utils.hpp"

#include "analysis/analyzer.hpp"
#include "analysis/token_attributes.hpp"
#include "analysis/token_streams.hpp"

#include "utils/bit_utils.hpp"
#include "utils/io_utils.hpp"
#include "utils/log.hpp"
#include "utils/lz4compression.hpp"
#include "utils/map_utils.hpp"
#include "utils/memory.hpp"
#include "utils/object_pool.hpp"
#include "utils/timer_utils.hpp"
#include "utils/type_limits.hpp"
#include "utils/bytes_utils.hpp"

namespace {

using namespace irs;

const byte_block_pool EMPTY_POOL;

void accumulate_features(feature_set_t& accum, const feature_map_t& features) {
  for (auto& entry : features) {
    accum.emplace(entry.first);
  }
}

template<typename Stream>
void write_offset(
    posting& p,
    Stream& out,
    const uint32_t base,
    const offset& offs) {
  const uint32_t start_offset = base + offs.start;
  const uint32_t end_offset = base + offs.end;

  assert(start_offset >= p.offs);

  irs::vwrite<uint32_t>(out, start_offset - p.offs);
  irs::vwrite<uint32_t>(out, end_offset - start_offset);

  p.offs = start_offset;
}

template<typename Stream>
void write_prox(
    Stream& out,
    uint32_t prox,
    const irs::payload* pay,
    IndexFeatures& features) {
  if (!pay || pay->value.empty()) {
    irs::vwrite<uint32_t>(out, shift_pack_32(prox, false));
  } else {
    irs::vwrite<uint32_t>(out, shift_pack_32(prox, true));
    irs::vwrite<size_t>(out, pay->value.size());
    out.write(pay->value.c_str(), pay->value.size());

    // saw payloads
    features |= IndexFeatures::PAY;
  }
}

template<typename Inserter>
FORCE_INLINE void write_cookie(Inserter& out, uint64_t cookie) {
  *out = static_cast<byte_type>(cookie & 0xFF); // offset
  irs::vwrite<uint32_t>(out, static_cast<uint32_t>((cookie >> 8) & 0xFFFFFFFF)); // slice offset
}

FORCE_INLINE uint64_t cookie(size_t slice_offset, size_t offset) noexcept {
  assert(offset <= std::numeric_limits<byte_type>::max());
  return static_cast<uint64_t>(slice_offset) << 8 | static_cast<byte_type>(offset);
}

template<typename Reader>
FORCE_INLINE uint64_t read_cookie(Reader& in) {
  const size_t offset = *in; ++in;
  const size_t slice_offset = irs::vread<uint32_t>(in);
  return cookie(slice_offset, offset);
}

FORCE_INLINE uint64_t cookie(const byte_block_pool::sliced_greedy_inserter& stream) noexcept {
  // we don't span slices over the buffers
  const auto slice_offset = stream.slice_offset();
  return cookie(slice_offset, stream.pool_offset() - slice_offset);
}

FORCE_INLINE byte_block_pool::sliced_greedy_reader greedy_reader(
    const byte_block_pool& pool,
    uint64_t cookie) noexcept {
  return byte_block_pool::sliced_greedy_reader(
    pool,
    static_cast<size_t>((cookie >> 8) & 0xFFFFFFFF),
    static_cast<size_t>(cookie & 0xFF)
  );
}

FORCE_INLINE byte_block_pool::sliced_greedy_inserter greedy_writer(
    byte_block_pool::inserter& writer,
    uint64_t cookie) noexcept {
  return byte_block_pool::sliced_greedy_inserter(
    writer,
    static_cast<size_t>((cookie >> 8) & 0xFFFFFFFF),
    static_cast<size_t>(cookie & 0xFF)
  );
}

template<typename Reader>
class pos_iterator final : public irs::position {
 public:
  pos_iterator()
    : prox_in_(EMPTY_POOL) {
  }

  void clear() noexcept {
    pos_ = 0;
    value_ = pos_limits::invalid();
    offs_.clear();
    pay_.value = bytes_ref::NIL;
  }

  // reset field
  void reset(IndexFeatures features, const frequency& freq) {
    assert(IndexFeatures::FREQ == (features & IndexFeatures::FREQ));

    freq_ = &freq;

    std::get<attribute_ptr<offset>>(attrs_) =
      IndexFeatures::NONE != (features & IndexFeatures::OFFS) ? &offs_ : nullptr;

    std::get<attribute_ptr<payload>>(attrs_) =
      IndexFeatures::NONE != (features & IndexFeatures::PAY) ? &pay_ : nullptr;
  }

  // reset value
  void reset(const Reader& prox) {
    clear();
    prox_in_ = prox;
  }

  virtual attribute* get_mutable(irs::type_info::type_id id) noexcept override final {
    return irs::get_mutable(attrs_, id);
  }

  virtual bool next() override {
    assert(freq_);

    if (pos_ == freq_->value) {
      value_ = irs::pos_limits::eof();

      return false;
    }

    uint32_t pos;

    if (shift_unpack_32(irs::vread<uint32_t>(prox_in_), pos)) {
      const size_t size = irs::vread<size_t>(prox_in_);
      payload_value_.resize(size);
      prox_in_.read(const_cast<byte_type*>(payload_value_.data()), size);
      pay_.value = payload_value_;
    }

    value_ += pos;
    assert(pos_limits::valid(value_));

    if (std::get<attribute_ptr<offset>>(attrs_).ptr) {
      offs_.start += irs::vread<uint32_t>(prox_in_);
      offs_.end = offs_.start + irs::vread<uint32_t>(prox_in_);
    }

    ++pos_;

    return true;
  }

  virtual void reset() override {
    assert(false); // unsupported
  }

 private:
  using attributes = std::tuple<attribute_ptr<offset>, attribute_ptr<payload>>;

  Reader prox_in_;
  bstring payload_value_;
  const frequency* freq_{}; // number of term positions in a document
  payload pay_;
  offset offs_;
  attributes attrs_;
  uint32_t pos_{}; // current position
}; // pos_iterator

}

namespace iresearch {
namespace detail {

class doc_iterator final : public irs::doc_iterator {
 public:
  doc_iterator() noexcept
   : freq_in_(EMPTY_POOL) {
  }

  // reset field
  void reset(const field_data& field) {
    field_ = &field;
    auto& freq = std::get<attribute_ptr<frequency>>(attrs_);
    auto& pos = std::get<attribute_ptr<position>>(attrs_);
    freq = nullptr;
    pos = nullptr;
    has_cookie_ = false;

    const auto features = field.meta().index_features;
    if (IndexFeatures::NONE != (features & IndexFeatures::FREQ)) {
      freq = &freq_;

      if (IndexFeatures::NONE != (features & IndexFeatures::POS)) {
        pos_.reset(features, freq_);
        pos = &pos_;
        has_cookie_ = field.prox_random_access();
      }
    }
  }

  // reset term
  void reset(
      const irs::posting& posting,
      const byte_block_pool::sliced_reader& freq,
      const byte_block_pool::sliced_reader* prox) {
    doc_.value = 0;
    freq_.value = 0;
    cookie_ = 0;
    freq_in_ = freq;
    posting_ = &posting;

    const auto& ppos = std::get<attribute_ptr<position>>(attrs_);

    if (ppos.ptr && prox) {
      // reset positions only once,
      // as we need iterator for sequential reads
      pos_.reset(*prox);
    }
  }

  uint64_t cookie() const noexcept {
    return cookie_;
  }

  size_t cost() const noexcept {
    return posting_->size;
  }

  virtual attribute* get_mutable(irs::type_info::type_id type) noexcept override final {
    return irs::get_mutable(attrs_, type);
  }

  virtual doc_id_t seek(doc_id_t doc) override {
    irs::seek(*this, doc);
    return value();
  }

  virtual doc_id_t value() const noexcept override {
    return doc_.value;
  }

  virtual bool next() override {
    if (freq_in_.eof()) {
      if (!posting_) {
        return false;
      }

      doc_.value = posting_->doc;
      freq_.value = posting_->freq;

      if (has_cookie_) {
        // read last cookie
        cookie_ = *field_->int_writer_->parent().seek(posting_->int_start+3);
      }

      posting_ = nullptr;
    } else {
      if (std::get<attribute_ptr<frequency>>(attrs_).ptr) {
        uint64_t delta;

        if (shift_unpack_64(irs::vread<uint64_t>(freq_in_), delta)) {
          freq_.value = 1U;
        } else {
          freq_.value = irs::vread<uint32_t>(freq_in_);
        }

        assert(delta < doc_limits::eof());
        doc_.value += doc_id_t(delta);

        if (has_cookie_) {
          cookie_ += read_cookie(freq_in_);
        }
      } else {
        doc_.value += irs::vread<uint32_t>(freq_in_);
      }

      assert(doc_.value != posting_->doc);
    }

    pos_.clear();

    return true;
  }

 private:
  using attributes = std::tuple<
    attribute_ptr<frequency>, attribute_ptr<position>>;

  const field_data* field_{};
  uint64_t cookie_{};
  document doc_;
  frequency freq_;
  pos_iterator<byte_block_pool::sliced_reader> pos_;
  byte_block_pool::sliced_reader freq_in_;
  const posting* posting_{};
  attributes attrs_;
  bool has_cookie_{false}; // FIXME remove
};

class sorting_doc_iterator final : public irs::doc_iterator {
 public:
  // reset field
  void reset(const field_data& field) {
    assert(field.prox_random_access());
    byte_pool_ = &field.byte_writer_->parent();

    auto& pfreq = std::get<attribute_ptr<frequency>>(attrs_);
    auto& ppos = std::get<attribute_ptr<position>>(attrs_);
    pfreq = nullptr;
    ppos = nullptr;

    const auto features = field.meta().index_features;
    if (IndexFeatures::NONE != (features & IndexFeatures::FREQ)) {
      pfreq = &freq_;

      if (IndexFeatures::NONE != (features & IndexFeatures::POS)) {
        pos_.reset(features, freq_);
        ppos = &pos_;
      }
    }
  }

  // reset iterator,
  // docmap == null -> segment is already sorted
  void reset(detail::doc_iterator& it, const std::vector<doc_id_t>* docmap) {
    const frequency no_frequency;
    const frequency* freq = &no_frequency;

    const auto* freq_attr = irs::get<frequency>(it);
    if (freq_attr) {
      freq = freq_attr;
    }

    docs_.reserve(it.cost());
    docs_.clear();

    if (!docmap) {
      reset_already_sorted(it, *freq);
    } else if (irs::use_dense_sort(it.cost(), docmap->size()-1)) { // -1 for first element
      reset_dense(it, *freq, *docmap);
    } else {
      reset_sparse(it, *freq, *docmap);
    }

    std::get<document>(attrs_).value = irs::doc_limits::invalid();
    freq_.value = 0;
    it_ = docs_.begin();
  }

  virtual attribute* get_mutable(irs::type_info::type_id type) noexcept override final {
    return irs::get_mutable(attrs_, type);
  }

  virtual doc_id_t seek(doc_id_t doc) noexcept override {
    irs::seek(*this, doc);
    return value();
  }

  virtual doc_id_t value() const noexcept override {
    return std::get<document>(attrs_).value;
  }

  virtual bool next() noexcept override {
    // cppcheck-suppress shadowFunction
    auto& value = std::get<document>(attrs_);

    while (it_ != docs_.end()) {
      if (doc_limits::eof(it_->doc)) {
        // skip invalid docs
        ++it_;
        continue;
      }

      auto& doc = *it_;
      value.value = doc.doc;
      freq_.value = doc.freq;

      if (doc.cookie) {
        // (cookie != 0) -> we have proximity data
        pos_.reset(greedy_reader(*byte_pool_, doc.cookie));
      }

      ++it_;
      return true;

    }

    value.value = doc_limits::eof();
    freq_.value = 0;
    return false;
  }

 private:
  using attributes = std::tuple<
    document, attribute_ptr<frequency>, attribute_ptr<position>>;

  struct doc_entry {
    doc_entry() = default;
    doc_entry(doc_id_t doc, uint32_t freq, uint64_t cookie) noexcept
      : doc(doc), freq(freq), cookie(cookie) {
    }

    doc_id_t doc{ doc_limits::eof() }; // doc_id
    uint32_t freq; // freq
    uint64_t cookie; // prox_cookie
  }; // doc_entry

  void reset_dense(
      detail::doc_iterator& it,
      const frequency& freq,
      const std::vector<doc_id_t>& docmap) {
    assert(!docmap.empty());
    assert(irs::use_dense_sort(it.cost(), docmap.size()-1)); // -1 for first element

    docs_.resize(docmap.size()-1); // -1 for first element

    while (it.next()) {
      assert(it.value() - doc_limits::min() < docmap.size());
      const auto new_doc = docmap[it.value()];

      if (doc_limits::eof(new_doc)) {
        // skip invalid documents
        continue;
      }

      auto& doc = docs_[new_doc - doc_limits::min()];
      doc.doc = new_doc;
      doc.freq = freq.value;
      doc.cookie = it.cookie();
    }
  }

  void reset_sparse(
      detail::doc_iterator& it,
      const frequency& freq,
      const std::vector<doc_id_t>& docmap) {
    assert(!docmap.empty());
    assert(!irs::use_dense_sort(it.cost(), docmap.size()-1)); // -1 for first element

    while (it.next()) {
      assert(it.value() - doc_limits::min() < docmap.size());
      const auto new_doc = docmap[it.value()];

      if (doc_limits::eof(new_doc)) {
        // skip invalid documents
        continue;
      }

      docs_.emplace_back(new_doc, freq.value, it.cookie());
    }

    std::sort(
      docs_.begin(), docs_.end(),
      [](const doc_entry& lhs, const doc_entry& rhs) noexcept {
        return lhs.doc < rhs.doc;
    });
  }

  void reset_already_sorted(detail::doc_iterator& it, const frequency& freq) {
    while (it.next()) {
      docs_.emplace_back(it.value(), freq.value, it.cookie());
    }
  }

  const byte_block_pool* byte_pool_{};
  std::vector<doc_entry>::const_iterator it_;
  std::vector<doc_entry> docs_;
  pos_iterator<byte_block_pool::sliced_greedy_reader> pos_;
  frequency freq_;
  attributes attrs_;
}; // sorting_doc_iterator

class term_iterator : public irs::term_iterator {
 public:
  explicit term_iterator(
      fields_data::postings_ref_t& postings,
      const doc_map* docmap) noexcept
    : postings_(&postings),
      doc_map_(docmap) {
  }

  void reset(
      const field_data& field,
      const bytes_ref*& min,
      const bytes_ref*& max) {
    field_ = &field;

    doc_itr_.reset(field);
    if (field.prox_random_access()) {
      sorting_doc_itr_.reset(field);
    }

    // reset state
    field_->terms_.get_sorted_postings(*postings_);
    next_ = it_ = postings_->begin();
    end_ = postings_->end();

    max = min = &irs::bytes_ref::NIL;
    if (it_ != end_) {
      min = &(*it_)->term;
      max = &(*(end_ - 1))->term;
    }
  }

  virtual const bytes_ref& value() const noexcept override {
    assert(it_ != end_);
    return (*it_)->term;
  }

  virtual attribute* get_mutable(irs::type_info::type_id) noexcept override {
    return nullptr;
  }

  virtual void read() noexcept override {
    // Does nothing now
  }

  virtual irs::doc_iterator::ptr postings(IndexFeatures /*features*/) const override {
    REGISTER_TIMER_DETAILED();
    assert(it_ != end_);

    return (this->*POSTINGS[size_t(field_->prox_random_access())])(**it_);
  }

  virtual bool next() override {
    if (next_ == end_) {
      return false;
    }

    it_ = next_;
    ++next_;
    return true;
  }

  const field_meta& meta() const noexcept {
    return field_->meta();
  }

 private:
  typedef irs::doc_iterator::ptr(term_iterator::*postings_f)(const posting&) const;

  static const postings_f POSTINGS[2];

  irs::doc_iterator::ptr postings(const posting& posting) const {
    assert(!doc_map_);

    // where the term data starts
    auto ptr = field_->int_writer_->parent().seek(posting.int_start);
    const auto freq_end = *ptr; ++ptr;
    const auto prox_end = *ptr; ++ptr;
    const auto freq_begin = *ptr; ++ptr;
    const auto prox_begin = *ptr;

    auto& pool = field_->byte_writer_->parent();
    const byte_block_pool::sliced_reader freq(pool, freq_begin, freq_end); // term's frequencies
    const byte_block_pool::sliced_reader prox(pool, prox_begin, prox_end); // term's proximity // TODO: create on demand!!!

    doc_itr_.reset(posting, freq, &prox);
    return memory::to_managed<irs::doc_iterator, false>(&doc_itr_);
  }

  irs::doc_iterator::ptr sort_postings(const posting& posting) const {
    // where the term data starts
    auto ptr = field_->int_writer_->parent().seek(posting.int_start);
    const auto freq_end = *ptr; ++ptr;
    const auto freq_begin = *ptr;

    auto& pool = field_->byte_writer_->parent();
    const byte_block_pool::sliced_reader freq(pool, freq_begin, freq_end); // term's frequencies

    doc_itr_.reset(posting, freq, nullptr);
    sorting_doc_itr_.reset(doc_itr_, doc_map_);
    return memory::to_managed<irs::doc_iterator, false>(&sorting_doc_itr_);
  }

  fields_data::postings_ref_t* postings_{};
  fields_data::postings_ref_t::const_iterator end_;
  fields_data::postings_ref_t::const_iterator next_;
  fields_data::postings_ref_t::const_iterator it_;
  const field_data* field_{};
  const doc_map* doc_map_{};
  mutable detail::doc_iterator doc_itr_;
  mutable detail::sorting_doc_iterator sorting_doc_itr_;
}; // term_iterator

/*static*/ const term_iterator::postings_f term_iterator::POSTINGS[2] {
  &term_iterator::postings,
  &term_iterator::sort_postings
};

class term_reader final : public irs::basic_term_reader,
                          private util::noncopyable {
 public:
  explicit term_reader(
      fields_data::postings_ref_t& postings,
      const doc_map* docmap) noexcept
    : it_(postings, docmap) {
  }

  void reset(const field_data& field) {
    it_.reset(field, min_, max_);
  }

  virtual const irs::bytes_ref& (min)() const noexcept override {
    return *min_;
  }

  virtual const irs::bytes_ref& (max)() const noexcept override {
    return *max_;
  }

  virtual const irs::field_meta& meta() const noexcept override {
    return it_.meta();
  }

  virtual irs::term_iterator::ptr iterator() const noexcept override {
    return memory::to_managed<irs::term_iterator, false>(&it_);
  }

  virtual attribute* get_mutable(irs::type_info::type_id) noexcept override {
    return nullptr;
  }

 private:
  mutable detail::term_iterator it_;
  const irs::bytes_ref* min_{ &irs::bytes_ref::NIL };
  const irs::bytes_ref* max_{ &irs::bytes_ref::NIL };
}; // term_reader

} // detail

// --SECTION--                                         field_data implementation

/*static*/ const field_data::process_term_f field_data::TERM_PROCESSING_TABLES[2][2] = {
  {
    &field_data::add_term,
    &field_data::new_term
  },

  {
    &field_data::add_term_random_access,
    &field_data::new_term_random_access
  }
};

field_data::field_data(
    const string_ref& name,
    const features_t& features,
    const field_features_t& field_features,
    const feature_column_info_provider_t& feature_columns,
    std::deque<cached_column>& cached_features,
    columnstore_writer& columns,
    byte_block_pool::inserter& byte_writer,
    int_block_pool::inserter& int_writer,
    IndexFeatures index_features,
    bool random_access)
  : meta_(name, index_features),
    terms_(*byte_writer),
    byte_writer_(&byte_writer),
    int_writer_(&int_writer),
    proc_table_(TERM_PROCESSING_TABLES[size_t(random_access)]),
    last_doc_(doc_limits::invalid()) {
  features_.reserve(field_features.size());
  for (const type_info::type_id feature : features) {
    const auto it = field_features.find(feature);

    if (IRS_UNLIKELY(it == field_features.end())) {
      assert(false);
      continue;
    }

    if (it->second) {
      assert(feature_columns);

      if (random_access) {
        // sorted index case
        auto* id = &meta_.features[feature];
        *id = field_limits::invalid();
        auto& stream = cached_features.emplace_back(id, feature_columns(feature));

        features_.emplace_back(
            it->second,
            [stream = &stream.stream](doc_id_t doc) mutable -> column_output& {
              stream->prepare(doc);
              return *stream; });
      } else {
        auto [id, handler] = columns.push_column(feature_columns(feature));
        features_.emplace_back(it->second, std::move(handler));
        meta_.features[feature] = id;
      }
    } else {
      meta_.features[feature] = field_limits::invalid();
    }
  }
}

void field_data::reset(doc_id_t doc_id) {
  assert(doc_limits::valid(doc_id));

  if (doc_id == last_doc_) {
    return; // nothing to do
  }

  pos_ = pos_limits::invalid();
  last_pos_ = 0;
  stats_ = {};
  offs_ = 0;
  last_start_offs_ = 0;
  last_doc_ = doc_id;
  seen_ = false;
}

void field_data::new_term(
    posting& p,
    doc_id_t did,
    const payload* pay,
    const offset* offs) {
  // where pointers to data starts
  p.int_start = int_writer_->pool_offset();

  const auto freq_start = byte_writer_->alloc_slice(); // pointer to freq stream
  const auto prox_start = byte_writer_->alloc_slice(); // pointer to prox stream
  *int_writer_ = freq_start; // freq stream end
  *int_writer_ = prox_start; // prox stream end
  *int_writer_ = freq_start; // freq stream start
  *int_writer_ = prox_start; // prox stream start

  p.doc = did;
  if (IndexFeatures::NONE == (meta_.index_features & IndexFeatures::FREQ)) {
    p.doc_code = did;
  } else {
    p.doc_code = uint64_t(did) << 1;
    p.freq = 1;

    if (IndexFeatures::NONE != (meta_.index_features & IndexFeatures::POS)) {
      auto& prox_stream_end = *int_writer_->parent().seek(p.int_start + 1);
      byte_block_pool::sliced_inserter prox_out(*byte_writer_, prox_stream_end);

      write_prox(prox_out, pos_, pay, meta_.index_features);

      if (IndexFeatures::NONE != (meta_.index_features & IndexFeatures::OFFS)) {
        assert(offs);
        write_offset(p, prox_out, offs_, *offs);
      }

      prox_stream_end = prox_out.pool_offset();
      p.pos = pos_;
    }
  }

  stats_.max_term_freq = std::max(1U, stats_.max_term_freq);
  ++stats_.num_unique;
}

void field_data::add_term(
    posting& p,
    doc_id_t did,
    const payload* pay,
    const offset* offs) {
  if (IndexFeatures::NONE == (meta_.index_features & IndexFeatures::FREQ)) {
    if (p.doc != did) {
      assert(did > p.doc);

      auto& doc_stream_end = *int_writer_->parent().seek(p.int_start);
      byte_block_pool::sliced_inserter doc_out(*byte_writer_, doc_stream_end);
      irs::vwrite<uint32_t>(doc_out, doc_id_t(p.doc_code));
      doc_stream_end = doc_out.pool_offset();

      p.doc_code = did - p.doc;
      p.doc = did;
      ++stats_.num_unique;
    }
  } else if (p.doc != did) {
    assert(did > p.doc);

    auto& doc_stream_end = *int_writer_->parent().seek(p.int_start);
    byte_block_pool::sliced_inserter doc_out(*byte_writer_, doc_stream_end);

    if (1U == p.freq) {
      irs::vwrite<uint64_t>(doc_out, p.doc_code | UINT64_C(1));
    } else {
      irs::vwrite<uint64_t>(doc_out, p.doc_code);
      irs::vwrite<uint32_t>(doc_out, p.freq);
    }

    p.doc_code = uint64_t(did - p.doc) << 1;
    p.freq = 1;

    p.doc = did;
    stats_.max_term_freq = std::max(1U, stats_.max_term_freq);
    ++stats_.num_unique;

    if (IndexFeatures::NONE != (meta_.index_features & IndexFeatures::POS)) {
      auto& prox_stream_end = *int_writer_->parent().seek(p.int_start+1);
      byte_block_pool::sliced_inserter prox_out(*byte_writer_, prox_stream_end);

      write_prox(prox_out, pos_, pay, meta_.index_features);

      if (IndexFeatures::NONE != (meta_.index_features & IndexFeatures::OFFS)) {
        assert(offs);
        p.offs = 0; // reset base offset
        write_offset(p, prox_out, offs_, *offs);
      }

      prox_stream_end = prox_out.pool_offset();
      p.pos = pos_;
    }

    doc_stream_end = doc_out.pool_offset();
  } else { // exists in current doc
    stats_.max_term_freq = std::max(++p.freq, stats_.max_term_freq);
    if (IndexFeatures::NONE != (meta_.index_features & IndexFeatures::POS)) {
      auto& prox_stream_end = *int_writer_->parent().seek(p.int_start+1);
      byte_block_pool::sliced_inserter prox_out(*byte_writer_, prox_stream_end);

      write_prox(prox_out, pos_ - p.pos, pay, meta_.index_features);

      if (IndexFeatures::NONE != (meta_.index_features & IndexFeatures::OFFS)) {
        assert(offs);
        write_offset(p, prox_out, offs_, *offs);
      }

      prox_stream_end = prox_out.pool_offset();
      p.pos = pos_;
    }
  }
}

void field_data::new_term_random_access(
    posting& p,
    doc_id_t did,
    const payload* pay,
    const offset* offs) {
  // where pointers to data starts
  p.int_start = int_writer_->pool_offset();

  const auto freq_start = byte_writer_->alloc_slice(); // pointer to freq stream
  const auto prox_start = byte_writer_->alloc_greedy_slice(); // pointer to prox stream
  *int_writer_ = freq_start; // freq stream end
  *int_writer_ = freq_start; // freq stream start

  const auto cookie = ::cookie(prox_start, 1);
  *int_writer_ = cookie; // end cookie
  *int_writer_ = cookie; // start cookie
  *int_writer_ = 0;      // last start cookie

  p.doc = did;
  if (IndexFeatures::NONE == (meta_.index_features & IndexFeatures::FREQ)) {
    p.doc_code = did;
  } else {
    p.doc_code = uint64_t(did) << 1;
    p.freq = 1;

    if (IndexFeatures::NONE != (meta_.index_features & IndexFeatures::POS)) {
      byte_block_pool::sliced_greedy_inserter prox_out(*byte_writer_, prox_start, 1);

      write_prox(prox_out, pos_, pay, meta_.index_features);

      if (IndexFeatures::NONE != (meta_.index_features & IndexFeatures::OFFS)) {
        assert(offs);
        write_offset(p, prox_out, offs_, *offs);
      }

      auto& end_cookie = *int_writer_->parent().seek(p.int_start+2);
      end_cookie = ::cookie(prox_out); // prox stream end cookie

      p.pos = pos_;
    }
  }

  stats_.max_term_freq = std::max(1U, stats_.max_term_freq);
  ++stats_.num_unique;
}

void field_data::add_term_random_access(
    posting& p,
    doc_id_t did,
    const payload* pay,
    const offset* offs) {
  if (IndexFeatures::NONE == (meta_.index_features & IndexFeatures::FREQ)) {
    if (p.doc != did) {
      assert(did > p.doc);

      auto& doc_stream_end = *int_writer_->parent().seek(p.int_start);
      byte_block_pool::sliced_inserter doc_out(*byte_writer_, doc_stream_end);
      irs::vwrite<uint32_t>(doc_out, doc_id_t(p.doc_code));
      doc_stream_end = doc_out.pool_offset();

      ++p.size;
      p.doc_code = did - p.doc;
      p.doc = did;
      ++stats_.num_unique;
    }
  } else if (p.doc != did) {
    assert(did > p.doc);

    auto& doc_stream_end = *int_writer_->parent().seek(p.int_start);
    byte_block_pool::sliced_inserter doc_out(*byte_writer_, doc_stream_end);

    if (1U == p.freq) {
      irs::vwrite<uint64_t>(doc_out, p.doc_code | UINT64_C(1));
    } else {
      irs::vwrite<uint64_t>(doc_out, p.doc_code);
      irs::vwrite<uint32_t>(doc_out, p.freq);
    }

    ++p.size;
    p.doc_code = uint64_t(did - p.doc) << 1;
    p.freq = 1;

    p.doc = did;
    stats_.max_term_freq = std::max(1U, stats_.max_term_freq);
    ++stats_.num_unique;

    if (IndexFeatures::NONE != (meta_.index_features & IndexFeatures::POS)) {
      auto prox_stream_cookie = int_writer_->parent().seek(p.int_start+2);

      auto& end_cookie = *prox_stream_cookie; ++prox_stream_cookie;
      auto& start_cookie = *prox_stream_cookie; ++prox_stream_cookie;
      auto& last_start_cookie = *prox_stream_cookie;

      write_cookie(doc_out, start_cookie - last_start_cookie);
      // cppcheck-suppress selfAssignment
      last_start_cookie = start_cookie; // update previous cookie
      // cppcheck-suppress selfAssignment
      start_cookie = end_cookie; // update start cookie

      auto prox_out = greedy_writer(*byte_writer_, end_cookie);

      write_prox(prox_out, pos_, pay, meta_.index_features);

      if (IndexFeatures::NONE != (meta_.index_features & IndexFeatures::OFFS)) {
        assert(offs);
        p.offs = 0; // reset base offset
        write_offset(p, prox_out, offs_, *offs);
      }

      end_cookie = cookie(prox_out);
      p.pos = pos_;
    }

    doc_stream_end = doc_out.pool_offset();
  } else { // exists in current doc
    stats_.max_term_freq = std::max(++p.freq, stats_.max_term_freq);
    if (IndexFeatures::NONE != (meta_.index_features & IndexFeatures::POS)) {
      // update end cookie
      auto& end_cookie = *int_writer_->parent().seek(p.int_start+2);
      auto prox_out = greedy_writer(*byte_writer_, end_cookie);

      write_prox(prox_out, pos_ - p.pos, pay, meta_.index_features);

      if (IndexFeatures::NONE != (meta_.index_features & IndexFeatures::OFFS)) {
        assert(offs);
        write_offset(p, prox_out, offs_, *offs);
      }

      end_cookie = cookie(prox_out);
      p.pos = pos_;
    }
  }
}

bool field_data::invert(token_stream& stream, doc_id_t id) {
  REGISTER_TIMER_DETAILED();
  assert(id < doc_limits::eof()); // 0-based document id

  const auto* term = get<term_attribute>(stream);
  const auto* inc = get<increment>(stream);
  const offset* offs = nullptr;
  const payload* pay = nullptr;

  if (!inc) {
    IR_FRMT_ERROR(
      "field '%s' missing required token_stream attribute '%s'",
      meta_.name.c_str(), type<increment>::name().c_str());
    return false;
  }

  if (!term) {
    IR_FRMT_ERROR(
      "field '%s' missing required token_stream attribute '%s'",
      meta_.name.c_str(), type<term_attribute>::name().c_str());
    return false;
  }

  if (IndexFeatures::NONE != (meta_.index_features & IndexFeatures::OFFS)) {
    offs = get<offset>(stream);

    if (offs) {
      pay = get<payload>(stream);
    }
  }

  reset(id); // initialize field_data for the supplied doc_id

  while (stream.next()) {
    pos_ += inc->value;

    if (pos_ < last_pos_) {
      IR_FRMT_ERROR("invalid position %u < %u in field '%s'",
                    pos_, last_pos_, meta_.name.c_str());
      return false;
    }

    if (pos_ >= pos_limits::eof()) {
      IR_FRMT_ERROR("invalid position %u >= %u in field '%s'",
                    pos_, pos_limits::eof(), meta_.name.c_str());
      return false;
    }

    if (0 == inc->value) {
      ++stats_.num_overlap;
    }

    if (offs) {
      const uint32_t start_offset = offs_ + offs->start;
      const uint32_t end_offset = offs_ + offs->end;

      if (start_offset < last_start_offs_ || end_offset < start_offset) {
        IR_FRMT_ERROR("invalid offset start=%u end=%u in field '%s'",
                      start_offset, end_offset, meta_.name.c_str());
        return false;
      }

      last_start_offs_ = start_offset;
    }

    const auto res = terms_.emplace(term->value);

    if (nullptr == res.first) {
      IR_FRMT_WARN("skipping too long term of size '" IR_SIZE_T_SPECIFIER "' in field '%s'",
                    term->value.size(), meta_.name.c_str());
      IR_FRMT_TRACE("field '%s' contains too long term '%s'",
                    meta_.name.c_str(), ref_cast<char>(term->value).c_str());
      continue;
    }

    (this->*proc_table_[size_t(res.second)])(*res.first, id, pay, offs);

    if (0 == ++stats_.len) {
      IR_FRMT_ERROR(
        "too many tokens in field '%s', document '" IR_UINT32_T_SPECIFIER "'",
         meta_.name.c_str(), id);
      return false;
    }

    last_pos_ = pos_;
  }

  if (offs) {
    offs_ += offs->end;
  }

  return true;
}

// --SECTION--                                        fields_data implementation

fields_data::fields_data(
    const field_features_t& field_features,
    const feature_column_info_provider_t& feature_columns,
    const comparer* comparator /*= nullptr*/)
  : comparator_(comparator),
    field_features_(&field_features),
    feature_columns_(&feature_columns),
    byte_writer_(byte_pool_.begin()),
    int_writer_(int_pool_.begin()) {
}

field_data* fields_data::emplace(
    const hashed_string_ref& name,
    IndexFeatures index_features,
    const features_t& features,
    columnstore_writer& columns) {
  assert(fields_map_.size() == fields_.size());

  auto it = fields_map_.lazy_emplace(
    name,
    [&name](const fields_map::constructor& ctor) {
      ctor(name.hash(), nullptr);
  });

  if (!it->second) {

    const_cast<field_data*&>(it->second) = &fields_.back();
  }

  return it->second;
}

void fields_data::flush(field_writer& fw, flush_state& state) {
  REGISTER_TIMER_DETAILED();

  IndexFeatures index_features{IndexFeatures::NONE};
  feature_set_t features;

  // sort fields
  sorted_fields_.resize(fields_.size());
  auto begin = sorted_fields_.begin();
  for (auto& entry : fields_) {
    *begin = &entry;
    ++begin;

    const auto& meta = entry.meta();
    index_features |= static_cast<IndexFeatures>(meta.index_features);
    accumulate_features(features, meta.features);
  }

  state.index_features = static_cast<IndexFeatures>(index_features);
  state.features = &features;

  std::sort(
    sorted_fields_.begin(), sorted_fields_.end(),
    [](const field_data* lhs, const field_data* rhs) noexcept {
      return lhs->meta().name < rhs->meta().name;
  });

  detail::term_reader terms(sorted_postings_, state.docmap);

  fw.prepare(state);
  for (auto* field : sorted_fields_) {
    auto& meta = field->meta();

    // reset reader
    terms.reset(*field);

    // write inverted data
    auto it = terms.iterator();
    fw.write(meta.name, meta.index_features, meta.features, *it);
  }

  fw.end();
}

void fields_data::reset() noexcept {
  byte_writer_ = byte_pool_.begin(); // reset position pointer to start of pool
  fields_.clear();
  cached_features_.clear(); // FIXME(@gnusi): we loose all per-column buffers
  fields_map_.clear();
  int_writer_ = int_pool_.begin(); // reset position pointer to start of pool
}

void fields_data::flush_features(
    columnstore_writer& writer,
    const doc_map& docmap,
    sorted_column::flush_buffer_t& buffer) {
  for (auto& feature : cached_features_) {
    assert(feature.id);
    if (IRS_LIKELY(!field_limits::valid(*feature.id))) {
      *feature.id = feature.stream.flush(writer, docmap, buffer);
    }
  }
}

size_t fields_data::memory_active() const noexcept {
  return byte_writer_.pool_offset()
    + int_writer_.pool_offset() * sizeof(int_block_pool::value_type)
    + fields_map_.size() * sizeof(fields_map::value_type)
    + fields_.size() * sizeof(decltype(fields_)::value_type)
    + std::accumulate(
        std::begin(cached_features_),
        std::end(cached_features_),
        size_t{0},
        [](const auto lhs, const auto& rhs) {
          return lhs + rhs.stream.memory_active(); });
}

size_t fields_data::memory_reserved() const noexcept {
  // FIXME(@gnusi): revisit the implementation
  return byte_pool_.size() + int_pool_.size();
}

// use base irs::position type for ancestors
template<typename Reader>
struct type<::pos_iterator<Reader>> : type<irs::position> { };

} // ROOT


try {
      files = TSystem::readDirectory(parentDir, false, true, true);
    } catch (...) {
    }

#include "tsystem.h"

using namespace std;

#include <set>
#include "tfilepath_io.h"
#include "tconvert.h"

#ifndef TNZCORE_LIGHT

#include <QDateTime>
#include <QStringList>
#include <QProcess>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QSettings>
#include <QVariant>
#include <QThread>
#include <QUrl>
#include <QCoreApplication>
#include <QUuid>

#include <QDesktopServices>
#include <QHostInfo>

#ifdef _WIN32
#include <shlobj.h>
#include <shellapi.h>
#include <winnt.h>
#endif

namespace {

inline QString toQString(const TFilePath &path) {
  return QString::fromStdWString(path.getWideString());
}

int HasMainLoop = -1;

}  // namespace

TFileStatus::TFileStatus(const TFilePath &path) {
  m_fileInfo = QFileInfo(QString::fromStdWString(path.getWideString()));
  m_exist    = m_fileInfo.exists();
}

QString TFileStatus::getGroup() const {
  if (!m_exist) return QString();
  return m_fileInfo.group();
}

QString TFileStatus::getUser() const {
  if (!m_exist) return QString();
  return m_fileInfo.owner();
}

TINT64 TFileStatus::getSize() const {
  if (!m_exist) return 0;
  return m_fileInfo.size();
}

QDateTime TFileStatus::getLastAccessTime() const {
  if (!m_exist) return QDateTime();
  return m_fileInfo.lastRead();
}

QDateTime TFileStatus::getLastModificationTime() const {
  if (!m_exist) return QDateTime();
  return m_fileInfo.lastModified();
}

QDateTime TFileStatus::getCreationTime() const {
  if (!m_exist) return QDateTime();
  return m_fileInfo.created();
}

QFile::Permissions TFileStatus::getPermissions() const {
  if (!m_exist) return 0;
  return m_fileInfo.permissions();
}

bool TFileStatus::isDirectory() const {
  if (!m_exist) return 0;
  return m_fileInfo.isDir();
}

bool TFileStatus::isLink() const { return m_fileInfo.isSymLink(); }

bool TSystem::doHaveMainLoop() {
  if (HasMainLoop == -1)
    assert(!"you MUST call the TSystem::hasMainLoop function in the main of the program!");
  return HasMainLoop == 1;
}

void TSystem::hasMainLoop(bool state) {
  assert(HasMainLoop == -1);
  HasMainLoop = state ? 1 : 0;
}

QString TSystem::getHostName() { return QHostInfo::localHostName(); }

QString TSystem::getUserName() {
  QStringList list = QProcess::systemEnvironment();
  int j;
  for (j = 0; j < list.size(); j++) {
    QString value = list.at(j);
    QString user;
#ifdef _WIN32
    if (value.startsWith("USERNAME=")) user = value.right(value.size() - 9);
#else
    if (value.startsWith("USER=")) user = value.right(value.size() - 5);
#endif
    if (!user.isEmpty()) return user;
  }
  return QString("none");
}

TFilePath TSystem::getTempDir() {
  return TFilePath(QDir::tempPath().toStdString());
}

TFilePath TSystem::getTestDir(string name) {
  return TFilePath("C:") + TFilePath(name);
}

QString TSystem::getSystemValue(const TFilePath &name) {
  QStringList strlist = toQString(name).split("\\", QString::SkipEmptyParts);

  assert(strlist.size() > 3);
  assert(strlist.at(0) == "SOFTWARE");

  QSettings qs(QSettings::SystemScope, strlist.at(1), strlist.at(2));

  int i;
  QString varName;

  for (i = 3; i < strlist.size(); i++) {
    varName += strlist.at(i);
    if (i < strlist.size() - 1) varName += "//";
  }

  return qs.value(varName).toString();
}

TFilePath TSystem::getBinDir() {
  TFilePath fp =
      TFilePath(QCoreApplication::applicationFilePath().toStdString());
  return fp.getParentDir();
}

TFilePath TSystem::getDllDir() { return getBinDir(); }

TFilePath TSystem::getUniqueFile(QString field) {
  QString uuid = QUuid::createUuid()
                     .toString()
                     .replace("-", "")
                     .replace("{", "")
                     .replace("}", "")
                     .toLatin1()
                     .data();

  QString path = QDir::tempPath() + QString("\\") + field + uuid;

  return TFilePath(path.toStdString());
}

namespace {
TFilePathSet getPathsToCreate(const TFilePath &path) {
  TFilePathSet pathList;
  if (path.isEmpty()) return pathList;
  TFilePath parentDir = path;
  while (!TFileStatus(parentDir).doesExist()) {
    if (parentDir == parentDir.getParentDir()) return TFilePathSet();
    pathList.push_back(parentDir);
    parentDir = parentDir.getParentDir();
  }
  return pathList;
}

void setPathsPermissions(const TFilePathSet &pathSet,
                         QFile::Permissions permissions) {
  TFilePathSet::const_iterator it;
  for (it = pathSet.begin(); it != pathSet.end(); it++) {
    QFile f(toQString(*it));
    f.setPermissions(permissions);
  }
}
}

// gestire exception
void TSystem::mkDir(const TFilePath &path) {
  TFilePathSet pathSet = getPathsToCreate(path);
  QString qPath        = toQString(path);
  assert(!qPath.contains("+"));
  if (!QDir::current().mkpath(qPath))
    throw TSystemException(path, "can't create folder!");

  setPathsPermissions(
      pathSet, QFile::ReadUser | QFile::WriteUser | QFile::ExeUser |
                   QFile::ReadGroup | QFile::WriteGroup | QFile::ExeGroup |
                   QFile::ReadOther | QFile::WriteOther | QFile::ExeOther);
}

// gestire exception
void TSystem::rmDir(const TFilePath &path) {
  if (!QDir(toQString(path.getParentDir()))
           .rmdir(QString::fromStdString(path.getName())))
    throw TSystemException(path, "can't remove folder!");
}

// vinz

namespace {
void rmDirTree(const QString &path) {
  int i;
  QFileInfoList fil = QDir(path).entryInfoList();
  for (i = 0; i < fil.size(); i++) {
    QFileInfo fi = fil.at(i);
    if (fi.fileName() == QString(".") || fi.fileName() == QString(".."))
      continue;
    QString son = fi.absoluteFilePath();
    if (QFileInfo(son).isDir())
      rmDirTree(son);
    else if (QFileInfo(son).isFile())
      if (!QFile::remove(son))
        throw TSystemException("can't remove file" + son.toStdString());
  }
  if (!QDir::current().rmdir(path))
    throw TSystemException("can't remove path!");
}

}  // namespace

void TSystem::rmDirTree(const TFilePath &path) { ::rmDirTree(toQString(path)); }

void TSystem::copyDir(const TFilePath &dst, const TFilePath &src,
                      bool overwrite) {
  QFileInfoList fil = QDir(toQString(src)).entryInfoList();

  QDir::current().mkdir(toQString(dst));

  int i;
  for (i = 0; i < fil.size(); i++) {
    QFileInfo fi = fil.at(i);
    if (fi.fileName() == QString(".") || fi.fileName() == QString(".."))
      continue;
    if (fi.isDir()) {
      TFilePath srcDir = TFilePath(fi.filePath().toStdString());
      TFilePath dstDir = dst + srcDir.getName();
      copyDir(dstDir, srcDir, overwrite);
    } else {
      TFilePath srcFi = dst + TFilePath(fi.fileName());
      if (overwrite && QFile::exists(toQString(srcFi)))
        QFile::remove(toQString(srcFi));
      QFile::copy(fi.filePath(), toQString(srcFi));
    }
  }
}

/*
void TSystem::touchFile(const TFilePath &path)
{
QFile f(toQString(path));

if (!f.open(QIODevice::ReadWrite))
  throw TSystemException(path, "can't touch file!");
else
  f.close();
}
*/
/*
#ifdef _WIN32

wstring getFormattedMessage(DWORD lastError)
{
LPVOID lpMsgBuf;
FormatMessage(
    FORMAT_MESSAGE_ALLOCATE_BUFFER |
    FORMAT_MESSAGE_FROM_SYSTEM |
    FORMAT_MESSAGE_IGNORE_INSERTS,
    NULL,
    lastError,
    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
    (LPTSTR) &lpMsgBuf,
    0,
    NULL
);

int wSize = MultiByteToWideChar(0,0,(char*)lpMsgBuf,-1,0,0);
if(!wSize)
  return wstring();

wchar_t* wBuffer = new wchar_t [wSize+1];
MultiByteToWideChar(0,0,(char*)lpMsgBuf,-1,wBuffer,wSize);
wBuffer[wSize]='\0';
wstring wmsg(wBuffer);

delete []wBuffer;
LocalFree(lpMsgBuf);
return wmsg;
}

#endif
*/

void TSystem::copyFile(const TFilePath &dst, const TFilePath &src,
                       bool overwrite) {
  assert(dst != TFilePath());

  if (dst == src) return;

  // Create the containing folder before trying to copy or it will crash!
  touchParentDir(dst);

  const QString &qDst = toQString(dst);
  if (overwrite && QFile::exists(qDst)) QFile::remove(qDst);

  if (!QFile::copy(toQString(src), qDst))
    throw TSystemException(dst, "can't copy file!");
}

void TSystem::renameFile(const TFilePath &dst, const TFilePath &src,
                         bool overwrite) {
  assert(dst != TFilePath());

  if (dst == src) return;

  const QString &qDst = toQString(dst);
  if (overwrite && QFile::exists(qDst)) QFile::remove(qDst);

  if (!QFile::rename(toQString(src), qDst))
    throw TSystemException(dst, "can't rename file!");
}

// gestire gli errori con GetLastError?
void TSystem::deleteFile(const TFilePath &fp) {
  if (TSystem::doesExistFileOrLevel(fp)) {
    if (fp.getQString().contains("cache")) {
      QFile::remove(toQString(fp));
    } else if (!QFile::remove(toQString(fp)))
      throw TSystemException(fp, "can't delete file!");
  }
}

void TSystem::hideFile(const TFilePath &fp) {
#ifdef _WIN32
  if (!SetFileAttributesW(fp.getWideString().c_str(), FILE_ATTRIBUTE_HIDDEN))
    throw TSystemException(fp, "can't hide file!");
#else  // MACOSX, and others
  TSystem::renameFile(TFilePath(fp.getParentDir() + L"." + fp.getLevelNameW()),
                      fp);
#endif
}

class CaselessFilepathLess final
    : public std::binary_function<TFilePath, TFilePath, bool> {
public:
  bool operator()(const TFilePath &a, const TFilePath &b) const {
    // Perform case sensitive compare, fallback to case insensitive.
    const wstring a_str = a.getWideString();
    const wstring b_str = b.getWideString();

    unsigned int i   = 0;
    int case_compare = -1;
    while (a_str[i] || b_str[i]) {
      if (a_str[i] != b_str[i]) {
        const wchar_t a_wchar = towlower(a_str[i]);
        const wchar_t b_wchar = towlower(b_str[i]);
        if (a_wchar < b_wchar) {
          return true;
        } else if (a_wchar > b_wchar) {
          return false;
        } else if (case_compare == -1) {
          case_compare = a_str[i] < b_str[i];
        }
      }
      i++;
    }
    return (case_compare == 1);
  }
};

/*! return the folder path list which is readable and executable
*/
void TSystem::readDirectory_Dir_ReadExe(TFilePathSet &dst,
                                        const TFilePath &path) {
  QStringList dirItems;
  readDirectory_DirItems(dirItems, path);

  for (const QString &item : dirItems) {
    TFilePath son = path + TFilePath(item.toStdWString());
    dst.push_back(son);
  }
}

// return the folder item list which is readable and executable
// (returns only names, not full path)
void TSystem::readDirectory_DirItems(QStringList &dst, const TFilePath &path) {
  if (!TFileStatus(path).isDirectory())
    throw TSystemException(path, " is not a directory");

  QDir dir(toQString(path));

#ifdef _WIN32
  // equivalent to sorting with QDir::LocaleAware
  auto const strCompare = [](const QString &s1, const QString &s2) {
    return QString::localeAwareCompare(s1, s2) < 0;
  };

  std::set<QString, decltype(strCompare)> entries(strCompare);

  WIN32_FIND_DATA find_dir_data;
  QString dir_search_path = dir.absolutePath() + "\\*";
  auto addEntry           = [&]() {
    // QDir::NoDotAndDotDot condition
    if (wcscmp(find_dir_data.cFileName, L".") != 0 &&
        wcscmp(find_dir_data.cFileName, L"..") != 0) {
      // QDir::AllDirs condition
      if (find_dir_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY &&
          (find_dir_data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) == 0) {
        entries.insert(QString::fromWCharArray(find_dir_data.cFileName));
      }
    }
  };
  HANDLE hFind =
      FindFirstFile((const wchar_t *)dir_search_path.utf16(), &find_dir_data);
  if (hFind != INVALID_HANDLE_VALUE) {
    addEntry();
    while (FindNextFile(hFind, &find_dir_data)) addEntry();
  }
  for (const QString &name : entries) dst.push_back(QString(name));

#else
  dst = dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Readable,
                      QDir::Name | QDir::LocaleAware);
#endif
}

/*! to retrieve the both lists with groupFrames option = on and off.
*/
void TSystem::readDirectory(TFilePathSet &groupFpSet, TFilePathSet &allFpSet,
                            const TFilePath &path) {
  if (!TFileStatus(path).isDirectory())
    throw TSystemException(path, " is not a directory");

  std::set<TFilePath, CaselessFilepathLess> fileSet_group;
  std::set<TFilePath, CaselessFilepathLess> fileSet_all;

  QStringList fil;
#ifdef _WIN32
  WIN32_FIND_DATA find_dir_data;
  QString dir_search_path = QDir(toQString(path)).absolutePath() + "\\*";
  auto addEntry           = [&]() {
    // QDir::NoDotAndDotDot condition
    if (wcscmp(find_dir_data.cFileName, L".") != 0 &&
        wcscmp(find_dir_data.cFileName, L"..") != 0) {
      // QDir::Files condition
      if ((find_dir_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0 &&
          (find_dir_data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) == 0) {
        fil.append(QString::fromWCharArray(find_dir_data.cFileName));
      }
    }
  };
  HANDLE hFind =
      FindFirstFile((const wchar_t *)dir_search_path.utf16(), &find_dir_data);
  if (hFind != INVALID_HANDLE_VALUE) {
    addEntry();
    while (FindNextFile(hFind, &find_dir_data)) addEntry();
  }
#else
  fil = QDir(toQString(path))
            .entryList(QDir::Files | QDir::NoDotAndDotDot | QDir::Readable);
#endif
  if (fil.size() == 0) return;

  for (int i = 0; i < fil.size(); i++) {
    QString fi = fil.at(i);

    TFilePath son = path + TFilePath(fi.toStdWString());

    // store all file paths
    fileSet_all.insert(son);

    // in case of the sequential files
    if (son.getDots() == "..") son = son.withFrame();

    // store the group. insersion avoids duplication of the item
    fileSet_group.insert(son);
  }

  groupFpSet.insert(groupFpSet.end(), fileSet_group.begin(),
                    fileSet_group.end());
  allFpSet.insert(allFpSet.end(), fileSet_all.begin(), fileSet_all.end());
}

void TSystem::readDirectory(TFilePathSet &dst, const QDir &dir,
                            bool groupFrames) {
  if (!(dir.exists() && QFileInfo(dir.path()).isDir()))
    throw TSystemException(TFilePath(dir.path().toStdWString()),
                           " is not a directory");
  QStringList entries;
#ifdef _WIN32
  WIN32_FIND_DATA find_dir_data;
  QString dir_search_path = dir.absolutePath() + "\\*";
  QDir::Filters filter    = dir.filter();

  // store name filters
  bool hasNameFilter = false;
  QList<QRegExp> nameFilters;
  for (const QString &nameFilter : dir.nameFilters()) {
    if (nameFilter == "*") {
      hasNameFilter = false;
      break;
    }
    QRegExp regExp(nameFilter);
    regExp.setPatternSyntax(QRegExp::Wildcard);
    nameFilters.append(regExp);
    hasNameFilter = true;
  }

  auto addEntry = [&]() {
    // QDir::NoDotAndDotDot condition
    if (wcscmp(find_dir_data.cFileName, L".") != 0 &&
        wcscmp(find_dir_data.cFileName, L"..") != 0) {
      // QDir::Files condition
      if ((filter & QDir::Files) == 0 &&
          (find_dir_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
        return;
      // QDir::Dirs condition
      if ((filter & QDir::Dirs) == 0 &&
          (find_dir_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
        return;
      // QDir::Hidden condition
      if ((filter & QDir::Hidden) == 0 &&
          (find_dir_data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN))
        return;

      QString fileName = QString::fromWCharArray(find_dir_data.cFileName);

      // name filter
      if (hasNameFilter) {
        bool matched = false;
        for (const QRegExp &regExp : nameFilters) {
          if (regExp.exactMatch(fileName)) {
            matched = true;
            break;
          }
        }
        if (!matched) return;
      }

      entries.append(fileName);
    }
  };
  HANDLE hFind =
      FindFirstFile((const wchar_t *)dir_search_path.utf16(), &find_dir_data);
  if (hFind != INVALID_HANDLE_VALUE) {
    addEntry();
    while (FindNextFile(hFind, &find_dir_data)) addEntry();
  }
#else
  entries    = (dir.entryList(dir.filter() | QDir::NoDotAndDotDot));
#endif

  TFilePath dirPath(dir.path().toStdWString());

  std::set<TFilePath, CaselessFilepathLess> fpSet;

  int e, eCount = entries.size();
  for (e = 0; e != eCount; ++e) {
    TFilePath path(dirPath + TFilePath(entries.at(e).toStdWString()));

    if (groupFrames && path.getDots() == "..") path = path.withFrame();

    fpSet.insert(path);
  }

  dst.insert(dst.end(), fpSet.begin(), fpSet.end());
}

void TSystem::readDirectory(TFilePathSet &dst, const TFilePath &path,
                            bool groupFrames, bool onlyFiles,
                            bool getHiddenFiles) {
  QDir dir(toQString(path));

  QDir::Filters filters(QDir::Files);
  if (!onlyFiles) filters |= QDir::Dirs;
  if (getHiddenFiles) filters |= QDir::Hidden;
  dir.setFilter(filters);

  readDirectory(dst, dir, groupFrames);
}

void TSystem::readDirectory(TFilePathSet &dst, const TFilePathSet &pathSet,
                            bool groupFrames, bool onlyFiles,
                            bool getHiddenFiles) {
  for (TFilePathSet::const_iterator it = pathSet.begin(); it != pathSet.end();
       it++)
    readDirectory(dst, *it, groupFrames, onlyFiles);
}

TFilePathSet TSystem::readDirectory(const TFilePath &path, bool groupFrames,
                                    bool onlyFiles, bool getHiddenFiles) {
  TFilePathSet filePathSet;
  readDirectory(filePathSet, path, groupFrames, onlyFiles, getHiddenFiles);
  return filePathSet;
}

TFilePathSet TSystem::readDirectory(const TFilePathSet &pathSet,
                                    bool groupFrames, bool onlyFiles,
                                    bool getHiddenFiles) {
  TFilePathSet dst;
  readDirectory(dst, pathSet, groupFrames, onlyFiles, getHiddenFiles);
  return dst;
}

void TSystem::readDirectoryTree(TFilePathSet &dst, const TFilePath &path,
                                bool groupFrames, bool onlyFiles) {
  if (!TFileStatus(path).isDirectory())
    throw TSystemException(path, " is not a directory");

  QFileInfoList fil = QDir(toQString(path)).entryInfoList();
  int i;
  for (i = 0; i < fil.size(); i++) {
    QFileInfo fi = fil.at(i);
    if (fi.fileName() == QString(".") || fi.fileName() == QString(".."))
      continue;
    TFilePath son = TFilePath(fi.filePath().toStdWString());
    if (TFileStatus(son).isDirectory()) {
      if (!onlyFiles) dst.push_back(son);
      readDirectoryTree(dst, son, groupFrames, onlyFiles);
    } else
      dst.push_back(son);
  }
}

void TSystem::readDirectoryTree(TFilePathSet &dst, const TFilePathSet &pathSet,
                                bool groupFrames, bool onlyFiles) {
  for (TFilePathSet::const_iterator it = pathSet.begin(); it != pathSet.end();
       it++)
    readDirectoryTree(dst, *it, groupFrames, onlyFiles);
}

TFilePathSet TSystem::readDirectoryTree(const TFilePath &path, bool groupFrames,
                                        bool onlyFiles) {
  TFilePathSet dst;
  readDirectoryTree(dst, path, groupFrames, onlyFiles);
  return dst;
}

TFilePathSet TSystem::readDirectoryTree(const TFilePathSet &pathSet,
                                        bool groupFrames, bool onlyFiles) {
  TFilePathSet dst;
  readDirectoryTree(dst, pathSet, groupFrames, onlyFiles);
  return dst;
}

TFilePathSet TSystem::packLevelNames(const TFilePathSet &fps) {
  std::set<TFilePath> tmpSet;
  TFilePathSet::const_iterator cit;
  for (cit = fps.begin(); cit != fps.end(); ++cit)
    tmpSet.insert(cit->getParentDir() + cit->getLevelName());

  TFilePathSet fps2;
  for (std::set<TFilePath>::const_iterator c_sit = tmpSet.begin();
       c_sit != tmpSet.end(); ++c_sit) {
    fps2.push_back(*c_sit);
  }
  return fps2;
}

TFilePathSet TSystem::getDisks() {
  TFilePathSet filePathSet;
  QFileInfoList fil = QDir::drives();
  int i;
  for (i = 0; i < fil.size(); i++)
    filePathSet.push_back(TFilePath(fil.at(i).filePath().toStdWString()));

  return filePathSet;
}

class LocalThread final : public QThread {
public:
  static LocalThread *currentThread() {
    return (LocalThread *)QThread::currentThread();
  }
  void sleep(TINT64 delay) { msleep(delay); }
};

void TSystem::sleep(TINT64 delay) {
  LocalThread::currentThread()->sleep(delay);
}

int TSystem::getProcessorCount() { return QThread::idealThreadCount(); }

bool TSystem::doesExistFileOrLevel(const TFilePath &fp) {
  if (TFileStatus(fp).doesExist()) return true;

  if (fp.isLevelName()) {
    const TFilePath &parentDir = fp.getParentDir();
    if (!TFileStatus(parentDir).doesExist()) return false;

    TFilePathSet files;

    TFilePathSet::iterator it, end = files.end();
    for (it = files.begin(); it != end; ++it) {
      if (it->getLevelNameW() == fp.getLevelNameW()) return true;
// On case insensitive systems like Windows/OSX, we need to
// compare using all the same case to confirm it is unique
// We'll force this for Linux as well since the project might
// be shared on other platforms.
#ifdef _WIN32
      if (_wcsicmp(it->getLevelNameW().c_str(), fp.getLevelNameW().c_str()) ==
          0)
        return true;
#else
      if (toLower(it->getLevelNameW()) == toLower(fp.getLevelNameW()))
        return true;
#endif
    }
  } else if (fp.getType() == "psd") {
    QString name(QString::fromStdWString(fp.getWideName()));
    name.append(QString::fromStdString(fp.getDottedType()));

    int sepPos                               = name.indexOf("#");
    int dotPos                               = name.indexOf(".", sepPos);
    int removeChars                          = dotPos - sepPos;
    int doubleUnderscorePos                  = name.indexOf("__", sepPos);
    if (doubleUnderscorePos > 0) removeChars = doubleUnderscorePos - sepPos;

    name.remove(sepPos, removeChars);

    TFilePath psdpath(fp.getParentDir() + TFilePath(name.toStdWString()));
    if (TFileStatus(psdpath).doesExist()) return true;
  }

  return false;
}

void TSystem::copyFileOrLevel_throw(const TFilePath &dst,
                                    const TFilePath &src) {
  if (src.isLevelName()) {
    TFilePathSet files;
    files = TSystem::readDirectory(src.getParentDir(), false);

    TFilePathSet::iterator it, end = files.end();
    for (it = files.begin(); it != end; ++it) {
      if (it->getLevelNameW() == src.getLevelNameW()) {
        TFilePath src1 = *it;
        TFilePath dst1 = dst.withFrame(it->getFrame());

        TSystem::copyFile(dst1, src1);
      }
    }
  } else
    TSystem::copyFile(dst, src);
}

void TSystem::renameFileOrLevel_throw(const TFilePath &dst,
                                      const TFilePath &src,
                                      bool renamePalette) {
  if (renamePalette && ((src.getType() == "tlv") || (src.getType() == "tzp") ||
                        (src.getType() == "tzu"))) {
    // Special case: since renames cannot be 'grouped' in the UI, palettes are
    // automatically
    // renamed here if required
    const char *type = (src.getType() == "tlv") ? "tpl" : "plt";

    TFilePath srcpltname(src.withNoFrame().withType(type));
    TFilePath dstpltname(dst.withNoFrame().withType(type));

    if (TSystem::doesExistFileOrLevel(src) &&
        TSystem::doesExistFileOrLevel(srcpltname))
      TSystem::renameFile(dstpltname, srcpltname, false);
  }

  if (src.isLevelName()) {
    TFilePathSet files;
    files = TSystem::readDirectory(src.getParentDir(), false);

    for (TFilePathSet::iterator it = files.begin(); it != files.end(); it++) {
      if (it->getLevelName() == src.getLevelName()) {
        TFilePath src1 = *it;
        TFilePath dst1 = dst.withFrame(it->getFrame());

        TSystem::renameFile(dst1, src1);
      }
    }
  } else
    TSystem::renameFile(dst, src);
}

void TSystem::removeFileOrLevel_throw(const TFilePath &fp) {
  if (fp.isLevelName()) {
    TFilePathSet files;
    files = TSystem::readDirectory(fp.getParentDir(), false, true, true);

    TFilePathSet::iterator it, end = files.end();
    for (it = files.begin(); it != end; ++it) {
      if (it->getLevelName() == fp.getLevelName()) TSystem::deleteFile(*it);
    }
  } else
    TSystem::deleteFile(fp);
}

void TSystem::hideFileOrLevel_throw(const TFilePath &fp) {
  if (fp.isLevelName()) {
    TFilePathSet files;
    files = TSystem::readDirectory(fp.getParentDir(), false);

    TFilePathSet::iterator it, end = files.end();
    for (it = files.begin(); it != end; ++it) {
      if (it->getLevelNameW() == fp.getLevelNameW()) TSystem::hideFile(*it);
    }
  } else
    TSystem::hideFile(fp);
}

void TSystem::moveFileOrLevelToRecycleBin_throw(const TFilePath &fp) {
  if (fp.isLevelName()) {
    TFilePathSet files;
    files = TSystem::readDirectory(fp.getParentDir(), false, true, true);

    TFilePathSet::iterator it, end = files.end();
    for (it = files.begin(); it != end; ++it) {
      if (it->getLevelNameW() == fp.getLevelNameW())
        TSystem::moveFileToRecycleBin(*it);
    }
  } else
    TSystem::moveFileToRecycleBin(fp);
}

bool TSystem::copyFileOrLevel(const TFilePath &dst, const TFilePath &src) {
  try {
    copyFileOrLevel_throw(dst, src);
  } catch (...) {
    return false;
  }
  return true;
}

bool TSystem::renameFileOrLevel(const TFilePath &dst, const TFilePath &src,
                                bool renamePalette) {
  try {
    renameFileOrLevel_throw(dst, src, renamePalette);
  } catch (...) {
    return false;
  }
  return true;
}

bool TSystem::removeFileOrLevel(const TFilePath &fp) {
  try {
    removeFileOrLevel_throw(fp);
  } catch (...) {
    return false;
  }
  return true;
}

bool TSystem::hideFileOrLevel(const TFilePath &fp) {
  try {
    hideFileOrLevel_throw(fp);
  } catch (...) {
    return false;
  }
  return true;
}

bool TSystem::moveFileOrLevelToRecycleBin(const TFilePath &fp) {
  try {
    moveFileOrLevelToRecycleBin_throw(fp);
  } catch (...) {
    return false;
  }
  return true;
}

bool TSystem::touchParentDir(const TFilePath &fp) {
  TFilePath parentDir = fp.getParentDir();
  TFileStatus fs(parentDir);
  if (fs.isDirectory())
    return true;
  else if (fs.doesExist())
    return false;
  try {
    mkDir(parentDir);
  } catch (...) {
    return false;
  }
  return true;
}

bool TSystem::showDocument(const TFilePath &path) {
#ifdef _WIN32
  int ret = (int)ShellExecuteW(0, L"open", path.getWideString().c_str(), 0, 0,
                               SW_SHOWNORMAL);
  if (ret <= 32) {
    return false;
    throw TSystemException(path, "Can't open");
  }
  return true;
#else
  string cmd = "open ";
  string thePath(::to_string(path));
  UINT pos = 0, count = 0;
  char newPath[2048];

  while (pos < thePath.size()) {
    char c                         = thePath[pos];
    if (c == ' ') newPath[count++] = '\\';

    newPath[count++] = c;
    ++pos;
  }
  newPath[count] = 0;

  cmd = cmd + string(newPath);
  system(cmd.c_str());
  return true;
#endif
}

QString TSystem::findFileLocation(QStringList folderList, QString fileName) {
  if (folderList.isEmpty()) return "";

  QStringList::iterator it;
  for (it = folderList.begin(); it != folderList.end(); it++) {
    QString path = *it + "/" + fileName;
    TFilePath fp(path);
    if (TSystem::doesExistFileOrLevel(fp) && !TFileStatus(fp).isDirectory())
      return *it;
  }

  return "";
}

#else

#include <windows.h>

void TSystem::sleep(TINT64 delay) { Sleep((DWORD)delay); }

// gestire gli errori con GetLastError?
void TSystem::deleteFile(const TFilePath &fp) { assert(false); }

void TSystem::rmDirTree(const TFilePath &path) { assert(false); }

#endif  // TNZCORE_LIGHT

TSystemException::TSystemException(const TFilePath &fname, int err)
    : m_fname(fname)
    , m_err(err)
    , m_msg(L"")

{}

TSystemException::TSystemException(const TFilePath &fname,
                                   const std::string &msg)
    : m_fname(fname), m_err(-1), m_msg(::to_wstring(msg)) {}

TSystemException::TSystemException(const TFilePath &fname, const wstring &msg)
    : m_fname(fname), m_err(-1), m_msg(msg) {}

TSystemException::TSystemException(const std::string &msg)
    : m_fname(""), m_err(-1), m_msg(::to_wstring(msg)) {}

TSystemException::TSystemException(const wstring &msg)
    : m_fname(""), m_err(-1), m_msg(msg) {}


try
    {
        IceSSL::ConnectionInfoPtr info = ICE_DYNAMIC_CAST(IceSSL::ConnectionInfo, c.con->getInfo());
        test(info->certs.size() == 0);
    }
    catch(const Ice::LocalException& ex)
    {
        cerr << ex << endl;
        test(false);
    }

#include <Ice/Ice.h>
#include <IceUtil/Thread.h>
#include <TestI.h>
#include <TestHelper.h>
#include <IceSSL/Plugin.h>
#include <IceSSL/ConnectionInfo.h>

using namespace std;
using namespace Ice;

ServerI::ServerI(const CommunicatorPtr& communicator) :
    _communicator(communicator)
{
}

void
ServerI::noCert(const Ice::Current& c)
{

}

void
ServerI::checkCert(ICE_IN(string) subjectDN, ICE_IN(string) issuerDN, const Ice::Current& c)
{
    try
    {
        IceSSL::ConnectionInfoPtr info = ICE_DYNAMIC_CAST(IceSSL::ConnectionInfo, c.con->getInfo());
        test(info->verified);
        test(info->certs.size() == 2);
        if(c.ctx.find("uwp") != c.ctx.end())
        {
            // UWP client just provide the subject and issuer CN, and not the full Subject and Issuer DN
            string subject(info->certs[0]->getSubjectDN());
            test(subject.find(subjectDN) != string::npos);
            string issuer(info->certs[0]->getIssuerDN());
            test(issuer.find(issuerDN) != string::npos);
        }
        else
        {
            test(info->certs[0]->getSubjectDN() == IceSSL::DistinguishedName(subjectDN));
            test(info->certs[0]->getIssuerDN() == IceSSL::DistinguishedName(issuerDN));
        }
    }
    catch(const Ice::LocalException&)
    {
        test(false);
    }
}

void
ServerI::checkCipher(ICE_IN(string) cipher, const Ice::Current& c)
{
    try
    {
        IceSSL::ConnectionInfoPtr info = ICE_DYNAMIC_CAST(IceSSL::ConnectionInfo, c.con->getInfo());
        test(info->cipher.compare(0, cipher.size(), cipher) == 0);
    }
    catch(const Ice::LocalException&)
    {
        test(false);
    }
}

void
ServerI::destroy()
{
    _communicator->destroy();
}

ServerFactoryI::ServerFactoryI(const string& defaultDir) : _defaultDir(defaultDir)
{
}

Test::ServerPrxPtr
ServerFactoryI::createServer(ICE_IN(Test::Properties) props, const Current&)
{
    InitializationData initData;
    initData.properties = createProperties();
    for(Test::Properties::const_iterator p = props.begin(); p != props.end(); ++p)
    {
        initData.properties->setProperty(p->first, p->second);
    }
    initData.properties->setProperty("IceSSL.DefaultDir", _defaultDir);

    CommunicatorPtr communicator = initialize(initData);
    ObjectAdapterPtr adapter = communicator->createObjectAdapterWithEndpoints("ServerAdapter", "ssl");
    ServerIPtr server = ICE_MAKE_SHARED(ServerI, communicator);
    ObjectPrxPtr obj = adapter->addWithUUID(server);
    _servers[obj->ice_getIdentity()] = server;
    adapter->activate();

    return ICE_UNCHECKED_CAST(Test::ServerPrx, obj);
}

void
ServerFactoryI::destroyServer(ICE_IN(Test::ServerPrxPtr) srv, const Ice::Current&)
{
    map<Identity, ServerIPtr>::iterator p = _servers.find(srv->ice_getIdentity());
    if(p != _servers.end())
    {
        p->second->destroy();
        _servers.erase(p);
    }
}

void
ServerFactoryI::shutdown(const Ice::Current& current)
{
    test(_servers.empty());
    current.adapter->getCommunicator()->shutdown();
}