/*
 * 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.any23.extractor.html.microformats2;

import org.apache.any23.extractor.ExtractionException;
import org.apache.any23.extractor.ExtractionResult;
import org.apache.any23.extractor.ExtractorDescription;
import org.apache.any23.vocab.HRecipe;
import org.eclipse.rdf4j.model.BNode;
import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.vocabulary.RDF;
import org.w3c.dom.Node;
import org.apache.any23.extractor.html.EntityBasedMicroformatExtractor;
import org.apache.any23.extractor.html.HTMLDocument;

/**
 * Extractor for the <a href="http://microformats.org/wiki/hrecipe">hRecipe</a>
 * microformat.
 *
 * @author Nisala Nirmana
 */
public class HRecipeExtractor extends EntityBasedMicroformatExtractor {

    private static final HRecipe vHRECIPE = HRecipe.getInstance();

    private static final String[] recipeFields = {
            "name",
            "ingredient",
            "yield",
            "instructions",
            "duration",
            "photo",
            "summary",
            "author",
            "published",
            "nutrition"
    };

    @Override
    public ExtractorDescription getDescription() {
        return HRecipeExtractorFactory.getDescriptionInstance();
    }

    @Override
    protected String getBaseClassName() {
        return Microformats2Prefixes.CLASS_PREFIX+"recipe";
    }

    @Override
    protected void resetExtractor() {
        // Empty.
    }

    @Override
    protected boolean extractEntity(Node node, ExtractionResult out) throws ExtractionException {
        final BNode recipe = getBlankNodeFor(node);
        conditionallyAddResourceProperty(recipe, RDF.TYPE, vHRECIPE.Recipe);
        final HTMLDocument fragment = new HTMLDocument(node);
        addName(fragment, recipe);
        addIngredients(fragment, recipe);
        addYield(fragment, recipe);
        addInstructions(fragment, recipe);
        addDurations(fragment, recipe);
        addPhoto(fragment, recipe);
        addSummary(fragment, recipe);
        addAuthors(fragment, recipe);
        addPublished(fragment, recipe);
        addNutritions(fragment, recipe);
        return true;
    }

    private void mapFieldWithProperty(HTMLDocument fragment, BNode recipe, String fieldClass, IRI property) {
        HTMLDocument.TextField title = fragment.getSingularTextField(fieldClass);
        conditionallyAddStringProperty(
                title.source(), recipe, property, title.value()
        );
    }

    private void addName(HTMLDocument fragment, BNode recipe) {
        mapFieldWithProperty(fragment, recipe, Microformats2Prefixes.PROPERTY_PREFIX + recipeFields[0], vHRECIPE.fn);
    }

    private void addIngredients(HTMLDocument fragment, BNode recipe) {
        final HTMLDocument.TextField[] ingredients = fragment.getPluralTextField
                (Microformats2Prefixes.PROPERTY_PREFIX+recipeFields[1]);
        for(HTMLDocument.TextField ingredient : ingredients) {
            conditionallyAddStringProperty(
                    ingredient.source(), recipe, vHRECIPE.ingredient, ingredient.value()
            );
        }
    }

    private void addInstructions(HTMLDocument fragment, BNode recipe) {
        mapFieldWithProperty(fragment, recipe, Microformats2Prefixes.EMBEDDED_PROPERTY_PREFIX+recipeFields[2],
                                                                                               vHRECIPE.instructions);
    }

    private void addYield(HTMLDocument fragment, BNode recipe) {
        mapFieldWithProperty(fragment, recipe, Microformats2Prefixes.PROPERTY_PREFIX+recipeFields[3], vHRECIPE.yield);
    }

    private void addDurations(HTMLDocument fragment, BNode recipe) {
        final HTMLDocument.TextField[] durations = fragment.getPluralTextField(
                Microformats2Prefixes.TIME_PROPERTY_PREFIX + recipeFields[4]);
        for(HTMLDocument.TextField duration : durations) {
            Node attribute=duration.source().getAttributes().getNamedItem("datetime");
            if (attribute==null){
                conditionallyAddStringProperty(
                    duration.source(),
                    recipe, vHRECIPE.duration, duration.value()
                );
            }else{
                conditionallyAddStringProperty(
                        duration.source(),
                        recipe, vHRECIPE.duration, attribute.getNodeValue()
                );

            }

        }
    }

    private void addPhoto(HTMLDocument fragment, BNode recipe) throws ExtractionException {
        final HTMLDocument.TextField[] photos = fragment.getPluralUrlField
                (Microformats2Prefixes.URL_PROPERTY_PREFIX+recipeFields[5]);
        for(HTMLDocument.TextField photo : photos) {
            addIRIProperty(recipe, vHRECIPE.photo, fragment.resolveIRI(photo.value()));
        }
    }

    private void addSummary(HTMLDocument fragment, BNode recipe) {
        mapFieldWithProperty(fragment, recipe, Microformats2Prefixes.PROPERTY_PREFIX+recipeFields[6], vHRECIPE.summary);
    }

    private void addAuthors(HTMLDocument fragment, BNode recipe) {
        final HTMLDocument.TextField[] authors = fragment.
                getPluralTextField(Microformats2Prefixes.PROPERTY_PREFIX + recipeFields[7]);
         for(HTMLDocument.TextField author : authors) {
             conditionallyAddStringProperty(
                    author.source(),
                    recipe, vHRECIPE.author, author.value()
              );
        }
    }

    private void addPublished(HTMLDocument fragment, BNode recipe) {
        final HTMLDocument.TextField[] durations = fragment.getPluralTextField(
                Microformats2Prefixes.TIME_PROPERTY_PREFIX + recipeFields[8]);
        for(HTMLDocument.TextField duration : durations) {
            Node attribute=duration.source().getAttributes().getNamedItem("datetime");
            if (attribute==null){
                conditionallyAddStringProperty(
                        duration.source(),
                        recipe, vHRECIPE.published, duration.value()
                );
            }else{
                conditionallyAddStringProperty(
                        duration.source(),
                        recipe, vHRECIPE.published, attribute.getNodeValue()
                );
            }
        }
    }

    private void addNutritions(HTMLDocument fragment, BNode recipe) {
        final HTMLDocument.TextField[] nutritions = fragment.getPluralTextField
                (Microformats2Prefixes.PROPERTY_PREFIX+recipeFields[9]);
        for(HTMLDocument.TextField nutrition : nutritions) {
            conditionallyAddStringProperty(
                    nutrition.source(), recipe, vHRECIPE.nutrition, nutrition.value()
            );
        }
    }
}
