/*
 * 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.render.pdf;

import java.util.LinkedList;
import java.util.Locale;

import org.xml.sax.Attributes;

import org.apache.fop.accessibility.StructureTreeElement;
import org.apache.fop.accessibility.StructureTreeEventHandler;
import org.apache.fop.events.EventBroadcaster;
import org.apache.fop.fo.extensions.ExtensionElementMapping;
import org.apache.fop.pdf.PDFFactory;
import org.apache.fop.pdf.PDFName;
import org.apache.fop.pdf.PDFObject;
import org.apache.fop.pdf.PDFParentTree;
import org.apache.fop.pdf.PDFStructElem;
import org.apache.fop.pdf.PDFStructTreeRoot;

class PDFStructureTreeBuilder implements StructureTreeEventHandler {

    private PDFFactory pdfFactory;

    private PDFLogicalStructureHandler logicalStructureHandler;

    private EventBroadcaster eventBroadcaster;

    private LinkedList<PDFStructElem> ancestors = new LinkedList<PDFStructElem>();

    private PDFStructElem rootStructureElement;

    void setPdfFactory(PDFFactory pdfFactory) {
        this.pdfFactory = pdfFactory;
    }

    void setLogicalStructureHandler(PDFLogicalStructureHandler logicalStructureHandler) {
        this.logicalStructureHandler = logicalStructureHandler;
        createRootStructureElement();
    }

    private void createRootStructureElement() {
        assert rootStructureElement == null;
        PDFParentTree parentTree = logicalStructureHandler.getParentTree();
        PDFStructTreeRoot structTreeRoot = pdfFactory.getDocument().makeStructTreeRoot(parentTree);
        rootStructureElement = createStructureElement("root", structTreeRoot, null);
        structTreeRoot.addKid(rootStructureElement);
    }

    void setEventBroadcaster(EventBroadcaster eventBroadcaster) {
        this.eventBroadcaster = eventBroadcaster;
    }

    public void startPageSequence(Locale language, String role) {
        ancestors = new LinkedList<PDFStructElem>();
        PDFStructElem structElem = createStructureElement("page-sequence", rootStructureElement, role);
        if (language != null) {
            structElem.setLanguage(language);
        }
        rootStructureElement.addKid(structElem);
        ancestors.add(structElem);
    }

    private PDFStructElem createStructureElement(String name, PDFObject parent, String role) {
        PDFName structureType = FOToPDFRoleMap.mapFormattingObject(name, role, parent, eventBroadcaster);
        return pdfFactory.getDocument().makeStructureElement(structureType, parent);
    }

    public void endPageSequence() {
    }

    public StructureTreeElement startNode(String name, Attributes attributes) {
        PDFStructElem parent = ancestors.getFirst();
        String role = attributes.getValue("role");
        PDFStructElem structElem = createStructureElement(name, parent, role);
        parent.addKid(structElem);
        ancestors.addFirst(structElem);
        return structElem;
    }

    public void endNode(String name) {
        removeFirstAncestor();
    }

    private void removeFirstAncestor() {
        ancestors.removeFirst();
    }

    public StructureTreeElement startImageNode(String name, Attributes attributes) {
        PDFStructElem parent = ancestors.getFirst();
        String role = attributes.getValue("role");
        PDFStructElem structElem = createStructureElement(name, parent, role);
        parent.addKid(structElem);
        String altTextNode = attributes.getValue(ExtensionElementMapping.URI, "alt-text");
        if (altTextNode != null) {
            structElem.put("Alt", altTextNode);
        } else {
            structElem.put("Alt", "No alternate text specified");
        }
        ancestors.addFirst(structElem);
        return structElem;
    }

    public StructureTreeElement startReferencedNode(String name, Attributes attributes) {
        PDFStructElem parent = ancestors.getFirst();
        String role = attributes.getValue("role");
        PDFStructElem structElem;
        if ("#PCDATA".equals(name)) {
            structElem = new PDFStructElem.Placeholder(parent, name);
        } else {
            structElem = createStructureElement(name, parent, role);
        }
        parent.addKid(structElem);
        ancestors.addFirst(structElem);
        return structElem;
    }

}
