/* ======================================================================
 The Bodington System Software License, Version 1.0
 
 Copyright (c) 2001 The University of Leeds.  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 acknowledgement:  "This product includes
 software developed by the University of Leeds
 (http://www.bodington.org/)."  Alternately, this acknowledgement may
 appear in the software itself, if and wherever such third-party
 acknowledgements normally appear.
 
 4.  The names "Bodington", "Nathan Bodington", "Bodington System",
 "Bodington Open Source Project", and "The University of Leeds" must not be
 used to endorse or promote products derived from this software without
 prior written permission. For written permission, please contact
 d.gardner@leeds.ac.uk.
 
 5.  The name "Bodington" may not appear in the name of products derived
 from this software without prior written permission of the University of
 Leeds.
 
 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 WARRANTIES, INCLUDING, BUT NOT LIMITED TO,  TITLE,  THE IMPLIED WARRANTIES
 OF QUALITY  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
 EVENT SHALL THE UNIVERSITY OF LEEDS 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 was originally created by the University of Leeds and may contain voluntary
 contributions from others.  For more information on the Bodington Open Source Project, please
 see http://bodington.org/
 
 ====================================================================== */

package org.bodington.servlet.facilities;

import org.bodington.database.PrimaryKey;
import org.bodington.servlet.*;
import org.bodington.server.*;
import org.bodington.server.resources.*;
import org.bodington.servlet.template.Template;
import org.bodington.server.realm.Permission;
import org.bodington.util.BodingtonURL;
import org.bodington.util.TextUtils;
import org.bodington.util.html.HtmlFilterFactory;

import org.apache.log4j.Logger;

import java.io.*;
import javax.servlet.ServletException;


/**
 * Class that encapsulates the web-based user interface to a quicklink. The
 * session type associated with this class is {@link QuickLinkSession}. The
 * type of resource associated with this class is {@link QuickLink}.
 * @author Antony Corfield
 * @see QuickLink
 * @see QuickLinkSession
 */
public class QuickLinkFacility extends Facility
{
    private static Logger log = Logger.getLogger( QuickLinkFacility.class );

    public boolean canCopy( Resource resource )
    {
        return true;
    }
    
    public boolean willAcceptFacility( Facility f )
    {
        return false;
    }

    public Resource newResource()
    {
        return new QuickLink();
    }

    public boolean initResource( Request breq, Resource new_resource )
        throws Exception
    {
        QuickLink link;

        if ( !(new_resource instanceof QuickLink) )
            throw new Exception(
                "Technical problem: An incorrect type of resource was created." );

        String url = "";
        link = (QuickLink)new_resource;

        if ( breq.getParameter( "link" ) != null )
            url = breq.getParameter( "link" );

        QuickLinkFacility.setLink( link, url, breq );

        String newWindow = breq.getParameter( "new_window" );
        if ( newWindow != null && newWindow.equalsIgnoreCase( "on" ) )// "true"??
            link.setNewWindow( true );
        else
            link.setNewWindow( false );

        return true;
    }

    public boolean initResource( Resource original_resource,
        Resource new_resource ) throws BuildingServerException
    {
        if ( !(new_resource instanceof QuickLink) )
            throw new BuildingServerException(
                "Error: The wrong type of resource was created." );

        QuickLink original = (QuickLink)original_resource;
        QuickLink new_link = (QuickLink)new_resource;
        new_link.setURL( original.getURL() );
        new_link.setResourceIdLink( original.getResourceIdLink() );
        new_link.setNewWindow( original.isNewWindow() );

        return true;
    }

    public boolean createCheck( Request breq, PrintWriter out )
    {
        String url = "";
        if ( breq.getParameter( "link" ) != null )
            url = breq.getParameter( "link" ).trim();
        if ( url.length() == 0 )
        {
            out.print( "Must supply a url" );
            return false;
        }
        if ( ! HtmlFilterFactory.isGoodProtocol(url))
        {
          out.print( "The Protocol isn't valid.");
          return false;
        }
        return true;
    }

    public void insert( Request req, PrintWriter out, String command,
        String insertname ) throws ServletException, IOException
    {
        log.debug( " QuickLinkFacility insert()" );

        try
        {
            BuildingSession session;
            QuickLinkSession link_session;
            QuickLink link;

            session = BuildingSessionManagerImpl.getSession( req.getResource() );
            if ( !(session instanceof QuickLinkSession) )
            {
                out.println( "<HR>Technical problem: unable to access "
                    + "appropriate tool session.<HR>" );
                return;
            }
            link_session = (QuickLinkSession)session;
            link = link_session.getQuickLink();

            if ( command.equalsIgnoreCase( "linkmodify" ) )
            {
                linkmodify( req, out, link );
                return;
            }

            if ( command.equalsIgnoreCase( "linkfield" ) )
            {
                linkfield( req, out, link, insertname );
                return;
            }

        }
        catch ( BuildingServerException bsex )
        {
            out.println( bsex.toString() );
            return;
        }

        super.insert( req, out, command, insertname );
    }

