/*
 * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
 *
 * The contents of this file are subject to the CCM Public
 * License (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the
 * License at http://www.redhat.com/licenses/ccmpl.html.
 *
 * Software distributed under the License is distributed on an
 * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
 * or implied. See the License for the specific language
 * governing rights and limitations under the License.
 *
 */
package com.arsdigita.cms.contenttypes.ui;

import com.arsdigita.bebop.ColumnPanel;
import com.arsdigita.bebop.FormData;
import com.arsdigita.bebop.FormSection;
import com.arsdigita.bebop.FormProcessException;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.SaveCancelSection;
import com.arsdigita.bebop.event.PrintEvent;
import com.arsdigita.bebop.event.PrintListener;
import com.arsdigita.bebop.event.FormInitListener;
import com.arsdigita.bebop.event.FormProcessListener;
import com.arsdigita.bebop.event.FormSectionEvent;
import com.arsdigita.bebop.event.FormValidationListener;
import com.arsdigita.bebop.event.FormSubmissionListener;
import com.arsdigita.bebop.form.Submit;
import com.arsdigita.bebop.form.Option;
import com.arsdigita.bebop.form.RadioGroup;
import com.arsdigita.bebop.form.TextArea;
import com.arsdigita.bebop.form.TextField;
import com.arsdigita.bebop.parameters.NotNullValidationListener;
import com.arsdigita.cms.ContentItem;
import com.arsdigita.cms.ItemSelectionModel;
import com.arsdigita.cms.contenttypes.Link;
import com.arsdigita.cms.ui.ItemSearchWidget;
import com.arsdigita.cms.util.GlobalizationUtil;
import com.arsdigita.util.Assert;
import com.arsdigita.util.UncheckedWrapperException;
import java.util.TooManyListenersException;
import java.net.MalformedURLException;
import java.net.URL;
import org.apache.log4j.Logger;

/**
 * Form to edit the basic properties of an Link. This form can be
 * extended to create forms for Link subclasses.
 * @version $Revision: #4 $ $Date: 2004/04/07 $
 * @author Nobuko Asakai (nasakai@redhat.com)
 */

