The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl -w

=head1 NAME

Quizzer::FrontEnd - base FrontEnd

=cut

=head1 DESCRIPTION

This is the base of the FrontEnd class. Each FrontEnd presents a
user interface of some kind to the user, and handles generating and
communicating with Elements to form that FrontEnd.

=cut

=head1 METHODS

=cut

package Quizzer::FrontEnd;
use Quizzer::Level;
use Quizzer::Config;
use Quizzer::Base;
use Quizzer::Log ':all';
use strict;
use vars qw(@ISA);
@ISA=qw(Quizzer::Base);

my $VERSION='0.01';

=head2 new

Creates a new FrontEnd object and returns it.

=cut

sub new {
	my $proto = shift;
	my $class = ref($proto) || $proto;
	my $self  = bless $proto->SUPER::new(@_), $class;
	$self->{elements}=[];
	$self->{interactive}='';
	$self->{capb}='';
	$self->{title}="";
	return $self
}

=head2 makeelement

This helper function creates an Element. Pass in the type of Frontend the
Element is for, and the Question that will be bound to the Element. It returns
the generated Element, or false if it was unable to make an Element of the given
type.

=cut

sub makeelement {
	my $this=shift;
	my $frontend_type=shift;
	my $question=shift;

	my $type=$frontend_type.'::'.ucfirst($question->template->type);
	debug 2, "Trying to make element of type $type";
	my $element=eval qq{
		use Quizzer::Element::$type;
		Quizzer::Element::$type->new;
	};
	debug 2, "Failed with $@" if $@;
	if (! ref $element) {
		return;
	}
	$element->frontend($this);
	$element->question($question);
	return $element;
}

=head2 add

Add a Question to the list to be displayed to the user. Pass the Question and
text indicating the level of the Question. This creates an Element and adds
it to the array in the elements property. Returns true if the created Element
is visible.

=cut

sub add {
	my $this=shift;
	my $question=shift || die "\$question is undefined";
	my $level=shift;

	# Figure out if the question should be displayed to the user or not.
	my $visible=1;

	# Noninteractive frontends never show anything.
	$visible='' if ! $this->interactive;
	
	# Don't show items that are unimportant.
	$visible='' unless Quizzer::Level::high_enough($level);
	
	# Set showold to ask even default questions.
	$visible='' if Quizzer::Config::showold() eq 'false' &&
		$question->flag_isdefault eq 'false';
	
	my $element;
	if ($visible) {
		# Create an input Element of the type associated with
		# this frontend. This requires some nastiness.
		my ($frontend_type)=ref($this)=~m/Quizzer::FrontEnd::(.*)/;
		$element=$this->makeelement($frontend_type, $question) ||
			 die "Unknown type of element";

		# Ask the Element if it thinks it is visible. If not,
		# fall back below to making a noninteractive element.
		#
		# This last check is useful, because for example, select
		# Elements are not really visible if they have less than two
		# choices.
		$visible=$element->visible;
	}
	if (! $visible) {
		# Create a noninteractive element.
		$element=$this->makeelement('Noninteractive', $question) ||
			return; # no noninteractive element of this type.
	}
	
	push @{$this->{elements}}, $element;
	return $element->visible;
}

=head2 go

Display accumulated Elements to the user. The Elements are in the elements
property, and that property is cleared after the Elements are presented.

After showing each element, checks to see if the object's backup property has
been set; if so, doen't display any of the other pending questions (remove them
from the buffer), and return false. The default is to return true.

The return value of each element's show() method is used to set the value of
the question associated with that element.

=cut

sub go {
	my $this=shift;

	debug 2, "preparing to ask questions";
	foreach my $element (@{$this->elements}) {
		my $value=$element->show;
		if ($this->backup) {
			$this->{elements}=[];
			$this->backup('');
			return;
		}
		$element->question->value($value);
		# Only set isdefault if the element was visible, because we
		# don't want to do it when showing noninteractive select 
		# elements and so on.
		$element->question->flag_isdefault('false')
			if $element->visible;
	}
	$this->clear;
	return 1;
}

=head2 clear

Clear out the accumulated elements.

=cut

sub clear {
	my $this=shift;
	
	$this->{elements}=[];
}

=head2 default_title

This sets the title property to a default. Pass in the name of the
package that is being configured.

=cut

sub default_title {
	my $this=shift;
	
	$this->title(ucfirst(shift));
}

=head1 AUTHOR

Joey Hess <joey@kitenet.net>

=cut

1