# -------------------------------------------------------------------------------------
# flo::editor::Poll
# -------------------------------------------------------------------------------------
#
#       Author : Jean-Michel Hiver (jhiver@mkdoc.com).
#    Copyright : (c) MKDoc Holdings Ltd, 2001
# 
#      Unauthorized modification, use, reuse, distribution or redistribution
#      of this module is stricly forbidden.
#
#    Description:
#
#      Poll component. Lets the user adds polls to their pages
#
# -------------------------------------------------------------------------------------
package flo::editor::Poll;
use Time::Local;
use lib::util::Text2Html;
use flo::Standard;
use flo::Editor;
use lib::Exception;
use MKDoc::Util::Date;
use strict;


##
# $self->new;
# -----------
#   Returns a new flo::editor::Poll object based on CGI input
##
sub new
{
    my $class = shift;
    $class    = ref $class || $class;
    my $self  = bless { @_ }, $class;
    
    my $cgi = $self->{cgi};
    my $param_name = $self->{param_name};
    if (defined $cgi)
    {
	if (defined $cgi->param ($param_name . "_add"))
	{
	    $self->{id} = join '', map { chr ( ord ('A') + int (rand (26)) ) } 1..10;
	    $self->{question} = '';
	    $self->{answer1}  = '';
	    $self->{answer2}  = '';
	    $self->{answer3}  = '';
	    $self->{answer4}  = '';
	    $self->{answer5}  = '';
	    $self->{answer6}  = '';
	    $self->{answer7}  = '';
	    $self->{answer8}  = '';
	    $self->{answer9}  = '';
	}
	else
	{
	    $self->{id}       = $cgi->param ($param_name . '_id') || join '', map { chr ( ord ('A') + int (rand (26)) ) } 1..10;
	    $self->{question} = $cgi->param ($param_name . '_question') || '';
	    $self->{answer1}  = $cgi->param ($param_name . '_answer1')  || '';
	    $self->{answer2}  = $cgi->param ($param_name . '_answer2')  || '';
	    $self->{answer3}  = $cgi->param ($param_name . '_answer3')  || '';
	    $self->{answer4}  = $cgi->param ($param_name . '_answer4')  || '';
	    $self->{answer5}  = $cgi->param ($param_name . '_answer5')  || '';
	    $self->{answer6}  = $cgi->param ($param_name . '_answer6')  || '';
	    $self->{answer7}  = $cgi->param ($param_name . '_answer7')  || '';
	    $self->{answer8}  = $cgi->param ($param_name . '_answer8')  || '';
	    $self->{answer9}  = $cgi->param ($param_name . '_answer9')  || '';
	    
	    # compute current date
	    my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime (time);
	    $mon++;
	    $year += 1900;
	    
	    my $start_year  = $cgi->param ($param_name . '_date_year_start')  || $year;
	    my $start_month = $cgi->param ($param_name . '_date_month_start') || $mon;
	    my $start_day   = $cgi->param ($param_name . '_date_day_start')   || $mday;
	    my $start_hour  = $cgi->param ($param_name . '_date_hour_start')  || $hour;
	    $start_year  =~ /^-?\d+$/ or $start_year  = $year;
	    $start_month =~ /^-?\d+$/ or $start_month = $mon;
	    $start_day   =~ /^-?\d+$/ or $start_day   = $mday;
	    $start_hour  =~ /^-?\d+$/ or $start_hour  = $hour;
	    
	    # fix the month if it needs to be
	    $start_month > 0 and $start_month < 13 or $start_month = 1;
	    
	    # fix the day if it needs to be
	    $start_day = 1  if ($start_day < 1);
	    $start_day = 28 if ( ( ($start_year % 4) and ($start_month == 2) ) and ($start_day > 28) );
	    $start_day = 29 if ( ( ($start_year %4 == 0) and ($start_month == 2) ) and ($start_day > 29) );
	    
	    $start_day = 30 if ( ( ($start_month == 4) or ($start_month == 6) or
				   ($start_month == 9) or ($start_month == 11) ) and $start_day > 30 );
	    
	    $start_day = 31 if ( ( ($start_month == 1) or ($start_month == 3)  or
				   ($start_month == 5) or ($start_month == 7)  or
				   ($start_month == 8) or ($start_month == 10) or
				   ($start_month == 12) ) and $start_day > 31 );
	    
	    $start_hour = 0  if ($start_hour < 0);
	    $start_hour = 23 if ($start_hour > 23);
	    
	    my $stop_year  = $cgi->param ($param_name . '_date_year_stop')  || $year;
	    my $stop_month = $cgi->param ($param_name . '_date_month_stop') || $mon;
	    my $stop_day   = $cgi->param ($param_name . '_date_day_stop')   || $mday;
	    my $stop_hour  = $cgi->param ($param_name . '_date_hour_stop')  || $hour;
	    $stop_year  =~ /^-?\d+$/ or $stop_year  = $year;
	    $stop_month =~ /^-?\d+$/ or $stop_month = $mon;
	    $stop_day   =~ /^-?\d+$/ or $stop_day   = $mday;
	    $stop_hour  =~ /^-?\d+$/ or $stop_hour  = $hour;
	    
	    # fix the month if it needs to be
	    $stop_month > 0 and $stop_month < 13 or $stop_month = 1;
	    
	    # fix the day if it needs to be
	    $stop_day > 0 or $stop_day = 1;
	    $stop_day = 28 if ( ( ($stop_year % 4) and ($stop_month == 2) ) and ($stop_day > 28) );
	    $stop_day = 29 if ( ( ($stop_year %4 == 0) and ($stop_month == 2) ) and ($stop_day > 29) );
	    
	    $stop_day = 30 if ( ( ($stop_month == 4) or ($stop_month == 6) or
				  ($stop_month == 9) or ($stop_month == 11) ) and $stop_day > 30 );
	    
	    $stop_day = 31 if ( ( ($stop_month == 1) or ($stop_month == 3)  or
				  ($stop_month == 5) or ($stop_month == 7)  or
				  ($stop_month == 8) or ($stop_month == 10) or
				  ($stop_month == 12) ) and $stop_day > 31 );
	    
	    $stop_hour = 0  if ($stop_hour < 0);
	    $stop_hour = 23 if ($stop_hour > 23);
	    
	    $self->{date_year_start}  = $start_year;
	    $self->{date_month_start} = $start_month;
	    $self->{date_day_start}   = $start_day;
	    $self->{date_hour_start}  = $start_hour;
	    $self->{date_year_stop}   = $stop_year;
	    $self->{date_month_stop}  = $stop_month;
	    $self->{date_day_stop}    = $stop_day;
	    $self->{date_hour_stop}   = $stop_hour;
	}
    }
    else
    {
	$self->{id}       = join '', map { chr ( ord ('A') + int (rand (26)) ) } 1..10;
	$self->{question} = '';
	$self->{answer1}  = '';
	$self->{answer2}  = '';
	$self->{answer3}  = '';
	$self->{answer4}  = '';
	$self->{answer5}  = '';
	$self->{answer6}  = '';
	$self->{answer7}  = '';
	$self->{answer8}  = '';
	$self->{answer9}  = '';
    }
    $self->{class} = 'poll';
    return $self;
}


