/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" and
 *     "Apache Jetspeed" must not be used to endorse or promote products
 *    derived from this software without prior written permission. For
 *    written permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache" or
 *    "Apache Jetspeed", nor may "Apache" appear in their name, without
 *    prior written permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

package org.apache.jetspeed.portal.portlets;

//standard java stuff
import java.io.*;
import java.util.*;

//Element Construction Set
import org.apache.ecs.html.*;
import org.apache.ecs.ElementContainer;
import org.apache.ecs.ConcreteElement;
import org.apache.ecs.ClearElement;
import org.apache.ecs.StringElement;

//standard Jetspeed stuff
import org.apache.jetspeed.util.*;
import org.apache.jetspeed.portal.*;

//utils used by the PollPortlet
import org.apache.jetspeed.portal.portlets.util.poll.*;


//turbine
import org.apache.turbine.util.*;


/**
A simple portlet for allowing users to vote on features.

THIS IS CURRENTLY EXPERIMENTAL!!!

@author <A HREF="mailto:burton@apache.org">Kevin A. Burton</A>
@version $Id: PollPortlet.java,v 1.3 2001/03/07 07:12:35 taylor Exp $ 
*/
public class PollPortlet extends AbstractPortlet {


    public static final String VOTE         = "Vote";
    

    public static final String ACTION_KEY   = "jetspeed.poll.action";
    public static final String VOTE_KEY     = "jetspeed.poll.vote";
    public static final String QUESTION_KEY = "jetspeed.poll.question";
    public static final String ANSWER_KEY   = "jetspeed.poll.answer";
    public static final String RESULTS_KEY  = "jetspeed.poll.results";
    
    public static final String WARNING = "NOTE:  Do to use this poll for anything important, it might not be accurate!";
    
    //misc actions..
    /**
    Used when casting your vote
    */
    public static final String ACTION_VOTE = VOTE_KEY;

    /**
    Used to get the results.
    */
    public static final String ACTION_GET_RESULTS = "jetspeed.pool.results";

    /**
    The mainbar gif used when building a vote
    */
    public static final String IMG_MAINBAR = "images/poll/mainbar.gif";

    /**
    The default width for the polling image.
    */
    public static final int DEFAULT_IMG_WIDTH = 400;

    /**
    The default height for the polling image.    
    */
    public static final int DEFAULT_IMG_HEIGHT = 20;
    
    private AnswerGroup ag = null;
    private Question question = new Question( "Question?  What question?  :)" );

    /**
    Holds the IP addresses of people that have already voted.
    */
    private Hashtable votes = new Hashtable();
    
    /**
    Get the vote prompt or post the vote.
    
    @see Portlet.getContent
    @author <A HREF="mailto:burton@apache.org">Kevin A. Burton</A>
    @version $Id: PollPortlet.java,v 1.3 2001/03/07 07:12:35 taylor Exp $ 
    */
    public ConcreteElement getContent(RunData rundata) {
        
        String action = rundata.getParameters().getString( ACTION_KEY );

        if ( action == null ) {

            return  getUI( rundata );

        } else if ( action.equals( VOTE_KEY ) ) {

            //cast the vote if necessary.
            if ( ! this.hasVoted( rundata ) ) {
                this.getAnswerGroup()
                    .getAnswer( rundata
                        .getParameters().getInt( ANSWER_KEY ) ).castVote();
            }

        } 
        
        
        //if the user requests the results or just votes... then return the
        //vote screen.
        if ( action.equals( VOTE_KEY ) ||
             action.equals( RESULTS_KEY ) ) {

            ConcreteElement results = this.getResults( rundata );
                 
            //set this user/IP to already voted.
            if ( action.equals( VOTE_KEY ) ) {
                this.setVoted( true, rundata );
            }
                 
            return results;
             
        } 

        return getUI( rundata );

    }
    
