/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
 */

/* $Id$ */

package org.apache.fop.layoutmgr;

import java.util.List;

import org.apache.fop.fo.Constants;

/**
 * This class represents an unresolved break possibility.
 */
public class BreakElement extends UnresolvedListElement {

    private int penaltyWidth;
    private int penaltyValue;
    private int breakClass;
    private List<ListElement> pendingBeforeMarks;
    private List<ListElement> pendingAfterMarks;

    /**
     * Main constructor
     * @param position the Position instance needed by the addAreas stage of the LMs.
     * @param penaltyValue the penalty value for the penalty element to be constructed
     * @param context the layout context which contains the pending conditional elements
     */
    public BreakElement(Position position, int penaltyValue, LayoutContext context) {
        this(position, penaltyValue, Constants.EN_AUTO, context);
    }

    /**
     * Create a new BreakElement for the given {@code position}, {@code penaltyValue}
     * and {@code breakClass}. (Used principally to generate break-possibilities in
     * ranges of content that must be kept together within the context corresponding
     * to the {@code breakClass}; expected to be one of
     *   {@link org.apache.fop.fo.Constants#EN_AUTO},
     *   {@link org.apache.fop.fo.Constants#EN_LINE},
     *   {@link org.apache.fop.fo.Constants#EN_COLUMN} or
     *   {@link org.apache.fop.fo.Constants#EN_PAGE})
     * @param position  the corresponding {@link Position}
     * @param penaltyValue  the penalty value
     * @param breakClass    the break class
     * @param context       the {@link LayoutContext}
     */
    public BreakElement(Position position, int penaltyValue, int breakClass,
                        LayoutContext context) {
        this(position, 0, penaltyValue, breakClass, context);
    }

    /**
     * Constructor for hard breaks.
     *
     * @param position the Position instance needed by the addAreas stage of the LMs.
     * @param penaltyWidth the penalty width
     * @param penaltyValue the penalty value for the penalty element to be constructed
     * @param breakClass the break class of this penalty (one of
     *   {@link org.apache.fop.fo.Constants#EN_AUTO},
     *   {@link org.apache.fop.fo.Constants#EN_COLUMN},
     *   {@link org.apache.fop.fo.Constants#EN_PAGE},
     *   {@link org.apache.fop.fo.Constants#EN_EVEN_PAGE},
     *   {@link org.apache.fop.fo.Constants#EN_ODD_PAGE})
     * @param context the layout context which contains the pending conditional elements
     */
    public BreakElement(Position position, int penaltyWidth, int penaltyValue,
                int breakClass, LayoutContext context) {
        super(position);
        this.penaltyWidth = penaltyWidth;
        this.penaltyValue = penaltyValue;
        setBreakClass(breakClass);
        this.pendingBeforeMarks = context.getPendingBeforeMarks();
        this.pendingAfterMarks = context.getPendingAfterMarks();
    }

    private static String getBreakClassName(int breakClass) {
        return AbstractBreaker.getBreakClassName(breakClass);
    }

    /** {@inheritDoc} */
    public boolean isConditional() {
        return false; //Does not really apply here
    }

    /** {@inheritDoc} */
    /*
    public boolean isPenalty() {
        return true; //not entirely true but a BreakElement will generate a penalty later
    }*/

    /** @return the penalty width */
    public int getPenaltyWidth() {
        return this.penaltyWidth;
    }

    /** @return the penalty value */
    public int getPenaltyValue() {
        return this.penaltyValue;
    }

    /**
     * Sets the penalty value.
     * @param p the new penalty value
     */
    public void setPenaltyValue(int p) {
        this.penaltyValue = p;
    }

    /** {@inheritDoc} */
    public boolean isForcedBreak() {
        return penaltyValue == -KnuthElement.INFINITE;
    }

    /**
     * Returns the break class of this penalty.
     *
     * @return one of
     *   {@link org.apache.fop.fo.Constants#EN_AUTO},
     *   {@link org.apache.fop.fo.Constants#EN_COLUMN},
     *   {@link org.apache.fop.fo.Constants#EN_PAGE},
     *   {@link org.apache.fop.fo.Constants#EN_EVEN_PAGE},
     *   {@link org.apache.fop.fo.Constants#EN_ODD_PAGE}.
     */
    public int getBreakClass() {
        return breakClass;
    }

    /**
     * Sets the break class.
     *
     * @param breakClass one of
     *   {@link org.apache.fop.fo.Constants#EN_AUTO},
     *   {@link org.apache.fop.fo.Constants#EN_LINE},
     *   {@link org.apache.fop.fo.Constants#EN_COLUMN},
     *   {@link org.apache.fop.fo.Constants#EN_PAGE},
     *   {@link org.apache.fop.fo.Constants#EN_EVEN_PAGE},
     *   {@link org.apache.fop.fo.Constants#EN_ODD_PAGE}.
     */
    public void setBreakClass(int breakClass) {
        switch (breakClass) {
        case Constants.EN_AUTO:
        case Constants.EN_LINE:
        case Constants.EN_COLUMN:
        case Constants.EN_PAGE:
        case Constants.EN_EVEN_PAGE:
        case Constants.EN_ODD_PAGE:
            this.breakClass = breakClass;
            break;
        default: throw new IllegalArgumentException("Illegal value for break class: " + breakClass);
        }
    }

    /** @return the pending border and padding elements at the before edge */
    public List<ListElement> getPendingBeforeMarks() {
        return this.pendingBeforeMarks;
    }

    /** @return the pending border and padding elements at the after edge */
    public List<ListElement> getPendingAfterMarks() {
        return this.pendingAfterMarks;
    }

    /**
     * Clears all pending marks associated with this break element. This is used in break
     * cases where we only know very late if the break is actually after all the content
     * of an FO has been generated.
     */
    public void clearPendingMarks() {
        this.pendingBeforeMarks = null;
        this.pendingAfterMarks = null;
    }

    /** {@inheritDoc} */
    public String toString() {
        StringBuffer sb = new StringBuffer(64);
        sb.append("BreakPossibility[p:");
        sb.append(KnuthPenalty.valueOf(this.penaltyValue));
        if (isForcedBreak()) {
            sb.append(" (forced break, ")
                    .append(getBreakClassName(this.breakClass))
                    .append(")");
        } else if (this.penaltyValue >= 0 && this.breakClass != -1) {
            sb.append(" (keep constraint, ")
                    .append(getBreakClassName(this.breakClass))
                    .append(")");
        }
        sb.append("; w:");
        sb.append(penaltyWidth);
        sb.append("]");
        return sb.toString();
    }

}
