/**
 * \file FormRef.C
 * This file is part of LyX, the document processor.
 * Licence details can be found in the file COPYING.
 *
 * \author Angus Leeming
 *
 * Full author contact details are available in file CREDITS.
 */

#include <config.h>

#include "FormRef.h"
#include "ControlRef.h"
#include "forms/form_ref.h"

#include "Tooltips.h"
#include "xforms_helpers.h"
#include "xformsBC.h"

#include "insets/insetref.h"

#include "support/lstrings.h" // trim

#include "lyx_forms.h"

using std::find;
using std::max;
using std::sort;
using std::string;
using std::vector;

namespace lyx {

using support::getStringFromVector;

namespace frontend {

typedef FormController<ControlRef, FormView<FD_ref> > base_class;

FormRef::FormRef(Dialog & parent)
	: base_class(parent, _("Cross-reference")),
	  at_ref_(false)
{}


void FormRef::build()
{
	dialog_.reset(build_ref(this));

	for (int i = 0; !InsetRef::types[i].latex_name.empty(); ++i)
		fl_addto_choice(dialog_->choice_format,
				_(InsetRef::types[i].gui_name).c_str());

	// Force the user to use the browser to change refs.
	fl_deactivate_object(dialog_->input_ref);

	fl_set_input_return(dialog_->input_name, FL_RETURN_CHANGED);
	fl_set_input_return(dialog_->input_ref,  FL_RETURN_CHANGED);

	setPrehandler(dialog_->input_name);
	setPrehandler(dialog_->input_ref);

	// Manage the ok and cancel/close buttons
	bcview().setOK(dialog_->button_ok);
	bcview().setApply(dialog_->button_apply);
	bcview().setCancel(dialog_->button_close);
	bcview().setRestore(dialog_->button_restore);

	bcview().addReadOnly(dialog_->button_update);
	bcview().addReadOnly(dialog_->input_name);
	bcview().addReadOnly(dialog_->input_ref);

	// set up the tooltips
	string str = _("Select a document for labels.");
	tooltips().init(dialog_->choice_document, str);
	str = _("Sort the labels alphabetically.");
	tooltips().init(dialog_->check_sort, str);
	str = _("Go to selected label.");
	tooltips().init(dialog_->button_go, str);
	str = _("Update the list of labels.");
	tooltips().init(dialog_->button_update, str);
	str = _("Select format style of the cross-reference.");
	tooltips().init(dialog_->choice_format, str);
}


void FormRef::update()
{
	fl_set_input(dialog_->input_ref,
		     controller().params().getContents().c_str());
	fl_set_input(dialog_->input_name,
		     controller().params().getOptions().c_str());
	fl_set_choice(dialog_->choice_format,
		      InsetRef::getType(controller().params().getCmdName()) + 1);

	at_ref_ = false;
	switch_go_button();

	// Name is irrelevant to LaTeX/Literate documents
	Kernel::DocType const doctype = kernel().docType();
	if (doctype == Kernel::LATEX || doctype == Kernel::LITERATE) {
		setEnabled(dialog_->input_name, false);
	} else {
		setEnabled(dialog_->input_name, true);
	}

	// type is irrelevant to LinuxDoc/DocBook.
	if (doctype == Kernel::LINUXDOC || doctype == Kernel::DOCBOOK) {
		fl_set_choice(dialog_->choice_format, 1);
		setEnabled(dialog_->choice_format, false);
	} else {
		setEnabled(dialog_->choice_format, true);
	}

	// Get the available buffers
	vector<string> const buffers = controller().getBufferList();
	vector<string> const choice_documents =
		getVector(dialog_->choice_document);

	// If different from the current contents of the choice, then update it
	if (buffers != choice_documents) {
		// create a string of entries " entry1 | entry2 | entry3 "
		// with which to initialise the xforms choice object.
		string const choice =
			' ' + getStringFromVector(buffers, " | ") + ' ';

		fl_clear_choice(dialog_->choice_document);
		fl_addto_choice(dialog_->choice_document, choice.c_str());
	}

	fl_set_choice(dialog_->choice_document,
		      controller().getBufferNum() + 1);

	string const name = controller().
		getBufferName(fl_get_choice(dialog_->choice_document) - 1);
	refs_ = controller().getLabelList(name);

	updateBrowser(refs_);
}


namespace {

void updateHighlight(FL_OBJECT * browser,
		     vector<string> const & keys,
		     string const & ref)
{
	vector<string>::const_iterator cit = (ref.empty())
		? keys.end()
		: find(keys.begin(), keys.end(), ref);

	if (cit == keys.end()) {
		fl_deselect_browser(browser);
	} else {
		int const i = static_cast<int>(cit - keys.begin());
		fl_set_browser_topline(browser, max(i-5, 1));
		fl_select_browser_line(browser, i+1);
	}
}

} // namespace anon


void FormRef::updateBrowser(vector<string> const & akeys) const
{
	vector<string> keys(akeys);
	if (fl_get_button(dialog_->check_sort))
		sort(keys.begin(), keys.end());

	vector<string> browser_keys = getVector(dialog_->browser_refs);

	if (browser_keys == keys) {
		updateHighlight(dialog_->browser_refs, keys,
				getString(dialog_->input_ref));
		return;
	}

	fl_clear_browser(dialog_->browser_refs);
	for (vector<string>::const_iterator it = keys.begin();
	     it != keys.end(); ++it)
		fl_add_browser_line(dialog_->browser_refs, it->c_str());

	if (keys.empty()) {
		fl_add_browser_line(dialog_->browser_refs,
				    _("*** No labels found in document ***").c_str());

		setEnabled(dialog_->browser_refs, false);
		setEnabled(dialog_->check_sort,   false);

		fl_set_input(dialog_->input_ref, "");
	} else {
		setEnabled(dialog_->browser_refs, true);
		setEnabled(dialog_->check_sort,   true);

		updateHighlight(dialog_->browser_refs, keys,
				getString(dialog_->input_ref));
	}
}


void FormRef::apply()
{
	int const type = fl_get_choice(dialog_->choice_format) - 1;
	controller().params().setCmdName(InsetRef::getName(type));

	controller().params().setOptions(getString(dialog_->input_name));
	controller().params().setContents(getString(dialog_->input_ref));
}


ButtonPolicy::SMInput FormRef::input(FL_OBJECT * ob, long)
{
	ButtonPolicy::SMInput activate(ButtonPolicy::SMI_VALID);

	if (ob == dialog_->button_go) {
		// goto label / go back

		// No change to data
		activate = ButtonPolicy::SMI_NOOP;

		at_ref_ = !at_ref_;
		if (at_ref_) {
			controller().gotoRef(getString(dialog_->input_ref));
		} else {
			controller().gotoBookmark();
		}
		switch_go_button();

	} else if (ob == dialog_->browser_refs) {

		unsigned int sel = fl_get_browser(dialog_->browser_refs);
		if (sel < 1 || sel > refs_.size())
			return ButtonPolicy::SMI_NOOP;

		if (!kernel().isBufferReadonly()) {
			string s = fl_get_browser_line(dialog_->browser_refs, sel);
			fl_set_input(dialog_->input_ref, s.c_str());
		}

		if (at_ref_)
			controller().gotoBookmark();
		at_ref_ = false;
		switch_go_button();

		setEnabled(dialog_->choice_format, true);
		setEnabled(dialog_->button_go, true);
		fl_set_object_lcol(dialog_->input_ref, FL_BLACK);

	} else if (ob == dialog_->button_update ||
		   ob == dialog_->check_sort ||
		   ob == dialog_->choice_document) {

		// No change to data
		activate = ButtonPolicy::SMI_NOOP;

		if (ob == dialog_->button_update ||
		    ob == dialog_->choice_document) {
			string const name =
				controller().getBufferName(fl_get_choice(dialog_->choice_document) - 1);
			refs_ = controller().getLabelList(name);
		}

		fl_freeze_form(form());
		updateBrowser(refs_);
		fl_unfreeze_form(form());

		activate = ButtonPolicy::SMI_NOOP;

	} else if (ob == dialog_->choice_format) {

		int const type = fl_get_choice(dialog_->choice_format) - 1;
		if (controller().params().getCmdName() ==
		    InsetRef::getName(type)) {
			activate = ButtonPolicy::SMI_NOOP;
		}
	}

	return activate;
}


void FormRef::switch_go_button()
{
	if (at_ref_) {
		fl_set_object_label(dialog_->button_go, _("Go back").c_str());
		tooltips().init(dialog_->button_go, _("Go back to original place.").c_str());
	} else {
		fl_set_object_label(dialog_->button_go, _("Go to").c_str());
		tooltips().init(dialog_->button_go, _("Go to selected label.").c_str());
	}
	fl_set_button_shortcut(dialog_->button_go, "#G", 1);
	fl_show_object(dialog_->button_go);
}

} // namespace frontend
} // namespace lyx