public class LinkPropertyForm extends FormSection
    implements FormInitListener, FormProcessListener, FormValidationListener, FormSubmissionListener {
    private static final Logger s_log = Logger.getLogger(LinkPropertyForm.class);

    /** Name of this form */
    public static final String ID = "link_edit";
    public static final String SSL_PROTOCOL = "https://";
    public static final String HTTP_PROTOCOL = "http://";
    
    private TextArea m_description;
    private TextField m_title;
    private TextField m_targetURI;
    private RadioGroup m_linkType;
    private ItemSelectionModel m_itemModel;
    private LinkSelectionModel m_linkModel;
    private SaveCancelSection m_saveCancelSection;
    private ItemSearchWidget m_itemSearch;

    private final String ITEM_SEARCH ="contentItem";
    
    /**
     * Creates a new form to edit the Link object specified
     * by the item selection model passed in.
     * @param itemModel The ItemSelectionModel to use to obtain the 
     *    ContentItem to which this link is (or will be) attached
     * @param link The LinkSelectionModel to use to obtain the 
     *    Link to work on
     */
    public LinkPropertyForm( ItemSelectionModel itemModel,
                             LinkSelectionModel link ) {
        super(new ColumnPanel(2));
        s_log.debug("property form constructor");
        m_linkModel = link;
        m_itemModel = itemModel;

        addWidgets();
        addSaveCancelSection();

        addInitListener(this);

        addValidationListener(this);

        addProcessListener(this);
        addSubmissionListener(this);
    }

    /**
     * Adds widgets to the form.
     */
    protected void addWidgets() {
        m_title = new TextField("title");
        m_title.addValidationListener(new NotNullValidationListener());
        add(new Label(GlobalizationUtil.globalize("cms.contenttypes.ui.title")));
        add(m_title);

        m_description = new TextArea("description");
        m_description.setCols(40);
        m_description.setRows(5);
        add(new Label(GlobalizationUtil.
            globalize("cms.contenttypes.ui.description")));

        add(m_description);

        add(new Label(
                "<script language=\"javascript\">\n" +
                "<!-- \n" +
                "function toggle_link_fields(status) { \n" +
                "  document.forms['linkEditForm'].targetURI.disabled = status; \n" + 
                "  document.forms['linkEditForm'].contentItem.disabled = !status; \n" +
                "  document.forms['linkEditForm'].contentItem_search.disabled = !status; \n" +
                "  document.forms['linkEditForm'].contentItem_clear.disabled = !status; \n" +
                "}\n" +
                "// -->\n" +
                "</script>\n",
                false
            ));

        add(new Label( "Choose either an External Link or a Content Item", Label.BOLD), 
            ColumnPanel.FULL_WIDTH);
        m_linkType = new RadioGroup("linkType");
        Option m_external = new Option(Link.EXTERNAL_LINK, "External Link");
        m_external.setOnClick("toggle_link_fields(false)");

        Option m_internal = new Option(Link.INTERNAL_LINK, "Content Item");
        m_internal.setOnClick("toggle_link_fields(true)");

        m_linkType.addOption(m_external);
        m_linkType.addOption(m_internal);
        m_linkType.setOptionSelected(m_external);
        m_linkType.addValidationListener(new NotNullValidationListener());
        add(new Label("Link Type (Choose one):"));
        add(m_linkType);

        m_targetURI = new TextField("targetURI");
        m_targetURI.setOnFocus("toggle_link_fields(false)");
        m_targetURI.setHint("Enter a URL such as http://www.example.com/");
        add( new Label( "External URL: " ) );
        add( m_targetURI );

        add(new Label("Content Item:"));
        m_itemSearch = new ItemSearchWidget(ITEM_SEARCH);
        m_itemSearch.getSearchButton().setOnFocus("toggle_link_fields(true)");
        m_itemSearch.getClearButton().setOnFocus("toggle_link_fields(true)");
        add(m_itemSearch);

        add(new Label(
                "<script language=\"javascript\">\n" +
                "<!-- \n" +
                "if (document.forms['linkEditForm'].linkType[0].checked) { \n" +
                "  toggle_link_fields(false); \n" +
                "} else { \n" +
                "  toggle_link_fields(true); \n" +
                "} \n" +
                "// -->\n" +
                "</script>\n",
                false
            ));
    }

    /** Adds the saveCancelSection */
    public void addSaveCancelSection() {
        m_saveCancelSection = new SaveCancelSection();
        try {
            m_saveCancelSection.getCancelButton().addPrintListener(
                new PrintListener() {
                    public void prepare(PrintEvent e) {
                        Submit target = (Submit)e.getTarget();
                        if (m_linkModel.isSelected(e.getPageState())) {
                            target.setButtonLabel("Cancel");
                        } else {
                            target.setButtonLabel("Reset");
                        }
                    }
                }
            );
            m_saveCancelSection.getSaveButton().addPrintListener(
                new PrintListener() {
                    public void prepare(PrintEvent e) {
                        Submit target = (Submit)e.getTarget();
                        if (m_linkModel.isSelected(e.getPageState())) {
                            target.setButtonLabel("Save");
                        } else {
                            target.setButtonLabel("Create");
                        }
                    }
                }
            );
        } catch (TooManyListenersException e) {
            throw new UncheckedWrapperException("this cannot happen", e);
        }
        add(m_saveCancelSection, ColumnPanel.FULL_WIDTH);
    }

    /** Retrieves the saveCancelSection */
    public SaveCancelSection getSaveCancelSection() {
        return m_saveCancelSection;
    }
    
    /** return selection model for Link that we are dealing with. */
    protected LinkSelectionModel getLinkSelectionModel(){
      return m_linkModel;
    }

    /** 
     * Submission listener. Handles cancel events.
     *
     * @param e the FormSectionEvent
     */
    public void submitted(FormSectionEvent e) 
        throws FormProcessException {
        if (m_saveCancelSection.getCancelButton().isSelected(e.getPageState())) {
	    s_log.debug("cancel in submission listener");
            m_linkModel.clearSelection(e.getPageState());
            init(e);
            throw new FormProcessException("cancelled");
        }
    }

    /** 
     * Validation listener. Ensures consistency of internal vs. external link data
     *
     * @param event the FormSectionEvent
     */
    public void validate(FormSectionEvent event) 
        throws FormProcessException {
        PageState state = event.getPageState();
        FormData data = event.getFormData();

        if (Link.EXTERNAL_LINK.equals((String)m_linkType.getValue(state))) {
            // The link is external, the URL must be valid and not null
            String externalURI = (String)m_targetURI.getValue(state);
            if (externalURI == null || externalURI.length() == 0 ) {
                throw new FormProcessException("The URI field is required for an external link.");
            }
            
            try {
                URL url = new URL((String)m_targetURI.getValue(state));
            } catch (MalformedURLException ex ) {
		            try {
		                String targetURI = (String)m_targetURI.getValue(state);
		                boolean httpsLink = targetURI.startsWith(SSL_PROTOCOL);
		                boolean hasProtocol = targetURI.indexOf("://") != -1;

                    String newURI;
		                if (httpsLink) {
			                  newURI = HTTP_PROTOCOL + targetURI.substring(SSL_PROTOCOL.length());
		                } else if (hasProtocol){
			                  newURI = targetURI;
		                } else {
			                  newURI = HTTP_PROTOCOL + targetURI;
		                }
        		        URL url = new URL(newURI);
		                if (!httpsLink) {
			                   m_targetURI.setValue(state, newURI);
		                }
		            } catch (MalformedURLException ex2 ) {
			              throw new FormProcessException("The URL is not valid: " 
						            + ex.getMessage() + ". \n" 
						            + "Valid URLs must begin with \"http://\" .");
		            }
	          }
	      } else if (Link.INTERNAL_LINK.equals((String)m_linkType.getValue(state))) {
            // The link is internal, the item selected must be not null
            if (data.get(ITEM_SEARCH) == null) {
                throw new FormProcessException("Item selection is required for internal link.");
            }
        }
    }

    /** 
     * Get the current ContentItem 
     *
     * @param s the PageState
     * @return the ContentItem
     */
    protected ContentItem getContentItem(PageState s) {
        return (ContentItem)m_itemModel.getSelectedObject(s);
    }

    /** 
     * Take care of basic Link creation steps
     *
     * @param s the PageState
     * @return the newly-created Link
     */
    protected Link createLink(PageState s) {
        ContentItem item = getContentItem(s);
        Assert.exists(item);
        Link link = new Link();
        return link;
    }

    
    /** 
     * Init listener. For edit actions, fills the form with current data
     *
     * @param fse the FormSectionEvent
     */
    public void init( FormSectionEvent fse ) throws FormProcessException {
        FormData data = fse.getFormData();
        PageState state = fse.getPageState();
        s_log.debug("Init");
        setVisible(state, true);
        Link link;
        if ( m_linkModel.isSelected(state)) {
            s_log.debug("Edit");
            link = m_linkModel.getSelectedLink(state);
            try {
                m_title.setValue(state, link.getTitle());
                m_description.setValue(state, link.getDescription());
                m_targetURI.setValue(state, link.getTargetURI());
                m_linkType.setValue(state, link.getTargetType());
                if (Link.INTERNAL_LINK.equals(link.getTargetType())) {
                    data.put(ITEM_SEARCH, link.getTargetItem());
                }

            } catch (IllegalStateException e ) {
                s_log.error(e.getMessage());
                throw e;
            }
        } else {
            // new link do nothing
            s_log.debug("new link");
            m_title.setValue(state, null);
            m_description.setValue(state, null);
            m_targetURI.setValue(state, null);
            m_linkType.setValue(state, Link.EXTERNAL_LINK);
            data.put(ITEM_SEARCH, null);
        }
    }
    

    /** 
     * Process listener. Saves/creates the new or modified Link
     *
     * @param fse the FormSectionEvent
     */
    public void process( FormSectionEvent fse ) throws FormProcessException {
        PageState state = fse.getPageState();
        Link link;

        // save only if save button was pressed
        if ( getSaveCancelSection().getCancelButton().isSelected(state) ) {
            // cancel button is selected
            m_linkModel.clearSelection(state);
	          s_log.debug("link save canceled");

        } else  {

            if (m_linkModel.isSelected(state)) {
                // Editing a link
                s_log.debug("processing link edit");
                link = m_linkModel.getSelectedLink(state);
            } else {
                s_log.debug("processing new link");
                link = createLink(state);
            } 

            //call to set various properties of Link.
            setLinkProperties(link , fse);
             s_log.debug("Created Link with ID: " + link.getOID().toString() 
                        + "Title " + link.getTitle());
        }
        // XXX Initialize the form
        m_linkModel.clearSelection(state);
        init(fse);
    }
    
    /**
     * Set various properties of the Link.Child clases can over-ride this
     * method to add additional properties to Link. 
     */
    protected void setLinkProperties(Link link , FormSectionEvent fse){
      PageState state = fse.getPageState();
      FormData data = fse.getFormData();
      // * Set required properties *
      link.setTitle((String)m_title.getValue(state));
      link.setDescription( (String)
                           m_description.getValue(state));
      link.setTargetType((String)m_linkType.getValue(state));

      // Process internal and external urls
      if (Link.EXTERNAL_LINK.equals(m_linkType.getValue(state))) {
          link.setTargetURI( 
              (String) m_targetURI.getValue(state));
          link.setTargetItem(null);
      } else {
          // Internal
          link.setTargetURI(null);
          link.setTargetItem((ContentItem) data.get(ITEM_SEARCH));
      }
      link.save();
    }
}