    /**
    Parse out the init parameters to get the questions and various answers.

    @see Portlet.init()
    */
    public void init() {
        
        this.setQuestion( new Question( this.getPortletConfig().getInitParameter( QUESTION_KEY ) ) );
        
        //get the questions and answers for this Poll.
        Enumeration params = this.getPortletConfig().getInitParameterNames();
        
        Vector v = new Vector();
        
        int id = 0;
        while ( params.hasMoreElements() ) {
            
            String key = (String)params.nextElement();
            if( key.indexOf( ANSWER_KEY ) > -1 ) {
                v.addElement( new Answer( this.getPortletConfig().getInitParameter( key ), id ) );
                ++id;
            }
            
        }

        
        Answer[] answers = new Answer[v.size()];
        v.copyInto( answers );
        this.ag = new AnswerGroup( answers );

        //set the title and description.
        this.setTitle( VOTE );
        this.setDescription( VOTE + ": " + this.getQuestion().getTitle() );
    }

    /** 
        Builds the voting form 
    */
    private ConcreteElement getUI( RunData rundata ) {
        
        Form form = new Form();
        
        //add the title to the form
        form.addElement( new B().addElement( this.getQuestion().getTitle() ) );
        
        
        Answer[] answers = this.ag.getAnswers();
        //add the answers to the form
        for( int i = 0; i < answers.length; ++i ) {
            
            form.addElement( new BR() );
            form.addElement( new Input().setType( "radio" )
                                        .setName( ANSWER_KEY )
                                        .setValue( answers[i].getID() )
                                .addElement( answers[i].getTitle() ) );
            
        }
        
        form.addElement( new BR() );

        //add the vote button.
        form.addElement( new Input().setType( "submit" )
                                    .setValue( VOTE ) );

                                    
        //add the link s that you can see results.
        form.addElement( " [ " );
        
        DynamicURI uri = new DynamicURI( rundata );
        uri.addQueryData( ACTION_KEY, RESULTS_KEY );
        form.addElement( new A( uri.toString() ).addElement( "Results" ) );
        
        form.addElement( " ] " );        
        
        //add the action for voting.
        form.addElement( new Input().setType( "hidden" )
                                    .setName( ACTION_KEY )
                                    .setValue( VOTE_KEY ) );
                                    
        return form;
    }
    
    /**
    Get all available answers for this poll.
    @author <A HREF="mailto:burton@apache.org">Kevin A. Burton</A>
    @version $Id: PollPortlet.java,v 1.3 2001/03/07 07:12:35 taylor Exp $ 
    */
    public AnswerGroup getAnswerGroup() {
        return this.ag;
    }
    
    /**
    Set the question for this poll
    @author <A HREF="mailto:burton@apache.org">Kevin A. Burton</A>
    @version $Id: PollPortlet.java,v 1.3 2001/03/07 07:12:35 taylor Exp $ 
    */    
    public void setQuestion( Question question ) {
        this.question = question;
    }
    
    /**
    Get the question for this poll.
    @author <A HREF="mailto:burton@apache.org">Kevin A. Burton</A>
    @version $Id: PollPortlet.java,v 1.3 2001/03/07 07:12:35 taylor Exp $ 
    */
    public Question getQuestion() {
        return this.question;
    }
    
    
    //begin private IMPL methods
    
