/*
 * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
 *
 * Copyright @ 2015 Atlassian Pty Ltd
 *
 * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.java.sip.communicator.plugin.desktoputil;

import java.awt.*;

import javax.swing.*;
import javax.swing.text.*;
import javax.swing.text.html.*;
import javax.swing.text.html.ParagraphView;

/**
 * The <tt>SIPCommHTMLEditorKit</tt> is an <tt>HTMLEditorKit</tt> which uses
 * an extended <tt>ParagraphView</tt>.
 *
 * @author Yana Stamcheva
 */
public class SIPCommHTMLEditorKit extends HTMLEditorKit
{
    /**
     * Serial version UID.
     */
    private static final long serialVersionUID = 0L;


    private final JComponent container;

    /**
     * Creates an instance of <tt>SIPCommHTMLEditorKit</tt> by specifying the
     * container, where the editor kit would be used.
     *
     * @param container
     */
    public SIPCommHTMLEditorKit(JComponent container)
    {
        this.container = container;
    }

    /**
     * Returns the extended <tt>HTMLFactory</tt> defined here.
     *
     * @return the extended view factory
     */
    @Override
    public ViewFactory getViewFactory()
    {
        return new HTMLFactoryX();
    }

    /**
     * An extended <tt>HTMLFactory</tt> that uses the <tt>SIPCommImageView</tt>
     * to represent images and the <tt>ParagraphViewX</tt> to represent
     * paragraphs.
     */
    private class HTMLFactoryX extends HTMLFactory
        implements ViewFactory
    {
        @Override
        public View create(Element elem)
        {
            View view = super.create(elem);

            viewCreated(this, view);

            if (view instanceof ParagraphView)
            {
                return new ParagraphViewX(elem);
            }
            else if (view instanceof ComponentView)
            {
                return new MyComponentView(elem);
            }

            return view;
        }
    }

    /**
     * Inform extenders for the view creation.
     * @param view the newly created view.
     */
    protected void viewCreated(ViewFactory factory, View view){}

    /**
     * An extended component view, which provides horizontal and vertical
     * filling.
     */
    private class MyComponentView extends ComponentView
    {
        /**
         * Creates a new ComponentView object.
         *
         * @param elem the element to decorate
         */
        public MyComponentView(Element elem)
        {
            super(elem);
        }

        /**
         * Determines the preferred span for this view along an
         * axis.  This is implemented to return the value
         * returned by Component.getPreferredSize along the
         * axis of interest.
         *
         * @param axis may be either View.X_AXIS or View.Y_AXIS
         * @return   the span the view would like to be rendered into >= 0.
         *           Typically the view is told to render into the span
         *           that is returned, although there is no guarantee.
         *           The parent may choose to resize or break the view.
         * @exception IllegalArgumentException for an invalid axis
         */
        @Override
        public float getPreferredSpan(int axis)
        {
            if ((axis != X_AXIS) && (axis != Y_AXIS))
            {
                throw new IllegalArgumentException("Invalid axis: " + axis);
            }
            if (getComponent() != null)
            {
                Dimension size = getComponent().getPreferredSize();
                if (axis == View.X_AXIS)
                {
                    return container.getWidth();
                }
                else
                {
                    return size.height;
                }
            }
            return 0;
        }

        /**
         * Determines the maximum span for this view along an
         * axis.  This is implemented to return the value
         * returned by Component.getMaximumSize along the
         * axis of interest.
         *
         * @param axis may be either View.X_AXIS or View.Y_AXIS
         * @return   the span the view would like to be rendered into >= 0.
         *           Typically the view is told to render into the span
         *           that is returned, although there is no guarantee.
         *           The parent may choose to resize or break the view.
         * @exception IllegalArgumentException for an invalid axis
         */
        @Override
        public float getMaximumSpan(int axis)
        {
            if ((axis != X_AXIS) && (axis != Y_AXIS))
            {
                throw new IllegalArgumentException("Invalid axis: " + axis);
            }
            if (getComponent() != null)
            {
                Dimension size = getComponent().getMaximumSize();
                if (axis == View.X_AXIS)
                {
                    return container.getWidth();
                }
                else
                {
                    return size.height;
                }
            }
            return 0;
        }
    }

    /**
     * The <tt>ParagraphViewX</tt> is created in order to solve the following
     * problem (Bug ID: 4855207):
     * <p>
     * When a paragraph in a JTextPane has a large amount of text the
     * processing needed to layout the entire paragraph increases as the
     * paragraph grows.
     */
    static class ParagraphViewX extends ParagraphView
    {
        /**
         * Creates an instance of <tt>ParagraphViewX</tt>.
         *
         * @param elem the element that this view is responsible for
         */
        public ParagraphViewX(Element elem)
        {
            super(elem);
        }

        /**
         * Calculate requirements along the minor axis.  This
         * is implemented to forward the request to the logical
         * view by calling getMinimumSpan, getPreferredSpan, and
         * getMaximumSpan on it.
         *
         * @param axis the axis, for which we calculate size requirements
         * @param sizeRequirements the initial size requirements
         * @return the recalculated size requirements for the given axis
         */
        @Override
        protected SizeRequirements calculateMinorAxisRequirements (
                int axis, SizeRequirements sizeRequirements)
        {
            if (sizeRequirements == null)
            {
                sizeRequirements = new SizeRequirements();
            }

            float pref = layoutPool.getPreferredSpan(axis);
            float min = layoutPool.getMinimumSpan(axis);

            // Don't include insets, Box.getXXXSpan will include them.
            sizeRequirements.minimum = (int)min;
            sizeRequirements.preferred
                = Math.max(sizeRequirements.minimum, (int) pref);
            sizeRequirements.maximum = Short.MAX_VALUE;
            sizeRequirements.alignment = 0.5f;
            return sizeRequirements;
        }
    }
}