##
# $self->Lang;
# ------------
#   returns the language which is associated with that poll component
##
sub Lang
{
    my $self = shift;

    my $path_info = path_info;
    if ($path_info ne '/')
    {
        $path_info =~ s/^\///;
        $path_info =~ s/\/$//;
        my @path_info = split /\//, $path_info;
        pop (@path_info);
        if (@path_info) { $path_info = '/' . (join '/', @path_info) . '/' }
        else { $path_info = '/' }
    }

    my $document_table = table ('Document');
    my $document = $document_table->get ( Full_Path => $path_info ) or
	throw (new lib::Exception ( code => 'CANNOT_GET_DOCUMENT',
				    info => "document with path $path_info does not seem to exists" ) );
    return $document->{Lang};
}


##
# $self->start_localtime;
# -----------------------
#   Compute localtime from which to activate the poll
##
sub start_localtime
{
    my $self = shift;
    return timelocal ( 0, 0,
		       $self->{date_hour_start}, $self->{date_day_start},
		       $self->{date_month_start} - 1, $self->{date_year_start} - 1900 );
}


##
# $self->StartDate;
# -----------------
#   Returns a human readable start date for the poll
##
sub StartDate
{
    my $self = shift;
    my $date = new MKDoc::Util::Date ($self->start_localtime);
    my $tmpl = '<WDAY_LANG> <MDAY> <MON_LANG> <YEAR>, <HOUR_12_PADDED>:00<AM_PM>';
    my $lang = $self->Lang;
    return $date->filter ($tmpl, $lang);
}


##
# $self->stop_localtime;
# ----------------------
#   Compute localtime from which to stop the poll
##
sub stop_localtime
{
    my $self = shift;
    return timelocal ( 0, 0,
		       $self->{date_hour_stop}, $self->{date_day_stop},
		       $self->{date_month_stop} - 1, $self->{date_year_stop} - 1900 );
}