    private void linkfield( Request req, PrintWriter out, QuickLink link,
        String name ) throws IOException
    {

        if ( name.equalsIgnoreCase( "url" ) )
        {
            String url = QuickLinkFacility.getLink( link, req, false );
            if ( url == null || url.length() == 0 )
            {
                out.println( "No URL has been specified for this QuickLink!" );
                return;
            }
            out.print( "<INPUT SIZE=\"32\" NAME=\"link\" VALUE=\"" );
            out.print( url );
            out.println( "\">" );
        }

        if ( name.equalsIgnoreCase( "window" ) )
        {
            out.print( "<INPUT TYPE=\"CHECKBOX\" NAME=\"new_window\" " );
            if ( link.isNewWindow() ) 
                out.print( "CHECKED=\"true\"" );

            out.println( " />" );
        }
 
        return;
    }

    private void linkmodify( Request req, PrintWriter out, QuickLink link )
        throws IOException, BuildingServerException
    {
        String url;
        boolean isBodURL;
        url = req.getParameter( "link" );
        if ( url == null || (url = url.trim()).length() == 0 )
        {
            out.println( "<HR>You must specify the URL<HR>" );
            return;
        }
        
        if ( ! HtmlFilterFactory.isGoodProtocol(url))
        {
          out.print( "<HR>The Protocol isn't valid.<HR>");
          return;
        }

        isBodURL = QuickLinkFacility.setLink( link, url, req );

        String newWindow = req.getParameter( "new_window" );
        if ( newWindow != null && newWindow.equalsIgnoreCase( "on" ) )
            link.setNewWindow( true );
        else
            link.setNewWindow( false );

        link.save();

        out.print( "<CENTER><P>The requested changes were stored.</P></CENTER>" );
        if ( isBodURL )
            out.print( "<P>This QuickLink points to a resource within this "
                + "site." );
        else
            out.print( "<P>This QuickLink points to an external website." );
        // TODO doesn't work with relative links:
        out.print( " You can test the link by clicking on the URL below (this "
            + "opens in a new window for test purposes only)"
                + "<BR><A HREF=\"" + url + "\" TARGET=_new>" + url + "</P>" );
        return;
    }

    protected void resourceMenuItem( Resource resource, Request breq,
        PrintWriter out, ResourceMenuOutputState state, int depth, int highlight )
        throws IOException, BuildingServerException
    {
        Facility facility;
        Template template;
        String href = null, icon_src = null, title = null, target = null, content;
        String link, link_target;

        boolean manage = false;
        boolean deny = false;

        link = QuickLinkFacility.getLink( (QuickLink)resource, breq, false );
        if ( ((QuickLink)resource).isNewWindow() )
            link_target = "_blank";
        else
            link_target = "_top";

        manage = resource.checkPermission( Permission.MANAGE );
        href = (!state.rootless && depth == 1) ? null : (breq.getContextPath()
            + breq.getServletPath() + resource.getFullName());
        target = "_top";
        // using getTemplateGifUrl doesn't work for descendant resources - need
        // to
        // reference proper resource.
        facility = state.fl.get( new Integer( resource.getHttpFacilityNo() ) );
        if ( facility != null )
        {
            template = Template.get( facility.facilityname, 
                resource.getImplicitHttpUIStyle(), resource.getResourceId(),
                state.big_icons ? "icon.gif" : "iconsmall.gif" );
            if ( template != null )
                icon_src = breq.getContextPath() + state.gif_url
                    + template.getUrl() + state.colour_code;
        }
        title = resource.getTitle();
        if ( !resource.checkPermission( Permission.VIEW ) )
        {
            // logged in but no access - don't give link
            href = null;
            deny = true;
        }

        // heading level for title line matches depth but there are only
        // six heading levels in HTML so stop there.
        int hlevel = (depth > 6) ? 6 : depth;
        int level = ((depth - 1) % 10) + 1;

        // title in heading with appropriate class attribute
        out.print( "          <h" );
        out.print( hlevel );

        out.print( " class=\"" + state.css_class_prefix + "_node_title_lev" );
        out.print( level );
        out.print( "\">" );

        // optional link around whole title (inc. icon)
        // changed to 2 separate links for icon and text
        // (and a third for the manage link)
        if ( link != null && !deny )
        {
            out.print( "<a" ); // link for icon
            if ( link_target != null )
            {
                out.print( " target=\"" );
                out.print( link_target );
                out.print( "\"" );
            }
            out.print( " href=\"" );
            out.print( link );
            out.print( "\">" );
        }
        // <altAndTitle> --->>>
        out.print( "<img alt=\"Icon for "
            + (TextUtils.beginsWithVowel( resource.getResourceTypeName() ) 
            ? "an" : "a") + " " + resource.getResourceTypeName() + ".\" src=\"" );
        // <<<--- <altAndTitle>
        out.print( icon_src );
        out.print( "\" " );

        if ( depth == 1 )
            out.print( "class=\"" + state.css_class_prefix
                + "_node_icon_without_stalk\" />" );
        else
            out.print( "class=\"" + state.css_class_prefix
                + "_node_icon_no_expander\" />" );

        if ( link != null && !deny ) out.print( "</a>" ); // closing tag for
                                                            // icon link

        // span is used to get everything on one line in browsers that
        // lack CSS but CSS redfines as block.
        out.print( "<span class=\"" + state.css_class_prefix + "_node_content" );
        if ( depth == 1 ) out.print( "_without_stalk" );
        out.print( "_lev" );
        out.print( level );
        out.print( "_hl" );
        out.print( highlight );
        out.print( "\">" );

        if ( link != null && !deny )
        {
            out.print( "<a" ); // tag for text link
            if ( link_target != null )
            {
                out.print( " target=\"" );
                out.print( link_target );
                out.print( "\"" );
            }
            out.print( " href=\"" );
            out.print( link );
            out.print( "\">" );
        }

        if ( title != null )
            out.print( title );
        else
            out.print( "{untitled item}" );

        if ( link != null && !deny ) out.print( "</a>" ); // closing tag for
                                                            // text link

        if ( href != null && manage )
        {
            out.print( "&nbsp;<a" ); // tag for manage link
            if ( target != null )
            {
                out.print( " target=\"" );
                out.print( target );
                out.print( "\"" );
            }
            out.print( " href=\"" );
            out.print( href );
            // Closing tag for manage link.
            out.print( "\" title=\"Manage QuickLink.\"><span style=\"font-size: x-small\"><sup>Manage</sup></span></a>" ); 
        }

        out.print( "</span>" );

        out.print( "</h" ); // end of node_title
        out.print( hlevel );
        out.println( ">" );

        if ( !state.rootless && depth == 1 )// TODO ??
            content = resource.getIntroduction();
        else
            content = resource.getDescription();

        if ( deny )
        {
            content = "<em>"
                + "You are not included on the access list for this item.</em>"
                + " <br />";
            /* TODO: show description? */
        }

        content = (content == null) ? "" : content;

        out.print( "<div class=\"" + state.css_class_prefix + "_node_content" );
        if ( depth == 1 ) out.print( "_without_stalk" );
        out.print( "_lev" );
        out.print( level );
        out.print( "_hl" );
        out.print( highlight );
        out.print( "\">" );

        out.print( content );

        String markup = timedResourceText( resource );
        if ( markup != null ) out.print( "<div>" + markup + "</div>" );

        out.print( "</div>" );
        out.print( "<div class=\"clearer\">&nbsp;</div>\n" );
    }