    /**
    Get the results of this poll and display them in a table format.
    @author <A HREF="mailto:burton@apache.org">Kevin A. Burton</A>
    @version $Id: PollPortlet.java,v 1.3 2001/03/07 07:12:35 taylor Exp $ 
    */
    private ConcreteElement getResults( RunData rundata ) {
        
        ElementContainer ec = new ElementContainer();

        AnswerGroup ag = this.getAnswerGroup();
        
        Answer[] answers = ag.getAnswers();
        
        int top = ag.getTopAnswer().getVoteCount();
        
        ConcreteElement nbsp = new ClearElement( "&nbsp;" );

        //make sure the user hasn't voted before.
        if ( this.hasVoted( rundata ) ) {
            ec.addElement( new I().addElement( "You have already voted!" ) );
            ec.addElement( new BR() );
        }
        
        
        //add the question to the Portlet so that he user knows what they just
        //voted on.
        ec.addElement( new B().addElement( this.getQuestion().getTitle() ) );        
        
        Table table = new Table();
        
        for( int i = 0; i < answers.length; ++i ) {
            
            Answer answer = answers[i];
            
            //compute the width of this vote item.

            int width = 0;
            
            TR row = new TR();

            row.addElement( getColumn( answer.getTitle() ) );
            
            if ( answer.getVoteCount() == 0 ) {
                width = 1;
            } else { 
                width = ( DEFAULT_IMG_WIDTH / top ) * answer.getVoteCount();
            }
            

            
            TD result = getColumn( new IMG().setSrc( IMG_MAINBAR )
                                                .setWidth( width )
                                                .setHeight( DEFAULT_IMG_HEIGHT ) );

            //get the current vote count and percentage
            result.addElement( nbsp );
            result.addElement( Integer.toString( answer.getVoteCount() ) +
                               " / " + 
                               Integer.toString( ag.getPercentage( answer ) ) +
                               "%" );
                                                
            row.addElement( result );
            
            row.addElement( new TD().setWidth( "100%" ) );
            
            table.addElement( row );
        }
        
        ec.addElement( table );
        
        //now add the total number of votes.
        ec.addElement( new BR() );
        ec.addElement( new B()
            .addElement( Integer.toString( this.getAnswerGroup().getTotal() ) + 
                         " total vote(s)" ) );

        //add the warning
        ec.addElement( new BR() );
        ec.addElement( WARNING );
                         
        return ec;
    }

    /**
    Get a column for the vote table.

    @author <A HREF="mailto:burton@apache.org">Kevin A. Burton</A>
    @version $Id: PollPortlet.java,v 1.3 2001/03/07 07:12:35 taylor Exp $ 
    */    
    private TD getColumn( String data ) {
        
        return getColumn( new StringElement( data ) );
        
    }
    

    /**
    Get a column for the vote table.

    @author <A HREF="mailto:burton@apache.org">Kevin A. Burton</A>
    @version $Id: PollPortlet.java,v 1.3 2001/03/07 07:12:35 taylor Exp $ 
    */    
    private TD getColumn( ConcreteElement ce ) {
        
        return new TD().setVAlign( "middle" )
                       .setAlign( "left" )
                       .setNoWrap( true )
                       .addElement( ce );
        
    }
    
    /**
    Returns true if this user/IP has already voted

    @author <A HREF="mailto:burton@apache.org">Kevin A. Burton</A>
    @version $Id: PollPortlet.java,v 1.3 2001/03/07 07:12:35 taylor Exp $ 
    */
    private boolean hasVoted( RunData rundata ) {
        
        /* FIX ME:
          
          This uses RunData and the Servlet API directly... a change is pending
          to the Turbine User object for getRemoteAddr and this code should be
          changed when done.
        
        */
        
        String addr = rundata.getRequest().getRemoteAddr();
        
        if ( addr == null ) {
            return false;
        }
        
        if ( this.votes.get( addr ) == null ) {
            return false;
        } else {
            return true;
        }
        
    }

    /**
    Set the current user's status to voted.
    
    @author <A HREF="mailto:burton@apache.org">Kevin A. Burton</A>
    @version $Id: PollPortlet.java,v 1.3 2001/03/07 07:12:35 taylor Exp $ 
    */
    public void setVoted( boolean voted, RunData rundata ) {
        
        String addr = rundata.getRequest().getRemoteAddr();

        if ( voted ) {

            if ( this.votes.get( addr ) == null ) {
                this.votes.put( addr, addr );
            }
            
        } else {
            this.votes.remove( addr );
        }
            
        
    }
    
}