##
# $self->StopDate;
# ----------------
#   Returns a human readable start date for the poll
##
sub StopDate
{
    my $self = shift;
    my $date = new MKDoc::Util::Date ($self->stop_localtime);
    my $tmpl = '<WDAY_LANG> <MDAY> <MON_LANG> <YEAR>, <HOUR_12_PADDED>:00<AM_PM>';
    my $lang = $self->Lang;
    return $date->filter ($tmpl, $lang);
}


##
# $self->is_started;
# ------------------
#   Returns TRUE if the poll is started, FALSE otherwise
##
sub is_started
{
    my $self = shift;
    return time > $self->start_localtime;
}


##
# $self->is_stopped;
# ------------------
#   Returns TRUE is the poll is stopped, FALSE otherwise
##
sub is_stopped
{
    my $self = shift;
    return time > $self->stop_localtime;
}


##
# $self->edit;
# ------------
#   Returns the edit HTML chunk for that component
##
sub edit
{
    my $self = shift;
    my $hash = {
	block_name       => $self->{param_name},
	id               => $self->{id},
	question         => $self->{question},
	answer1          => $self->{answer1},
	answer2          => $self->{answer2},
	answer3          => $self->{answer3},
	answer4          => $self->{answer4},
	answer5          => $self->{answer5},
	answer6          => $self->{answer6},
	answer7          => $self->{answer7},
	answer8          => $self->{answer8},
	answer9          => $self->{answer9},
	date_day_start   => $self->{date_day_start},
	date_month_start => $self->{date_month_start},
	date_year_start  => $self->{date_year_start},
	date_hour_start  => $self->{date_hour_start},
	date_day_stop    => $self->{date_day_stop},
	date_month_stop  => $self->{date_month_stop},
	date_year_stop   => $self->{date_year_stop},
	date_hour_stop   => $self->{date_hour_stop},
    };

    return new flo::Template ( file => $self->edit_template_path,
			       hash => $hash )->parse;
}


##
# $self->Count_Votes;
# -------------------
#   Count the number of votes for that poll
##
sub Count_Votes
{
    my $self = shift;
    my $poll_results_table = table ('Poll_Results');
    my $count = 0;
    my $query = $poll_results_table->select ( where => { Poll_ID => $self->{id} } );

    while (my $vote = $query->next)
    {
	$count++;
    }
    
    return $count;
}


##
# $self->Question;
# ----------------
#   alias for $self->question;
##
sub Question
{
    my $self = shift;
    return $self->{question};
}


##
# $self->results;
# ---------------
#   Returns the results for that poll component
##
sub Results
{
    my $self = shift;
    my $poll_results_table = table ('Poll_Results');
    
    # now we need to compute the results for that poll;
    # query the poll tables to get the results for that vote, and
    # aggregates these results
    my $votes = {};
    my $count = 0;
    my $query = $poll_results_table->select ( where => { Poll_ID => $self->{id} } );
    
    while (my $vote = $query->next)
    {
	$count++;
	my $answer = $vote->{Answer};
	if (defined $votes->{$answer}) { $votes->{$answer}++   }
	else                           { $votes->{$answer} = 1 }
    }
    
    # if count is equals to zero, then scores should be quite easy to
    # compute. We need to return before we screw up with a division
    # by zero error
    my @poll_results = ();
    if ($count == 0)
    {
	foreach my $answer_name (qw /answer1 answer2 answer3 answer4 answer5
				 answer6 answer7 answer8 answer9/)
	{
	    my $answer = $self->{$answer_name};
	    next unless (defined $answer and $answer ne '');
	    push @poll_results, {
		Answer => $answer,
		Votes  => 0,
		Score  => 0,
		Score_Array => [],
	    };
	}
    }
    else
    {
	foreach my $answer_name (qw /answer1 answer2 answer3 answer4 answer5
				 answer6 answer7 answer8 answer9/)
	{
	    my $answer = $self->{$answer_name};
	    next unless (defined $answer and $answer ne '');
	    my $votes = $votes->{$answer_name};
	    $votes = 0 unless (defined $votes);
	    
	    my $score = 100 * $votes / $count;
	    unless ($score == int ($score))
	    {
		my $score_low = int ($score);
		my $score_high = $score_low + 1;
		if (abs ($score - $score_low) < abs ($score - $score_high))
		{
		    $score = $score_low;
		}
		else
		{
		    $score = $score_high;
		}
	    }
	    
	    push @poll_results, {
		Answer => $answer,
		Votes  => $votes,
		Score  => $score,
		Score_Array => [ map { {} } 1 .. $score ],
	    }
	}
    }
    return wantarray ? @poll_results : \@poll_results;
}


