Texture TextureFactory::loadTexture(const std::string& fileName, ObjectsCache& cache,
	bool async, TextureLoaderDelegate* delegate)
{
	if (fileName.length() == 0)
		return Texture();
	
	CriticalSectionScope lock(_csTextureLoading);
	
	std::string file = application().environment().resolveScalableFileName(fileName,
		renderContext()->screenScaleFactor());
	
	if (!fileExists(file))
	{
		log::error("Unable to find texture file: %s", file.c_str());
		return Texture();
	}
	
	uint64_t cachedFileProperty = 0;
    Texture texture = cache.findAnyObject(file, &cachedFileProperty);
	if (texture.invalid())
	{
		TextureDescription::Pointer desc =
			async ? et::loadTextureDescription(file, false) : et::loadTexture(file);

		if (desc.valid())
		{
			bool calledFromAnotherThread = Threading::currentThread() != threading().renderingThread();
			
			texture = Texture(new TextureData(renderContext(), desc, desc->origin(), async || calledFromAnotherThread));
			cache.manage(texture, ObjectLoader::Pointer(this));
			
			if (async)
				_loadingThread->addRequest(desc->origin(), texture, delegate);
			else if (calledFromAnotherThread)
				assert(false && "ERROR: Unable to load texture synchronously from non-rendering thread.");
		}
		
	}
	else
	{
		auto newProperty = cache.getFileProperty(file);
		if (cachedFileProperty != newProperty)
			reloadObject(texture, cache);
	
		if (async && (delegate != nullptr))
		{
			Invocation1 i;
			i.setTarget(delegate, &TextureLoaderDelegate::textureDidStartLoading, texture);
			i.invokeInMainRunLoop();
			i.setTarget(delegate, &TextureLoaderDelegate::textureDidLoad, texture);
			i.invokeInMainRunLoop();
		}
	}
   
	return texture;
}
void TextureLoadingThread::addRequest(const std::string& fileName, Texture::Pointer texture,
	TextureLoaderDelegate* delegate)
{
	if (delegate)
	{
		Invocation1 i;
		i.setTarget(delegate, &TextureLoaderDelegate::textureDidStartLoading, texture);
		i.invokeInMainRunLoop();
	}
	
	CriticalSectionScope lock(_requestsCriticalSection);
	_requests.push(etCreateObject<TextureLoadingRequest>(fileName, texture, delegate));

	if (running())
		resume();
	else
		run();
}
uint64_t TextureLoadingThread::main()
{
	while (running())
	{
		TextureLoadingRequest* req = dequeRequest();

		if (req)
		{
			req->textureDescription = loadTexture(req->fileName);

			Invocation1 invocation;
			invocation.setTarget(_delegate, &TextureLoadingThreadDelegate::textureLoadingThreadDidLoadTextureData, req);
			invocation.invokeInMainRunLoop();
		}
		else
		{
			suspend();
		}
	}

	return 0;
}
Texture::Pointer TextureFactory::loadTexture(const std::string& fileName, ObjectsCache& cache,
	bool async, TextureLoaderDelegate* delegate)
{
	if (fileName.length() == 0)
		return Texture::Pointer();
	
	CriticalSectionScope lock(_csTextureLoading);
	
	auto file = resolveTextureName(fileName);
	if (!fileExists(file))
		return Texture::Pointer();
	
	uint64_t cachedFileProperty = 0;
    Texture::Pointer texture = cache.findAnyObject(file, &cachedFileProperty);
	if (texture.invalid())
	{
		TextureDescription::Pointer desc =
			async ? et::loadTextureDescription(file, false) : et::loadTexture(file);
		
		int maxTextureSize = static_cast<int>(RenderingCapabilities::instance().maxTextureSize());
		if ((desc->size.x > maxTextureSize) || (desc->size.y > maxTextureSize))
		{
			log::warning("Attempt to load texture with dimensions (%d x %d) larger than max allowed (%d)",
				desc->size.x, desc->size.y, maxTextureSize);
		}
		
		if (desc.valid())
		{
			bool calledFromAnotherThread = !threading::inMainThread();
			
			texture = Texture::Pointer::create(renderContext(), desc, desc->origin(), async || calledFromAnotherThread);
			cache.manage(texture, _private->loader);
			
			if (async)
			{
				_loadingThread->addRequest(desc->origin(), texture, delegate);
			}
			else if (calledFromAnotherThread)
			{
				ET_FAIL("ERROR: Unable to load texture synchronously from non-rendering thread.");
			}
		}
		
	}
	else
	{
		auto newProperty = cache.getFileProperty(file);
		if (cachedFileProperty != newProperty)
			reloadObject(texture, cache);
	
		if (async)
		{
			textureDidStartLoading.invokeInMainRunLoop(texture);
			if (delegate != nullptr)
			{
				Invocation1 i;
				i.setTarget(delegate, &TextureLoaderDelegate::textureDidStartLoading, texture);
				i.invokeInMainRunLoop();
			}
			
			textureDidLoad.invokeInMainRunLoop(texture);
			if (delegate != nullptr)
			{
				Invocation1 i;
				i.setTarget(delegate, &TextureLoaderDelegate::textureDidLoad, texture);
				i.invokeInMainRunLoop();
			}
		}

	}
   
	return texture;
}