    /**
     * Associate the quicklink with the specified URL. This method tries to
     * ascertain whether or not this link is an internal one.
     * @param quickLink the quicklink object we are trying to set.
     * @param url the URL that is the target of the quicklink.
     * @param request the request object.
     * @return <code>true</code> if the <code>link</code> parameter refers
     *         to an internal resource, otherwise <code>false</code>.
     * @see QuickLink#isBodURL()
     */
    public static boolean setLink( QuickLink quickLink, String url,
        Request request ) throws BuildingServerException
    {
        PrimaryKey id;
        Resource res;
        UploadedFile ulf;
        BodingtonURL bodUrl = new BodingtonURL( request );

        if ( (res = bodUrl.getResource( url )) != null )
        {
            quickLink.setResourceIdLink( res.getResourceId() );
            quickLink.setURL( null );
            return true;
        }

        if ( (ulf = bodUrl.getUploadedFile( url )) != null )
        {
            quickLink.setResourceIdLink( ulf.getPrimaryKey() );
            quickLink.setURL( null );
            return true;
        }

        quickLink.setURL( url );
        quickLink.setResourceIdLink( null );
        return false;
    }

    /**
     * Get the URL associated with the specified quicklink. If the URL can not
     * be calculated, then an empty string is returned.
     * @param quickLink The QuickLink we are trying to build the URL for.
     * @param request The Request object that making this call.
     * @param check if this is <code>true</code> then we check that the
     *        desination of this link hasn't been deleted.
     * @return the URL associated with the specified quicklink.
     */
    public static String getLink( QuickLink quickLink, Request request,
        boolean check )
    {
        Resource res;
        UploadedFile ulf;
        String description, link = null;
        String msg = " (The resource this links to may have been deleted)";
        int i;
        BodingtonURL bodUrl = new BodingtonURL( request );

        if ( quickLink.getResourceIdLink() == null ) 
            return quickLink.getURL();

        // check == true; returns null if resource has been deleted
        link = bodUrl.getResourceUrl( quickLink.getResourceIdLink(), check );
        if ( link == null )

            // check == true; returns null if UploadedFile has been deleted
            link = bodUrl.getUploadedFileUrl( quickLink.getResourceIdLink(),
                check );
        try
        {
            description = quickLink.getDescription();
            if ( link == null )
            {
                i = description.indexOf( msg );
                if ( i < 0 )
                    quickLink.setDescription( quickLink.getDescription() + msg );
                return "";
            }
            i = description.indexOf( msg );
            if ( i > -1 )
            {
                description = description.substring( 0, i );
                quickLink.setDescription( description );
            }
            return link;
        }
        catch ( BuildingServerException ex )
        {
            return "";
        }
    }
}