/*
* 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.stanbol.entityhub.indexing.source.vcard;

import java.util.HashMap;
import java.util.Map;

import net.fortuna.ical4j.vcard.Property.Id;
import edu.emory.mathcs.backport.java.util.Collections;

public class OntologyMappings {
    
    public static final String N_FAMILY = "N.family";
    public static final String N_GIVEN = "N.given";
    public static final String N_ADDITIONAL ="N.additional";
    public static final String N_PREFIX = "N.prefix";
    public static final String N_SUFFIX = "N.suffix";
    public static final String ADR_POST_OFFICE_ADDRESS = "ADR.poBox";
    public static final String ADR_EXTENDED = "ADR.extended";
    public static final String ADR_STREET = "ADR.street";
    public static final String ADR_LOCALITY = "ADR.locality";
    public static final String ADR_REGION = "ADR.region";
    public static final String ADR_POSTAL_CODE = "ADR.postalCode";
    public static final String ADR_COUNTRY = "ADR.country";
    public static final String GEO_LONGITUDE = "GEO.longitude";
    public static final String GEO_LATITUDE = "GEO.latitude";
    
    public static final String ORG_NAME = "ORG.name";
    public static final String ORG_UNIT = "ORG.unit";
    
    public static final String RDF_TYPE = "rdf:type";
    
    public static final String VCARD_PERSON = "vcard:Person";
    public static final String VCARD_ORGANIZATION = "vcard:Organization";
    
    /**
     * Defines mapping information for vCard properties as used as 
     * values for the {@link Map} defined as static members of the parent
     * {@link OntologyMappings} class.<p>
     * The keys {@link OntologyMappings#VCARD_PERSON} and
     * {@link OntologyMappings#VCARD_ORGANIZATION} can be used to specify the
     * <code>rdf:type</code> used for imported Persons and Organisations.<p>
     * The <code>uri</code> is a required parameter and specifies the
     * property of the Ontology used to store the values of the mapped vcard
     * property.<p>
     * For object properties the creation of sub-mappings and an inverse relation
     * is supported. The created sub-resource will be linked the the resource
     * of the vCard object by the <code>uri</code> property. If a mapping for
     * {@link OntologyMappings#RDF_TYPE} is present fir sub-mappings this is used
     * as <code>rdf:type</code> value for sub-resources<p>
     * To specify that sub-values of a vCard property should be directly
     * added to the resource of the vCard object one needs to add a
     * <code>null</code> mapping for the parent and than add the mappings for
     * the sub-properties also directly to this mappings.<br>
     * Here an example for the vCard "N" element:<p>
     * <pre><code>
     *     mappings.put("N",null); //add null mapping for the parent
     *     mappings.put("N_FAMILY", new Mapping("http:schema.org/familyName");
     *     mappings.put("N_GIVEN", new Mapping("http:schema.org/givenName");
     * </code></pre>
     * @author Rupert Westenthaler
     */
    public static class Mapping{
        /**
         * The uri
         */
        public final String uri;
        public final String invUri;
        /**
         * the sub-mappings (read only).
         */
        public final Map<String,Mapping> subMappings;
        /**
         * Mapping for a vCard property to an rdfs:Property uri.
         * @param uri the URI of the rdfs:Property
         */
        private Mapping(String uri) {
            this(uri,null,null);
        }
        /**
         * Allows to define a Mapping with type and subMappings. See 
         * class level documentation for details.
         * @param uri the rdfs:Property uri used to store the information of the
         * mapped vCard property 
         * @param invUri the inverse property used to link from the sub-resource
         * back to the vCard resource.
         * @param subMappings mappings fpr the sub-resource
         */
        private Mapping(String uri,String invUri,Map<String,Mapping> subMappings) {
            if(uri == null || uri.isEmpty()){
                throw new IllegalArgumentException("The parsed property MUST NOT be NULL nor empty!");
            }
            this.uri = uri;
            if(invUri != null && subMappings == null){
                throw new IllegalArgumentException("Inverse Properties are only supported if sub-mappings are present");
            }
            this.invUri = invUri;
            this.subMappings = subMappings == null ? null : Collections.unmodifiableMap(subMappings);
        }
    }
    
    public static final Map<String,Mapping> schemaOrgMappings;
    private static final String schema = "http://schema.org/";
    static {
        Map<String,Mapping> mappings = new HashMap<String,Mapping>();
        //the rdf:type for vCard objects is schema.org/Person
        mappings.put(VCARD_PERSON, new Mapping(schema+"Person"));
        //and schema.org/Organization for vCard objects that do not define a
        //FN but only a ORG property
        mappings.put(VCARD_ORGANIZATION, new Mapping(schema+"Organization"));
        //map the formatted name and the nick to schema.org/name
        mappings.put(Id.FN.getPropertyName(), new Mapping(schema+"name"));
        mappings.put(Id.NICKNAME.getPropertyName(), new Mapping(schema+"name"));
        mappings.put(Id.NOTE.getPropertyName(), new Mapping(schema+"description"));
        //Name details are directly added to the person
        mappings.put(Id.N.getPropertyName(), null);
        mappings.put(N_FAMILY, new Mapping(schema+"familyName"));
        mappings.put(N_GIVEN, new Mapping(schema+"givenName"));
        mappings.put(N_ADDITIONAL, new Mapping(schema+"additionalName"));
        mappings.put(N_PREFIX, new Mapping(schema+"honorificPrefix"));
        mappings.put(N_SUFFIX, new Mapping(schema+"honorificSuffix"));


        //Address are stored in an own resource
        Map<String,Mapping> subMappings = new HashMap<String,Mapping>();
        mappings.put(Id.ADR.getPropertyName(), new Mapping(
            schema+"address", null,//address is the property (no inverse)
            subMappings)); //and there are sub mappings
        subMappings.put(RDF_TYPE, new Mapping(schema+"PostalAddress"));
        subMappings.put(ADR_COUNTRY, new Mapping(schema+"addressCountry"));
        subMappings.put(ADR_POSTAL_CODE, new Mapping(schema+"postalCode"));
        subMappings.put(ADR_STREET, new Mapping(schema+"streetAddress"));
        subMappings.put(ADR_POST_OFFICE_ADDRESS, new Mapping(schema+"postOfficeBoxNumber"));
        subMappings.put(ADR_REGION, new Mapping(schema+"addressRegion"));
        subMappings.put(ADR_EXTENDED, new Mapping(schema+"addressLocality"));
        
        //WorkLocation can be both "PostalAddress" or "Place". For modelling the
        //ADR I have chosen to use "PostalAdress". However to support both
        //ADR and GEO one would need to use an intermediate Place and append the
        //PostalAddress and the GeoCoordinate to it.
        //Because in vCard the ADR is much more important I choose to use
        // "PostalAddres" and to ignore GEO information.
        //mappings.put(Id.GEO.getPropertyName(), new Mapping(schema+"workLocation",));

        //Organisational properties
        
        //Here both TITLE and ROLE is mapped to JobTitle
        mappings.put(Id.TITLE.getPropertyName(), new Mapping(schema+"jobTitle"));
        mappings.put(Id.ROLE.getPropertyName(), new Mapping(schema+"jobTitle"));
        subMappings = new HashMap<String,Mapping>();
        mappings.put(Id.ORG.getPropertyName(), new Mapping(
            schema+"worksFor", schema+"employees",
            subMappings));
        subMappings.put(RDF_TYPE, new Mapping(schema+"Organization"));
        subMappings.put(ORG_NAME, new Mapping(schema+"name"));
        
        mappings.put(Id.BDAY.getPropertyName(), new Mapping(schema+"birthDate"));
        mappings.put(Id.DEATH.getPropertyName(), new Mapping(schema+"deathDate"));
        mappings.put(Id.GENDER.getPropertyName(), new Mapping(schema+"gender"));
        
        mappings.put(Id.PHOTO.getPropertyName(), new Mapping(schema+"image"));

        mappings.put(Id.TEL.getPropertyName(), new Mapping(schema+"telephone"));
        mappings.put(Id.EMAIL.getPropertyName(), new Mapping(schema+"email"));
        mappings.put(Id.URL.getPropertyName(), new Mapping(schema+"url"));


        //Not mapped Properties
        //mappings.put(Id.AGENT.getPropertyName(), new Mapping(schema+""));
        //mappings.put(Id.LABEL.getPropertyName(), new Mapping(schema+""));
        //mappings.put(Id.LANG.getPropertyName(), new Mapping(schema+""));
        //mappings.put(Id.LOGO.getPropertyName(), new Mapping(schema+""));
        //mappings.put(Id.MEMBER.getPropertyName(), new Mapping(schema+""));
        //mappings.put(Id.PRODID.getPropertyName(), new Mapping(schema+""));
        //mappings.put(Id.RELATED.getPropertyName(), new Mapping(schema+""));
        //mappings.put(Id.REV.getPropertyName(), new Mapping(schema+""));
        //mappings.put(Id.SORT_STRING.getPropertyName(), new Mapping(schema+""));
        //mappings.put(Id.SOUND.getPropertyName(), new Mapping(schema+""));
        //mappings.put(Id.SOURCE.getPropertyName(), new Mapping(schema+""));
        //mappings.put(Id.TZ.getPropertyName(), new Mapping(schema+""));
        //mappings.put(Id.UID.getPropertyName(), new Mapping(schema+""));
        //mappings.put(Id.VERSION.getPropertyName(), new Mapping(schema+""));
        schemaOrgMappings = Collections.unmodifiableMap(mappings);
    }

}
