/**
 * \file ButtonPolicy.cpp
 * This file is part of LyX, the document processor.
 * Licence details can be found in the file COPYING.
 *
 * \author Allan Rae
 *
 * Full author contact details are available in file CREDITS.
 */

#include <config.h>

#include "ButtonPolicy.h"

#include "support/debug.h"

#include <iostream>
#include <vector>

using namespace std;

namespace lyx {
namespace frontend {

static char const * printState(ButtonPolicy::State const & state)
{
	switch (state) {
		case ButtonPolicy::INITIAL:
			return "INITIAL";
		case ButtonPolicy::VALID:
			return "VALID";
		case ButtonPolicy::INVALID:
			return "INVALID";
		case ButtonPolicy::APPLIED:
			return "APPLIED";
		case ButtonPolicy::AUTOAPPLY_INITIAL:
			return "AUTOAPPLY_INITIAL";
		case ButtonPolicy::AUTOAPPLY_CHANGED:
			return "AUTOAPPLY_CHANGED";
		case ButtonPolicy::RO_INITIAL:
			return "RO_INITIAL";
		case ButtonPolicy::RO_VALID:
			return "RO_VALID";
		case ButtonPolicy::RO_INVALID:
			return "RO_INVALID";
		case ButtonPolicy::RO_APPLIED:
			return "RO_APPLIED";
		case ButtonPolicy::RO_AUTOAPPLY:
			return "RO_AUTOAPPLY";
		case ButtonPolicy::BOGUS:
			return "BOGUS";
		default:
			return "";
	}
}


static char const * printInput(ButtonPolicy::SMInput const & input)
{
	switch (input) {
		case ButtonPolicy::SMI_VALID:
			return "SMI_VALID";
		case ButtonPolicy::SMI_INVALID:
			return "SMI_INVALID";
		case ButtonPolicy::SMI_OKAY:
			return "SMI_OKAY";
		case ButtonPolicy::SMI_APPLY:
			return "SMI_APPLY";
		case ButtonPolicy::SMI_CANCEL:
			return "SMI_CANCEL";
		case ButtonPolicy::SMI_RESTORE:
			return "SMI_RESTORE";
		case ButtonPolicy::SMI_AUTOAPPLY:
			return "SMI_AUTOAPPLY";
		case ButtonPolicy::SMI_HIDE:
			return "SMI_HIDE";
		case ButtonPolicy::SMI_READ_ONLY:
			return "SMI_READ_ONLY";
		case ButtonPolicy::SMI_READ_WRITE:
			return "SMI_READ_WRITE";
		case ButtonPolicy::SMI_NOOP:
			return "SMI_NOOP";
		case ButtonPolicy::SMI_TOTAL:
			return "SMI_TOTAL";
		default:
			return "";
	}
}


char const * functionName(ButtonPolicy::Policy policy)
{
	switch (policy) {
		case ButtonPolicy::PreferencesPolicy:
			return "PreferencesPolicy";
		case ButtonPolicy::OkCancelPolicy:
			return "OkCancelPolicy";
		case ButtonPolicy::OkCancelReadOnlyPolicy:
			return "OkCancelReadOnlyPolicy";
		case ButtonPolicy::OkApplyCancelPolicy:
			return "OkApplyCancelPolicy";
		case ButtonPolicy::OkApplyCancelReadOnlyPolicy:
			return "OkApplyCancelReadOnlyPolicy";
		case ButtonPolicy::OkApplyCancelAutoReadOnlyPolicy:
			return "OkApplyCancelAutoReadOnlyPolicy";
		case ButtonPolicy::NoRepeatedApplyPolicy:
			return "NoRepeatedApplyPolicy";
		case ButtonPolicy::NoRepeatedApplyReadOnlyPolicy:
			return "NoRepeatedApplyReadOnlyPolicy";
		case ButtonPolicy::IgnorantPolicy:
			return "IgnorantPolicy";
		default:
			return "Unknown policy";
	}
}


ostream & operator<<(ostream & os, ButtonPolicy::State st)
{
	return os << int(st);
}


ostream & operator<<(ostream & os, ButtonPolicy::SMInput smi)
{
	return os << int(smi);
}


/////////////////////////////////////////////////////////////////////////
//
// ButtonPolicy::Private
//
/////////////////////////////////////////////////////////////////////////

class ButtonPolicy::Private
{
public:
	typedef ButtonPolicy::SMInput SMInput;
	typedef ButtonPolicy::Policy Policy;
	typedef ButtonPolicy::State State;

	Private(Policy policy);

	void nextState(SMInput input);

