/*
 * 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.
 */

package org.apache.fop.layoutmgr;

import java.util.LinkedList;
import java.util.List;

import org.apache.fop.area.Area;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.flow.MultiSwitch;

public class MultiSwitchLayoutManager extends BlockStackingLayoutManager {

    static class WhitespaceManagementPosition extends Position {

        private List<ListElement> knuthList;

        public WhitespaceManagementPosition(LayoutManager lm) {
            super(lm);
        }

        public List<Position> getPositionList() {
            List<Position> positions = new LinkedList<Position>();
            if (knuthList != null && !knuthList.isEmpty()) {
                SpaceResolver.performConditionalsNotification(knuthList, 0, knuthList.size() - 1, -1);
                for (ListElement el : knuthList) {
                    if (el.getPosition() != null) {
                        positions.add(el.getPosition());
                    }
                }
            }
            return positions;
        }

        public void setKnuthList(List<ListElement> knuthList) {
            this.knuthList = knuthList;
        }

        public List<ListElement> getKnuthList() {
            return knuthList;
        }

    }

    private interface KnuthElementsGenerator {
        List<ListElement> getKnuthElements(LayoutContext context, int alignment);
    }

    private class DefaultKnuthListGenerator implements KnuthElementsGenerator {

        public List<ListElement> getKnuthElements(LayoutContext context, int alignment) {
            List<ListElement> knuthList = new LinkedList<ListElement>();
            LayoutManager childLM = getChildLM();
            while (!childLM.isFinished()) {
                LayoutContext childLC = makeChildLayoutContext(context);
                List childElements = childLM.getNextKnuthElements(childLC, alignment);
                if (childElements != null) {
                    List<ListElement> newList = new LinkedList<ListElement>();
                    wrapPositionElements(childElements, newList);
                    knuthList.addAll(newList);
                }
            }
            return knuthList;
        }

    }

    private class WhitespaceManagement implements KnuthElementsGenerator {

        @SuppressWarnings("unchecked")
        public List<ListElement> getKnuthElements(LayoutContext context, int alignment) {

            MultiSwitchLayoutManager mslm = MultiSwitchLayoutManager.this;
            List<ListElement> knuthList = new LinkedList<ListElement>();
            WhitespaceManagementPenalty penalty = new WhitespaceManagementPenalty(
                    new WhitespaceManagementPosition(mslm));
            LayoutManager childLM;
            while ((childLM = getChildLM()) != null) {
                LayoutContext childLC = makeChildLayoutContext(context);
                List<ListElement> childElements = new LinkedList<ListElement>();
                while (!childLM.isFinished()) {
                    childElements.addAll(childLM.getNextKnuthElements(childLC, alignment));
                }
                List<ListElement> wrappedElements = new LinkedList<ListElement>();
                wrapPositionElements(childElements, wrappedElements);
                // TODO Doing space resolution here is wrong.
                SpaceResolver.resolveElementList(wrappedElements);
                int contentLength = ElementListUtils.calcContentLength(wrappedElements);
                penalty.addVariant(penalty.new Variant(wrappedElements, contentLength));
            }
            // Prevent the penalty from being ignored if it is at the beginning of the content
            knuthList.add(new KnuthBox(0, new Position(mslm), false));
            knuthList.add(penalty);
            // Prevent the penalty from being ignored if it is at the end of the content
            knuthList.add(new KnuthBox(0, new Position(mslm), false));
            return knuthList;
        }

    }

    private KnuthElementsGenerator knuthGen;

    public MultiSwitchLayoutManager(FObj node) {
        super(node);
        MultiSwitch multiSwitchNode = (MultiSwitch) node;
        if (multiSwitchNode.getAutoToggle() == Constants.EN_SELECT_FIRST_FITTING) {
            knuthGen = new WhitespaceManagement();
        } else {
            knuthGen = new DefaultKnuthListGenerator();
        }
    }

    @Override
    public List<ListElement> getNextKnuthElements(LayoutContext context, int alignment) {
        referenceIPD = context.getRefIPD();
        List<ListElement> knuthList = knuthGen.getKnuthElements(context, alignment);
        setFinished(true);
        return knuthList;
    }

    @Override
    public Area getParentArea(Area childArea) {
        return parentLayoutManager.getParentArea(childArea);
    }

    @Override
    public void addChildArea(Area childArea) {
        parentLayoutManager.addChildArea(childArea);
    }

    @Override
    public void addAreas(PositionIterator posIter, LayoutContext context) {
        LinkedList<Position> positionList = new LinkedList<Position>();
        while (posIter.hasNext()) {
            Position pos = posIter.next();
            if (pos instanceof WhitespaceManagementPosition) {
                positionList.addAll(((WhitespaceManagementPosition) pos).getPositionList());
            } else {
                positionList.add(pos);
            }
        }
        PositionIterator newPosIter = new PositionIterator(positionList.listIterator());
        AreaAdditionUtil.addAreas(this, newPosIter, context);
        flush();
    }

}
