package com.oreilly.jent.people.tags;

import java.util.ArrayList;
import java.util.logging.Logger;

import javax.ejb.CreateException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.BodyTagSupport;
import javax.servlet.jsp.tagext.Tag;

import com.oreilly.jent.people.InvalidSearchException;
import com.oreilly.jent.people.PersistenceException;
import com.oreilly.jent.people.Person;
import com.oreilly.jent.people.PersonDAO;
import com.oreilly.jent.people.SearchArg;
import com.oreilly.jent.people.ejb.PeopleFinderLocal;
import com.oreilly.jent.people.ejb.PeopleFinderLocalHome;


/**
 * SearchTag: A custom tag that performs a search against a person database,
 *    given one or more search fields.  The results of the search consist
 *    of a Collection of Person beans, used to populate a page variable
 *    with page scope, whose variable name is set on the tag using the
 *    "varName" attribute.
 * 
 *    The tag supports four attributes in total:
 * 
 *      - varName : Name to be assigned to the page variable holding 
 *                  the results of the search.  If any error is generated
 *                  while processing the search, an error message will be
 *                  place into a page variable named "<varName>-error".
 *      - firstNamePattern : Pattern to search for in the first name field.
 *      - lastNamePattern : Pattern to search for in the last name field.
 *      - emailPattern : Pattern to search for in the email field.
 * 
 *    All search patterns are "AND"ed together in the search.  Each search field
 *    is optional, but a search must include at least one search pattern.
 *    Search patterns can include the '*' character as a wildcard.  If no 
 *    wildcards are included in the pattern, then a "contains" search is used 
 *    for that field.
 */

public class SearchTag extends BodyTagSupport {
    private Logger mLog = Logger.getLogger(SearchTag.class.getName());
    
    private String mFirstNamePattern = "";
    private String mLastNamePattern = "";
    private String mEmailPattern = "";
    private String mVarName = "";

    public SearchTag() {
    }

    /** Set the search pattern for first name */
    public void setFirstNamePattern(String mFirstNamePattern) {
        this.mFirstNamePattern = mFirstNamePattern;
    }
    
    /** Get the search pattern for first name */
    public String getFirstNamePattern() {
        return mFirstNamePattern;
    }
    
    /** Predicate, to detemine whether a first name pattern has been set */
    public boolean hasFirstNamePattern() {
        return (this.getFirstNamePattern() != null && 
                this.getFirstNamePattern().length() > 0);
    }

    /** Set the search pattern for last name */
    public void setLastNamePattern(String mLastNamePattern) {
        this.mLastNamePattern = mLastNamePattern;
    }

    /** Get the search pattern for last name */
    public String getLastNamePattern() {
        return mLastNamePattern;
    }
    
    /** Predicate, to detemine whether a last name pattern has been set */
    public boolean hasLastNamePattern() {
        return (this.getLastNamePattern() != null &&
                this.getLastNamePattern().length() > 0);
    }

    /** Set the search pattern for email */
    public void setEmailPattern(String mEmailPattern) {
        this.mEmailPattern = mEmailPattern;
    }

    /** Get the search pattern for email */
    public String getEmailPattern() {
        return mEmailPattern;
    }
    
    /** Predicate, to detemine whether an email pattern has been set */
    public boolean hasEmailPattern() {
        return (this.getEmailPattern() != null && 
                this.getEmailPattern().length() > 0);
    }
    
    /** Set the name of the page variable for the results */
    public void setVarName(String mVarName) {
        this.mVarName = mVarName;
    }

    /** Get the name of the page variable for the results */
    public String getVarName() {
        return mVarName;
    }

    /**
     *  Take the provided search parameters, translate them into a search
     * against the person database.  Populate a page variable with a 
     * collection of the people found, represented as Person beans.
     */
    public int doStartTag() {
        mLog.info("doStartTag invoked");
        // List of people found
        Person[] people = new Person[0];
        ArrayList pList = new ArrayList();
        String error = null;
        
        // Convert the tag's attributes into a set of SearchArgs
        SearchArg[] sargs = new SearchArg[3];
        sargs[0] = new SearchArg();
        sargs[0].setName(PersonDAO.FIRST_NAME);
        sargs[0].setValue(this.getFirstNamePattern());
        sargs[1] = new SearchArg();
        sargs[1].setName(PersonDAO.LAST_NAME);
        sargs[1].setValue(this.getLastNamePattern());
        sargs[2] = new SearchArg();
        sargs[2].setName(PersonDAO.EMAIL);
        sargs[2].setValue(this.getEmailPattern());
        
        // Get the PeopleFinder EJB home
        try {
            Context ctx = new InitialContext();
            Object ref = ctx.lookup("ejb/ant-PeopleFinderLocal");
            PeopleFinderLocalHome home = 
                (PeopleFinderLocalHome)PortableRemoteObject.narrow(ref, PeopleFinderLocalHome.class);
            PeopleFinderLocal finder = home.create();
            people = finder.findPeople(sargs);
            // Add the people to our list
            for (int i = 0; i < people.length; i++) {
                pList.add(people[i]);
            }
        }
        catch (NamingException ne) {
            error = "Failed to locate/access EJB component: " + ne.getMessage();
            mLog.severe(error);
        }
        catch (CreateException ce) {
            error = "Failed to create EJB component: " + ce.getMessage();
            mLog.severe(error);
        }
        catch (InvalidSearchException ise) {
            error = "Search parameters were invalid: " + ise.getMessage();
            mLog.severe(error);
        }
        catch (PersistenceException pe) {
            error = "Persistence error during search: " + pe.getMessage();
            mLog.severe(error);
        }
        
        // Set the page variable (if the search failed, it will be empty)
        this.pageContext.setAttribute(getVarName(), pList, 
                                      PageContext.SESSION_SCOPE);
        // Set the error page variable if there is an error.
        if (error != null) {
            this.pageContext.setAttribute(getVarName() + "-error", error,
                                          PageContext.REQUEST_SCOPE);
        }
        return Tag.SKIP_BODY;
    }
}