	void initOkCancel();
	void initOkCancelReadOnly();
	void initNoRepeatedApplyReadOnly();
	void initOkApplyCancelReadOnly();
	void initOkApplyCancelAutoReadOnly();
	void initOkApplyCancel();
	void initNoRepeatedApply();
	void initPreferences();

public:
	///
	Policy policy_;

	/// Transition map of the state machine.
	typedef std::vector<State> StateArray;
	///
	typedef std::vector<StateArray> StateMachine;
	/// The state outputs are the status of the buttons.
	typedef std::vector<int> StateOutputs;

	/// Current state.
	State state_;
	/// Which buttons are active for a given state.
	StateOutputs outputs_;
	///
	StateMachine state_machine_;
};


ButtonPolicy::Private::Private(Policy policy)
{
	policy_ = policy;
	state_ = INITIAL;

	switch (policy_) {
		case OkCancelPolicy:
			initOkCancel();
			break;
		case OkCancelReadOnlyPolicy:
			initOkCancelReadOnly();
			break;
		case OkApplyCancelPolicy:
			initOkApplyCancel();
			break;
		case OkApplyCancelReadOnlyPolicy:
			initOkApplyCancelReadOnly();
			break;
		case OkApplyCancelAutoReadOnlyPolicy:
			initOkApplyCancelAutoReadOnly();
			break;
		case NoRepeatedApplyPolicy:
			initNoRepeatedApply();
			break;
		case NoRepeatedApplyReadOnlyPolicy:
			initNoRepeatedApplyReadOnly();
			break;
		case PreferencesPolicy:
			initPreferences();
			break;
		case IgnorantPolicy:
			break;
	}
}


void ButtonPolicy::Private::nextState(SMInput input)
{
	if (SMI_NOOP == input)
		return;

	State tmp = state_machine_[state_][input];

	LYXERR(Debug::GUI, "Transition from state "
			   << printState(state_) << " to state "
			   << printState(tmp) << " after input "
			   << printInput(input));

	if (tmp != BOGUS) {
		state_ = tmp;
	} else {
		LYXERR0(functionName(policy_) << ": No transition for input "
	    << printInput(input) << " from state " << printState(state_));
	}
}


void ButtonPolicy::Private::initPreferences()
{
	outputs_ = StateOutputs(APPLIED + 1, ButtonPolicy::ALL_BUTTONS);
	state_machine_ = StateMachine(APPLIED + 1,
			 StateArray(int(SMI_TOTAL), ButtonPolicy::BOGUS));

	// Build the state output map
	outputs_[INITIAL] = CLOSE;
	outputs_[VALID] = RESTORE | OKAY | APPLY | CANCEL;
	outputs_[INVALID] = RESTORE | CANCEL;
	outputs_[APPLIED] = OKAY | CLOSE;

	// Build the state machine one state at a time
	// NOTE:  Since CANCEL and HIDE always go to INITIAL they are
	//        left out of the state machine and handled explicitly
	//        in input().  This won't necessarily be true for all
	//        policies though so I'll leave those two as distinct
	//        inputs rather than merge them.  For example, a dialog
	//        that doesn't update it's input fields when reshown
	//        after being hidden needs a policy where CANCEL and
	//        HIDE are treated differently.
	//
	// State::INITIAL
	state_machine_[INITIAL][SMI_READ_ONLY] = INITIAL;
	state_machine_[INITIAL][SMI_READ_WRITE] = INITIAL;
	state_machine_[INITIAL][SMI_VALID] = VALID;
	state_machine_[INITIAL][SMI_INVALID] = INVALID;
	// State::VALID
	state_machine_[VALID][SMI_VALID] = VALID;
	state_machine_[VALID][SMI_READ_ONLY] = VALID;
	state_machine_[VALID][SMI_READ_WRITE] = VALID;
	state_machine_[VALID][SMI_INVALID] = INVALID;
	state_machine_[VALID][SMI_APPLY] = APPLIED;
	state_machine_[VALID][SMI_OKAY] = INITIAL;
	state_machine_[VALID][SMI_RESTORE] = INITIAL;
	// State::INVALID
	state_machine_[INVALID][SMI_VALID] = VALID;
	state_machine_[INVALID][SMI_INVALID] = INVALID;
	state_machine_[INVALID][SMI_READ_ONLY] = INVALID;
	state_machine_[INVALID][SMI_READ_WRITE] = INVALID;
	state_machine_[INVALID][SMI_RESTORE] = INITIAL;
	// State::APPLIED
	state_machine_[APPLIED][SMI_VALID] = VALID;
	state_machine_[APPLIED][SMI_INVALID] = INVALID;
	state_machine_[APPLIED][SMI_OKAY] = INITIAL;
	state_machine_[APPLIED][SMI_READ_ONLY] = APPLIED;
	state_machine_[APPLIED][SMI_READ_WRITE] = APPLIED;
}


void ButtonPolicy::Private::initOkCancel()
{
	outputs_ = StateOutputs(INVALID + 1, ButtonPolicy::ALL_BUTTONS);
	state_machine_ = StateMachine(INVALID + 1,
			 StateArray(int(SMI_TOTAL), ButtonPolicy::BOGUS));

	// Build the state output map
	outputs_[INITIAL] = CLOSE;
	outputs_[VALID] = RESTORE | OKAY | CANCEL;
	outputs_[INVALID] = RESTORE | CANCEL;

	// Build the state machine one state at a time
	// NOTE:  Since CANCEL and HIDE always go to INITIAL they are
	//        left out of the state machine and handled explicitly
	//        in input()
	//
	// State::INITIAL
	state_machine_[INITIAL][SMI_READ_ONLY] = INITIAL;
	state_machine_[INITIAL][SMI_READ_WRITE] = INITIAL;
	state_machine_[INITIAL][SMI_VALID] = VALID;
	state_machine_[INITIAL][SMI_INVALID] = INVALID;
	// State::VALID
	state_machine_[VALID][SMI_VALID] = VALID;
	state_machine_[VALID][SMI_READ_ONLY] = VALID;
	state_machine_[VALID][SMI_READ_WRITE] = VALID;
	state_machine_[VALID][SMI_INVALID] = INVALID;
	state_machine_[VALID][SMI_OKAY] = INITIAL;
	state_machine_[VALID][SMI_RESTORE] = INITIAL;
	// State::INVALID
	state_machine_[INVALID][SMI_VALID] = VALID;
	state_machine_[INVALID][SMI_INVALID] = INVALID;
	state_machine_[INVALID][SMI_READ_ONLY] = INVALID;
	state_machine_[INVALID][SMI_READ_WRITE] = INVALID;
	state_machine_[INVALID][SMI_RESTORE] = INITIAL;
}


void ButtonPolicy::Private::initOkCancelReadOnly()
{
	outputs_ = StateOutputs(RO_INVALID + 1, ButtonPolicy::ALL_BUTTONS);
	state_machine_ = StateMachine(RO_INVALID + 1,
			 StateArray(int(SMI_TOTAL), ButtonPolicy::BOGUS));

	// Build the state output map
	outputs_[INITIAL] = CLOSE;
	outputs_[VALID] = RESTORE | OKAY | CANCEL;
	outputs_[INVALID] = RESTORE | CANCEL;
	outputs_[RO_INITIAL] = CLOSE;
	outputs_[RO_VALID] = RESTORE | CANCEL;
	outputs_[RO_INVALID] = RESTORE | CANCEL;

	// Build the state machine one state at a time
	// NOTE:  Since CANCEL and HIDE always go to INITIAL they are
	//        left out of the state machine and handled explicitly
	//        in input()
	//
	// State::INITIAL
	state_machine_[INITIAL][SMI_READ_WRITE] = INITIAL;
	state_machine_[INITIAL][SMI_VALID] = VALID;
	state_machine_[INITIAL][SMI_INVALID] = INVALID;
	state_machine_[INITIAL][SMI_READ_ONLY] = RO_INITIAL;
	// State::VALID
	state_machine_[VALID][SMI_VALID] = VALID;
	state_machine_[VALID][SMI_READ_WRITE] = VALID;
	state_machine_[VALID][SMI_INVALID] = INVALID;
	state_machine_[VALID][SMI_OKAY] = INITIAL;
	state_machine_[VALID][SMI_RESTORE] = INITIAL;
	state_machine_[VALID][SMI_READ_ONLY] = RO_VALID;
	// State::INVALID
	state_machine_[INVALID][SMI_INVALID] = INVALID;
	state_machine_[INVALID][SMI_READ_WRITE] = INVALID;
	state_machine_[INVALID][SMI_VALID] = VALID;
	state_machine_[INVALID][SMI_RESTORE] = INITIAL;
	state_machine_[INVALID][SMI_READ_ONLY] = RO_INVALID;
	// State::RO_INITIAL
	state_machine_[RO_INITIAL][SMI_READ_ONLY] = RO_INITIAL;
	state_machine_[RO_INITIAL][SMI_VALID] = RO_VALID;
	state_machine_[RO_INITIAL][SMI_INVALID] = RO_INVALID;
	state_machine_[RO_INITIAL][SMI_READ_WRITE] = INITIAL;
	// State::RO_VALID
	state_machine_[RO_VALID][SMI_VALID] = RO_VALID;
	state_machine_[RO_VALID][SMI_READ_ONLY] = RO_VALID;
	state_machine_[RO_VALID][SMI_INVALID] = RO_INVALID;
	state_machine_[RO_VALID][SMI_READ_WRITE] = VALID;
	state_machine_[RO_VALID][SMI_RESTORE] = RO_INITIAL;
	// State::RO_INVALID
	state_machine_[RO_INVALID][SMI_READ_ONLY] = RO_INVALID;
	state_machine_[RO_INVALID][SMI_INVALID] = RO_INVALID;
	state_machine_[RO_INVALID][SMI_VALID] = RO_VALID;
	state_machine_[RO_INVALID][SMI_READ_WRITE] = INVALID;
	state_machine_[RO_INVALID][SMI_RESTORE] = RO_INITIAL;
}


void ButtonPolicy::Private::initNoRepeatedApplyReadOnly()
{
	outputs_ = StateOutputs(RO_INVALID + 1, ButtonPolicy::ALL_BUTTONS);
	state_machine_ = StateMachine(RO_INVALID + 1,
			 StateArray(int(SMI_TOTAL), ButtonPolicy::BOGUS));

	// Build the state output map
	outputs_[INITIAL] = CLOSE;
	outputs_[VALID] = RESTORE | OKAY | APPLY | CANCEL;
	outputs_[INVALID] = RESTORE | CANCEL;
	outputs_[RO_INITIAL] = CLOSE;
	outputs_[RO_VALID] = RESTORE | CANCEL;
	outputs_[RO_INVALID] = RESTORE | CANCEL;

	// Build the state machine one state at a time
	// NOTE:  Since CANCEL and HIDE always go to INITIAL they are
	//        left out of the state machine and handled explicitly
	//        in input()
	//
	// State::INITIAL
	state_machine_[INITIAL][SMI_READ_WRITE] = INITIAL;
	state_machine_[INITIAL][SMI_VALID] = VALID;
	state_machine_[INITIAL][SMI_INVALID] = INVALID;
	state_machine_[INITIAL][SMI_READ_ONLY] = RO_INITIAL;
	// State::VALID
	state_machine_[VALID][SMI_VALID] = VALID;
	state_machine_[VALID][SMI_READ_WRITE] = VALID;
	state_machine_[VALID][SMI_INVALID] = INVALID;
	state_machine_[VALID][SMI_OKAY] = INITIAL;
	state_machine_[VALID][SMI_APPLY] = INITIAL;
	state_machine_[VALID][SMI_RESTORE] = INITIAL;
	state_machine_[VALID][SMI_READ_ONLY] = RO_VALID;
	// State::INVALID
	state_machine_[INVALID][SMI_INVALID] = INVALID;
	state_machine_[INVALID][SMI_READ_WRITE] = INVALID;
	state_machine_[INVALID][SMI_VALID] = VALID;
	state_machine_[INVALID][SMI_OKAY] = INITIAL;
	state_machine_[INVALID][SMI_APPLY] = INITIAL;
	state_machine_[INVALID][SMI_RESTORE] = INITIAL;
	state_machine_[INVALID][SMI_READ_ONLY] = RO_INVALID;
	// State::RO_INITIAL
	state_machine_[RO_INITIAL][SMI_READ_ONLY] = RO_INITIAL;
	state_machine_[RO_INITIAL][SMI_VALID] = RO_VALID;
	state_machine_[RO_INITIAL][SMI_INVALID] = RO_INVALID;
	state_machine_[RO_INITIAL][SMI_READ_WRITE] = INITIAL;
	// State::RO_VALID
	state_machine_[RO_VALID][SMI_VALID] = RO_VALID;
	state_machine_[RO_VALID][SMI_READ_ONLY] = RO_VALID;
	state_machine_[RO_VALID][SMI_INVALID] = RO_INVALID;
	state_machine_[RO_VALID][SMI_READ_WRITE] = VALID;
	state_machine_[RO_VALID][SMI_RESTORE] = RO_INITIAL;
	// State::RO_INVALID
	state_machine_[RO_INVALID][SMI_INVALID] = RO_INVALID;
	state_machine_[RO_INVALID][SMI_READ_ONLY] = RO_INVALID;
	state_machine_[RO_INVALID][SMI_VALID] = RO_VALID;
	state_machine_[RO_INVALID][SMI_READ_WRITE] = INVALID;
	state_machine_[RO_INVALID][SMI_RESTORE] = RO_INITIAL;
}


void ButtonPolicy::Private::initOkApplyCancelReadOnly()
{
	outputs_ = StateOutputs(RO_APPLIED + 1, ButtonPolicy::ALL_BUTTONS);
	state_machine_ = StateMachine(RO_APPLIED + 1,
			 StateArray(int(SMI_TOTAL), ButtonPolicy::BOGUS));

	// Build the state output map
	outputs_[INITIAL] = CLOSE;
	outputs_[VALID] = RESTORE | OKAY | APPLY | CANCEL;
	outputs_[INVALID] = RESTORE | CANCEL;
	outputs_[APPLIED] = OKAY | APPLY | CLOSE;
	outputs_[RO_INITIAL] = CLOSE;
	outputs_[RO_VALID] = RESTORE | CANCEL;
	outputs_[RO_INVALID] = RESTORE | CANCEL;
	outputs_[RO_APPLIED] = CLOSE;

	// Build the state machine one state at a time
	// NOTE:  Since CANCEL and HIDE always go to INITIAL they are
	//        left out of the state machine and handled explicitly
	//        in input()
	//
	// State::INITIAL
	state_machine_[INITIAL][SMI_READ_WRITE] = INITIAL;
	state_machine_[INITIAL][SMI_VALID] = VALID;
	state_machine_[INITIAL][SMI_INVALID] = INVALID;
	state_machine_[INITIAL][SMI_READ_ONLY] = RO_INITIAL;
	// State::VALID
	state_machine_[VALID][SMI_VALID] = VALID;
	state_machine_[VALID][SMI_READ_WRITE] = VALID;
	state_machine_[VALID][SMI_INVALID] = INVALID;
	state_machine_[VALID][SMI_OKAY] = INITIAL;
	state_machine_[VALID][SMI_RESTORE] = INITIAL;
	state_machine_[VALID][SMI_APPLY] = APPLIED;
	state_machine_[VALID][SMI_READ_ONLY] = RO_VALID;
	// State::INVALID
	state_machine_[INVALID][SMI_INVALID] = INVALID;
	state_machine_[INVALID][SMI_READ_WRITE] = INVALID;
	state_machine_[INVALID][SMI_VALID] = VALID;
	state_machine_[INVALID][SMI_RESTORE] = INITIAL;
	state_machine_[INVALID][SMI_READ_ONLY] = RO_INVALID;
	// State::APPLIED
	state_machine_[APPLIED][SMI_APPLY] = APPLIED;
	state_machine_[APPLIED][SMI_READ_WRITE] = APPLIED;
	state_machine_[APPLIED][SMI_VALID] = VALID;
	state_machine_[APPLIED][SMI_INVALID] = INVALID;
	state_machine_[APPLIED][SMI_OKAY] = INITIAL;
	state_machine_[APPLIED][SMI_READ_ONLY] = RO_APPLIED;
	// State::RO_INITIAL
	state_machine_[RO_INITIAL][SMI_READ_ONLY] = RO_INITIAL;
	state_machine_[RO_INITIAL][SMI_VALID] = RO_VALID;
	state_machine_[RO_INITIAL][SMI_INVALID] = RO_INVALID;
	state_machine_[RO_INITIAL][SMI_READ_WRITE] = INITIAL;
	// State::RO_VALID
	state_machine_[RO_VALID][SMI_VALID] = RO_VALID;
	state_machine_[RO_VALID][SMI_READ_ONLY] = RO_VALID;
	state_machine_[RO_VALID][SMI_INVALID] = RO_INVALID;
	state_machine_[RO_VALID][SMI_READ_WRITE] = VALID;
	state_machine_[RO_VALID][SMI_RESTORE] = RO_INITIAL;
	// State::RO_INVALID
	state_machine_[RO_INVALID][SMI_INVALID] = RO_INVALID;
	state_machine_[RO_INVALID][SMI_READ_ONLY] = RO_INVALID;
	state_machine_[RO_INVALID][SMI_VALID] = RO_VALID;
	state_machine_[RO_INVALID][SMI_READ_WRITE] = INVALID;
	state_machine_[RO_INVALID][SMI_RESTORE] = RO_INITIAL;
	// State::RO_APPLIED
	state_machine_[RO_APPLIED][SMI_READ_ONLY] = RO_APPLIED;
	state_machine_[RO_APPLIED][SMI_INVALID] = RO_INVALID;
	state_machine_[RO_APPLIED][SMI_VALID] = RO_VALID;
	state_machine_[RO_APPLIED][SMI_READ_WRITE] = APPLIED;
}


void ButtonPolicy::Private::initOkApplyCancel()
{
	outputs_ = StateOutputs(APPLIED + 1, ButtonPolicy::ALL_BUTTONS);
	state_machine_ = StateMachine(APPLIED + 1,
			 StateArray(int(SMI_TOTAL), ButtonPolicy::BOGUS));

	// Build the state output map
	outputs_[INITIAL] = CLOSE;
	outputs_[VALID] = RESTORE | OKAY | APPLY | CANCEL;
	outputs_[INVALID] = RESTORE | CANCEL;
	outputs_[APPLIED] = OKAY | APPLY | CLOSE;

	// Build the state machine one state at a time
	// NOTE:  Since CANCEL and HIDE always go to INITIAL they are
	//        left out of the state machine and handled explicitly
	//        in input()
	//
	// State::INITIAL
	state_machine_[INITIAL][SMI_READ_ONLY] = INITIAL;
	state_machine_[INITIAL][SMI_READ_WRITE] = INITIAL;
	state_machine_[INITIAL][SMI_VALID] = VALID;
	state_machine_[INITIAL][SMI_INVALID] = INVALID;
	// State::VALID
	state_machine_[VALID][SMI_VALID] = VALID;
	state_machine_[VALID][SMI_READ_ONLY] = VALID;
	state_machine_[VALID][SMI_READ_WRITE] = VALID;
	state_machine_[VALID][SMI_INVALID] = INVALID;
	state_machine_[VALID][SMI_OKAY] = INITIAL;
	state_machine_[VALID][SMI_RESTORE] = INITIAL;
	state_machine_[VALID][SMI_APPLY] = APPLIED;
	// State::INVALID
	state_machine_[INVALID][SMI_INVALID] = INVALID;
	state_machine_[INVALID][SMI_READ_ONLY] = INVALID;
	state_machine_[INVALID][SMI_READ_WRITE] = INVALID;
	state_machine_[INVALID][SMI_VALID] = VALID;
	state_machine_[INVALID][SMI_RESTORE] = INITIAL;
	// State::APPLIED
	state_machine_[APPLIED][SMI_APPLY] = APPLIED;
	state_machine_[APPLIED][SMI_READ_ONLY] = APPLIED;
	state_machine_[APPLIED][SMI_READ_WRITE] = APPLIED;
	state_machine_[APPLIED][SMI_VALID] = VALID;
	state_machine_[APPLIED][SMI_INVALID] = INVALID;
	state_machine_[APPLIED][SMI_OKAY] = INITIAL;
}


void ButtonPolicy::Private::initOkApplyCancelAutoReadOnly()
{
	outputs_ = StateOutputs(RO_AUTOAPPLY + 1, ButtonPolicy::ALL_BUTTONS);
	state_machine_ = StateMachine(RO_AUTOAPPLY + 1,
			 StateArray(int(SMI_TOTAL), ButtonPolicy::BOGUS));

	// Build the state output map
	outputs_[INITIAL] = CLOSE | AUTOAPPLY;
	outputs_[VALID] = RESTORE | OKAY | APPLY | CANCEL | AUTOAPPLY;
	outputs_[INVALID] = RESTORE | CANCEL | AUTOAPPLY;
	outputs_[APPLIED] = OKAY | CLOSE | AUTOAPPLY;
	outputs_[AUTOAPPLY_INITIAL] = CLOSE | AUTOAPPLY;
	outputs_[AUTOAPPLY_CHANGED] = CLOSE | RESTORE | AUTOAPPLY | OKAY;
	outputs_[RO_INITIAL] = CLOSE;
	outputs_[RO_VALID] = RESTORE | CANCEL;
	outputs_[RO_INVALID] = RESTORE | CANCEL;
	outputs_[RO_APPLIED] = CLOSE;
	outputs_[RO_AUTOAPPLY] = CLOSE;

	// Build the state machine one state at a time
	// NOTE:  Since CANCEL and HIDE always go to INITIAL they are
	//        left out of the state machine and handled explicitly
	//        in input()
	//
	// State::INITIAL
	state_machine_[INITIAL][SMI_READ_WRITE] = INITIAL;
	state_machine_[INITIAL][SMI_VALID] = VALID;
	state_machine_[INITIAL][SMI_INVALID] = INVALID;
	state_machine_[INITIAL][SMI_READ_ONLY] = RO_INITIAL;
	state_machine_[INITIAL][SMI_AUTOAPPLY] = AUTOAPPLY_INITIAL;
	// State::VALID
	state_machine_[VALID][SMI_VALID] = VALID;
	state_machine_[VALID][SMI_READ_WRITE] = VALID;
	state_machine_[VALID][SMI_INVALID] = INVALID;
	state_machine_[VALID][SMI_OKAY] = INITIAL;
	state_machine_[VALID][SMI_RESTORE] = INITIAL;
	state_machine_[VALID][SMI_APPLY] = APPLIED;
	state_machine_[VALID][SMI_READ_ONLY] = RO_VALID;
	state_machine_[VALID][SMI_AUTOAPPLY] = AUTOAPPLY_INITIAL;
	// State::INVALID
	state_machine_[INVALID][SMI_INVALID] = INVALID;
	state_machine_[INVALID][SMI_READ_WRITE] = INVALID;
	state_machine_[INVALID][SMI_VALID] = VALID;
	state_machine_[INVALID][SMI_RESTORE] = INITIAL;
	state_machine_[INVALID][SMI_READ_ONLY] = RO_INVALID;
	state_machine_[INVALID][SMI_AUTOAPPLY] = AUTOAPPLY_CHANGED;
	// State::APPLIED
	state_machine_[APPLIED][SMI_APPLY] = APPLIED;
	state_machine_[APPLIED][SMI_READ_WRITE] = APPLIED;
	state_machine_[APPLIED][SMI_VALID] = VALID;
	state_machine_[APPLIED][SMI_INVALID] = INVALID;
	state_machine_[APPLIED][SMI_OKAY] = INITIAL;
	state_machine_[APPLIED][SMI_READ_ONLY] = RO_APPLIED;
	state_machine_[APPLIED][SMI_AUTOAPPLY] = AUTOAPPLY_INITIAL;
	// State::AUTOAPPLY_INITIAL
	state_machine_[AUTOAPPLY_INITIAL][SMI_AUTOAPPLY] = APPLIED;
	state_machine_[AUTOAPPLY_INITIAL][SMI_APPLY] = AUTOAPPLY_INITIAL;
	state_machine_[AUTOAPPLY_INITIAL][SMI_READ_ONLY] = RO_AUTOAPPLY;
	state_machine_[AUTOAPPLY_INITIAL][SMI_VALID] = AUTOAPPLY_CHANGED;
	state_machine_[AUTOAPPLY_INITIAL][SMI_INVALID] = AUTOAPPLY_CHANGED;
	state_machine_[AUTOAPPLY_INITIAL][SMI_READ_WRITE] = AUTOAPPLY_INITIAL;
	// State::AUTOAPPLY_CHANGED
	state_machine_[AUTOAPPLY_CHANGED][SMI_AUTOAPPLY] = APPLIED;
	state_machine_[AUTOAPPLY_CHANGED][SMI_READ_ONLY] = RO_AUTOAPPLY;
	state_machine_[AUTOAPPLY_CHANGED][SMI_RESTORE] = AUTOAPPLY_INITIAL;
	state_machine_[AUTOAPPLY_CHANGED][SMI_VALID] = AUTOAPPLY_CHANGED;
	state_machine_[AUTOAPPLY_CHANGED][SMI_INVALID] = AUTOAPPLY_CHANGED;
	state_machine_[AUTOAPPLY_CHANGED][SMI_READ_WRITE] = AUTOAPPLY_CHANGED;
	state_machine_[AUTOAPPLY_CHANGED][SMI_APPLY] = AUTOAPPLY_INITIAL;
	// State::RO_INITIAL
	state_machine_[RO_INITIAL][SMI_READ_ONLY] = RO_INITIAL;
	state_machine_[RO_INITIAL][SMI_VALID] = RO_VALID;
	state_machine_[RO_INITIAL][SMI_INVALID] = RO_INVALID;
	state_machine_[RO_INITIAL][SMI_READ_WRITE] = INITIAL;
	state_machine_[RO_INITIAL][SMI_AUTOAPPLY] = RO_AUTOAPPLY;
	// State::RO_VALID
	state_machine_[RO_VALID][SMI_VALID] = RO_VALID;
	state_machine_[RO_VALID][SMI_READ_ONLY] = RO_VALID;
	state_machine_[RO_VALID][SMI_INVALID] = RO_INVALID;
	state_machine_[RO_VALID][SMI_READ_WRITE] = VALID;
	state_machine_[RO_VALID][SMI_RESTORE] = RO_INITIAL;
	state_machine_[RO_VALID][SMI_AUTOAPPLY] = RO_AUTOAPPLY;
	// State::RO_INVALID
	state_machine_[RO_INVALID][SMI_INVALID] = RO_INVALID;
	state_machine_[RO_INVALID][SMI_READ_ONLY] = RO_INVALID;
	state_machine_[RO_INVALID][SMI_VALID] = RO_VALID;
	state_machine_[RO_INVALID][SMI_READ_WRITE] = INVALID;
	state_machine_[RO_INVALID][SMI_RESTORE] = RO_INITIAL;
	state_machine_[RO_INVALID][SMI_AUTOAPPLY] = RO_AUTOAPPLY;
	// State::RO_APPLIED
	state_machine_[RO_APPLIED][SMI_READ_ONLY] = RO_APPLIED;
	state_machine_[RO_APPLIED][SMI_INVALID] = RO_INVALID;
	state_machine_[RO_APPLIED][SMI_VALID] = RO_VALID;
	state_machine_[RO_APPLIED][SMI_READ_WRITE] = APPLIED;
	state_machine_[RO_APPLIED][SMI_AUTOAPPLY] = RO_AUTOAPPLY;
	// State::RO_AUTOAPPLY
	state_machine_[RO_AUTOAPPLY][SMI_READ_WRITE] = AUTOAPPLY_INITIAL;
}


void ButtonPolicy::Private::initNoRepeatedApply()
{
	outputs_ = StateOutputs(INVALID + 1, ButtonPolicy::ALL_BUTTONS);
	state_machine_ = StateMachine(INVALID + 1,
			 StateArray(int(SMI_TOTAL), ButtonPolicy::BOGUS));

	// Build the state output map
	outputs_[INITIAL] = CLOSE;
	outputs_[VALID] = RESTORE | OKAY | APPLY | CANCEL;
	outputs_[INVALID] = RESTORE | CANCEL;

	// Build the state machine one state at a time
	// NOTE:  Since CANCEL and HIDE always go to INITIAL they are
	//        left out of the state machine and handled explicitly
	//        in input()
	//
	// State::INITIAL
	state_machine_[INITIAL][SMI_READ_ONLY] = INITIAL;
	state_machine_[INITIAL][SMI_READ_WRITE] = INITIAL;
	state_machine_[INITIAL][SMI_VALID] = VALID;
	state_machine_[INITIAL][SMI_INVALID] = INVALID;
	// State::VALID
	state_machine_[VALID][SMI_VALID] = VALID;
	state_machine_[VALID][SMI_READ_ONLY] = VALID;
	state_machine_[VALID][SMI_READ_WRITE] = VALID;
	state_machine_[VALID][SMI_INVALID] = INVALID;
	state_machine_[VALID][SMI_OKAY] = INITIAL;
	state_machine_[VALID][SMI_APPLY] = INITIAL;
	state_machine_[VALID][SMI_RESTORE] = INITIAL;
	// State::INVALID
	state_machine_[INVALID][SMI_INVALID] = INVALID;
	state_machine_[INVALID][SMI_READ_ONLY] = INVALID;
	state_machine_[INVALID][SMI_READ_WRITE] = INVALID;
	state_machine_[INVALID][SMI_VALID] = VALID;
	state_machine_[INVALID][SMI_RESTORE] = INITIAL;
}


/////////////////////////////////////////////////////////////////////////
//
// ButtonPolicy
//
/////////////////////////////////////////////////////////////////////////

ButtonPolicy::ButtonPolicy(Policy policy)
	: d(new Private(policy))
{}


ButtonPolicy::~ButtonPolicy()
{
	delete d;
}


void ButtonPolicy::setPolicy(Policy policy)
{
	*d = Private(policy);
}


void ButtonPolicy::input(SMInput input)
{
	switch (d->policy_) {
		case PreferencesPolicy:
			// The APPLIED state is persistent. Next time the dialog is opened,
			// the user will be able to press 'Save'.
			if (SMI_CANCEL == input || SMI_HIDE == input) {
				if (d->state_ != APPLIED)
					d->state_ = INITIAL;
			} else {
				d->nextState(input);
			}
			break;
		case IgnorantPolicy:
			break;
		default:
			// CANCEL and HIDE always take us to INITIAL for all cases
			if (SMI_CANCEL == input || SMI_HIDE == input) {
				if (d->state_ == AUTOAPPLY_INITIAL
					  || d->state_ == AUTOAPPLY_CHANGED)
					d->state_ = AUTOAPPLY_INITIAL;
				else
					d->state_ = INITIAL;
			} else
				d->nextState(input);
			break;
	}
}


bool ButtonPolicy::buttonStatus(Button button) const
{
	return d->policy_ == IgnorantPolicy || (button & d->outputs_[d->state_]);
}


bool ButtonPolicy::isReadOnly() const
{
	switch (d->policy_) {
		case NoRepeatedApplyReadOnlyPolicy:
		case OkCancelReadOnlyPolicy:
		case OkApplyCancelReadOnlyPolicy:
			return RO_INITIAL == d->state_
				|| RO_VALID == d->state_
				|| RO_INVALID == d->state_
				|| RO_APPLIED == d->state_;
		default:
			return false;
	}
}



} // namespace frontend
} // namespace lyx
