/*
 * 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.area.inline;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.fop.area.Area;

/**
 * Inline parent area.
 * This is an inline area that can have other inlines as children.
 */
public class InlineParent extends InlineArea {

    private static final long serialVersionUID = -3047168298770354813L;

    /**
     * The list of inline areas added to this inline parent.
     */
    protected List<InlineArea> inlines = new ArrayList<InlineArea>();

    /** Controls whether the IPD is automatically adjusted based on the area's children. */
    protected transient boolean autoSize;

    /** The offset of the beforest child area of this area. */
    protected int minChildOffset;

    /**
     * The offset of the afterest child area of this area. Offset from the
     * before-edge of this area's content-rectangle and the after-edge of the child area's
     * allocation-rectangle.
     */
    private int maxAfterEdge;

    private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
        ois.defaultReadObject();
    }

    @Override
    public void addChildArea(Area c) {
        assert c instanceof InlineArea;
        if (inlines.size() == 0) {
            autoSize = (getIPD() == 0);
        }
        InlineArea childArea = (InlineArea) c;
        inlines.add(childArea);
        // set the parent area for the child area
        childArea.setParentArea(this);
        if (autoSize) {
            increaseIPD(childArea.getAllocIPD());
        }
        updateLevel(childArea.getBidiLevel());
        int childOffset = childArea.getVirtualOffset();

        // do not offset if the childArea comes from a footnote
        // or if the parent itself comes from a footnote
        if (!(childArea.isFromFootnote() || isFromFootnote())) {
            minChildOffset = Math.min(minChildOffset, childOffset);
            maxAfterEdge = Math.max(maxAfterEdge, childOffset + childArea.getVirtualBPD());
        } else {
            minChildOffset = Math.min(minChildOffset, childArea.getBlockProgressionOffset());
            maxAfterEdge = minChildOffset + childArea.getVirtualBPD();
        }
    }

    @Override
    int getVirtualOffset() {
        return getBlockProgressionOffset() + minChildOffset;
    }

    @Override
    int getVirtualBPD() {
        return maxAfterEdge - minChildOffset;
    }

    /**
     * Get the child areas for this inline parent.
     *
     * @return the list of child areas
     */
    public List<InlineArea> getChildAreas() {
        return inlines;
    }

    /**
     * recursively apply the variation factor to all descendant areas
     * @param variationFactor the variation factor that must be applied to adjustments
     * @param lineStretch     the total stretch of the line
     * @param lineShrink      the total shrink of the line
     * @return true if there is an UnresolvedArea descendant
     */
    @Override
    public boolean applyVariationFactor(double variationFactor,
                                        int lineStretch, int lineShrink) {
        boolean hasUnresolvedAreas = false;
        int cumulativeIPD = 0;
        // recursively apply variation factor to descendant areas
        for (InlineArea inline : inlines) {
            hasUnresolvedAreas |= inline.applyVariationFactor(
                    variationFactor, lineStretch, lineShrink);
            cumulativeIPD += inline.getIPD();  //Update this area's IPD based on changes to children
        }
        setIPD(cumulativeIPD);

        return hasUnresolvedAreas;
    }

    @Override
    public List collectInlineRuns(List runs) {
        for (InlineArea ia : getChildAreas()) {
            runs = ia.collectInlineRuns(runs);
        }
        return runs;
    }

    /**
     * Reset bidirectionality level of all children to default (-1),
     * signalling that they will inherit the level of their parent text area.
     */
    public void resetChildrenLevel() {
        for (InlineArea inline : inlines) {
            (inline).resetBidiLevel();
        }
    }

    private void updateLevel(int newLevel) {
        if (newLevel >= 0) {
            int curLevel = getBidiLevel();
            if (curLevel >= 0) {
                if (newLevel < curLevel) {
                    setBidiLevel(newLevel);
                }
            } else {
                setBidiLevel(newLevel);
            }
        }
    }

    @Override
    public void setFromFootnote(boolean fromFootnote) {
        super.setFromFootnote(fromFootnote);

        // set all the children to avoid offsetting any of them
        // otherwise we would offset the parent by offsetting the child
        for (InlineArea area : inlines) {
            area.setFromFootnote(fromFootnote);
        }
    }
}
