JNIEXPORT jint JNICALL Java_org_geometerplus_fbreader_formats_NativeFormatPlugin_readModelNative(JNIEnv* env, jobject thiz, jobject javaModel) {
	shared_ptr<FormatPlugin> plugin = findCppPlugin(thiz);
	if (plugin.isNull()) {
		return 1;
	}

	jobject javaBook = AndroidUtil::Field_NativeBookModel_Book->value(javaModel);

	shared_ptr<Book> book = Book::loadFromJavaBook(env, javaBook);
	shared_ptr<BookModel> model = new BookModel(book, javaModel);
	if (!plugin->readModel(*model)) {
		return 2;
	}
	if (!model->flush()) {
		AndroidUtil::throwCachedCharStorageException("Cannot write file from native code");
		return 3;
	}

	if (!initInternalHyperlinks(env, javaModel, *model)) {
		return 4;
	}

	initTOC(env, javaModel, *model->contentsTree());

	shared_ptr<ZLTextModel> textModel = model->bookTextModel();
	jobject javaTextModel = createTextModel(env, javaModel, *textModel);
	if (javaTextModel == 0) {
		return 5;
	}
	AndroidUtil::Method_NativeBookModel_setBookTextModel->call(javaModel, javaTextModel);
	if (env->ExceptionCheck()) {
		return 6;
	}
	env->DeleteLocalRef(javaTextModel);

	const std::map<std::string,shared_ptr<ZLTextModel> > &footnotes = model->footnotes();
	std::map<std::string,shared_ptr<ZLTextModel> >::const_iterator it = footnotes.begin();
	for (; it != footnotes.end(); ++it) {
		jobject javaFootnoteModel = createTextModel(env, javaModel, *it->second);
		if (javaFootnoteModel == 0) {
			return 7;
		}
		AndroidUtil::Method_NativeBookModel_setFootnoteModel->call(javaModel, javaFootnoteModel);
		if (env->ExceptionCheck()) {
			return 8;
		}
		env->DeleteLocalRef(javaFootnoteModel);
	}
	return 0;
}
static bool initTOC(JNIEnv *env, jobject javaModel, BookModel &model) {
	ContentsModel &contentsModel = (ContentsModel&)*model.contentsModel();

	jobject javaTextModel = createTextModel(env, javaModel, contentsModel);
	if (javaTextModel == 0) {
		return false;
	}

	std::vector<jint> childrenNumbers;
	std::vector<jint> referenceNumbers;
	const size_t size = contentsModel.paragraphsNumber();
	childrenNumbers.reserve(size);
	referenceNumbers.reserve(size);
	for (size_t pos = 0; pos < size; ++pos) {
		ZLTextTreeParagraph *par = (ZLTextTreeParagraph*)contentsModel[pos];
		childrenNumbers.push_back(par->children().size());
		referenceNumbers.push_back(contentsModel.reference(par));
	}
	jintArray javaChildrenNumbers = AndroidUtil::createJavaIntArray(env, childrenNumbers);
	jintArray javaReferenceNumbers = AndroidUtil::createJavaIntArray(env, referenceNumbers);

	AndroidUtil::Method_NativeBookModel_initTOC->call(javaModel, javaTextModel, javaChildrenNumbers, javaReferenceNumbers);

	env->DeleteLocalRef(javaTextModel);
	env->DeleteLocalRef(javaChildrenNumbers);
	env->DeleteLocalRef(javaReferenceNumbers);
	return !env->ExceptionCheck();
}
JNIEXPORT jint JNICALL Java_org_geometerplus_fbreader_formats_NativeFormatPlugin_readModelNative(JNIEnv* env, jobject thiz, jobject javaModel, jstring javaCacheDir) {
	shared_ptr<FormatPlugin> plugin = findCppPlugin(thiz);
	if (plugin.isNull()) {
		return 1;
	}

	const std::string cacheDir = AndroidUtil::fromJavaString(env, javaCacheDir);

	jobject javaBook = AndroidUtil::Field_BookModel_Book->value(javaModel);

	shared_ptr<Book> book = Book::loadFromJavaBook(env, javaBook);
	shared_ptr<BookModel> model = new BookModel(book, javaModel, cacheDir);
	if (!plugin->readModel(*model)) {
		return 2;
	}
	if (!model->flush()) {
		return 3;
	}

	if (!initInternalHyperlinks(env, javaModel, *model, cacheDir)) {
		return 4;
	}

	initTOC(env, javaModel, *model->contentsTree());

	shared_ptr<ZLTextModel> textModel = model->bookTextModel();
	jobject javaTextModel = createTextModel(env, javaModel, *textModel);
	if (javaTextModel == 0) {
		return 5;
	}
	AndroidUtil::Method_BookModel_setBookTextModel->call(javaModel, javaTextModel);
	if (env->ExceptionCheck()) {
		return 6;
	}
	env->DeleteLocalRef(javaTextModel);

	const std::map<std::string,shared_ptr<ZLTextModel> > &footnotes = model->footnotes();
	std::map<std::string,shared_ptr<ZLTextModel> >::const_iterator it = footnotes.begin();
	for (; it != footnotes.end(); ++it) {
		jobject javaFootnoteModel = createTextModel(env, javaModel, *it->second);
		if (javaFootnoteModel == 0) {
			return 7;
		}
		AndroidUtil::Method_BookModel_setFootnoteModel->call(javaModel, javaFootnoteModel);
		if (env->ExceptionCheck()) {
			return 8;
		}
		env->DeleteLocalRef(javaFootnoteModel);
	}

	const std::vector<std::vector<std::string> > familyLists = model->fontManager().familyLists();
	for (std::vector<std::vector<std::string> >::const_iterator it = familyLists.begin(); it != familyLists.end(); ++it) {
		const std::vector<std::string> &lst = *it;
		jobjectArray jList = env->NewObjectArray(lst.size(), AndroidUtil::Class_java_lang_String.j(), 0);
		for (std::size_t i = 0; i < lst.size(); ++i) {
			JString jString(env, lst[i]);
			env->SetObjectArrayElement(jList, i, jString.j());
		}
		AndroidUtil::Method_BookModel_registerFontFamilyList->call(javaModel, jList);
		env->DeleteLocalRef(jList);
	}

	const std::map<std::string,shared_ptr<FontEntry> > entries = model->fontManager().entries();
	for (std::map<std::string,shared_ptr<FontEntry> >::const_iterator it = entries.begin(); it != entries.end(); ++it) {
		if (it->second.isNull()) {
			continue;
		}
		JString family(env, it->first);
		jobject normal = createJavaFileInfo(env, it->second->Normal);
		jobject bold = createJavaFileInfo(env, it->second->Bold);
		jobject italic = createJavaFileInfo(env, it->second->Italic);
		jobject boldItalic = createJavaFileInfo(env, it->second->BoldItalic);

		AndroidUtil::Method_BookModel_registerFontEntry->call(
			javaModel, family.j(), normal, bold, italic, boldItalic
		);

		if (boldItalic != 0) env->DeleteLocalRef(boldItalic);
		if (italic != 0) env->DeleteLocalRef(italic);
		if (bold != 0) env->DeleteLocalRef(bold);
		if (normal != 0) env->DeleteLocalRef(normal);
	}

	return 0;
}