##
# $self->edit_template_path;
# --------------------------
#   Computes the path of the template which has to be used for
#   that component given the current document language
##
sub edit_template_path
{
    my $self = shift;
    
    my $document_table = table ('Document');
    my $document = $document_table->get ( Full_Path => path_info );
    my $lang = $document->{Lang};
    
    return template ('editor/poll', $lang);
}


##
# $self->view_template_path;
# --------------------------
#   Computes the path of the template which has to be used for
#   that component given the current document language
##
sub view_template_path
{
    my $self = shift;
    
    my $document_table = table ('Document');
    my $document = $document_table->get ( Full_Path => path_info );
    my $lang = $document->{Lang};
    
    return template ('editor/poll/view', $lang);
}


##
# $self->view_results_uri;
# ------------------------
#   Computes the URI to see the results for that poll
##
sub view_results_uri
{
    my $self = shift;
    
    my $cfg = new lib::Config ('flo.plugin.Poll');
    my $http_name = $cfg->get ('http_name');
    return $http_name . '?Poll_ID=' . $self->{id};
}


##
# $self->generate_xml;
# --------------------
#   Generates the XML for that component
##
sub generate_xml
{
    my $self = shift;
    my @res  = ();
    push @res, "<id>" . $self->{id} . "</id>";
    push @res, "<startdate>" . $self->start_localtime . "</startdate>";
    push @res, "<stopdate>" . $self->stop_localtime . "</stopdate>";
    push @res, "<question>" . text2xml ($self->{question}) . "</question>";
    for ( $self->{answer1}, $self->{answer2}, $self->{answer3}, $self->{answer4},
	  $self->{answer5}, $self->{answer6}, $self->{answer7}, $self->{answer8},
	  $self->{answer9} )
    {
	push @res, "<answer>" . text2xml ($_) . "</answer>";
    }
    
    return join "\n", @res;
}


##
# $self->parse_xml;
# -----------------
#   Parses the XML to reconstruct the component
##
sub parse_xml
{
    my $self = shift;
    my $data = shift;
    
    ($self->{id})       = $data =~ /\<id\>(.*?)\<\/id\>/;
    ($self->{question}) = $data =~ /\<question\>(.*?)\<\/question\>/;
    ( $self->{answer1}, $self->{answer2}, $self->{answer3}, $self->{answer4}, 
      $self->{answer5}, $self->{answer6}, $self->{answer7}, $self->{answer8}, 
      $self->{answer9} ) = $data =~ /\<answer\>(.*?)\<\/answer\>/gsim;
    
    $self->{question} = html2text ($self->{question});
    $self->{answer1}  = html2text ($self->{answer1});
    $self->{answer2}  = html2text ($self->{answer2});
    $self->{answer3}  = html2text ($self->{answer3});
    $self->{answer4}  = html2text ($self->{answer4});
    $self->{answer5}  = html2text ($self->{answer5});
    $self->{answer6}  = html2text ($self->{answer6});
    $self->{answer7}  = html2text ($self->{answer7});
    $self->{answer8}  = html2text ($self->{answer8});
    $self->{answer9}  = html2text ($self->{answer9});

    my ($start_localtime) = $data =~ /\<startdate\>(.*?)\<\/startdate\>/ism;
    my ($stop_localtime)  = $data =~ /\<stopdate\>(.*?)\<\/stopdate\>/ism;
    
    my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime ($start_localtime);
    $self->{date_hour_start}  = $hour;
    $self->{date_day_start}   = $mday;
    $self->{date_month_start} = $mon + 1;
    $self->{date_year_start}  = $year + 1900;
    
    ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime ($stop_localtime);
    $self->{date_hour_stop}   = $hour;
    $self->{date_day_stop}    = $mday;
    $self->{date_month_stop}  = $mon + 1;
    $self->{date_year_stop}   = $year + 1900;
}


##
# $self->html;
# ------------
#   Return the edit HTML chunk for that component
##
sub html
{
    my $self = shift;
    return "[ Poll Component ]";
    # return new flo::Template ( file => $self->view_template_path, hash => $self );
}


##
# $self->to_html;
# ---------------
#   Return the edit HTML chunk for that component
##
sub to_html
{
    my $self = shift;
    return $self->html;
}


1;
