/*
 * $Id: SKOSRendererVisitor.java 1299 2010-03-07 19:47:04Z euzenat $
 *
 * Copyright (C) INRIA, 2003-2004, 2006-2010
 * Copyright (C) Quentin Reul, 2008
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 */

package fr.inrialpes.exmo.align.impl.renderer; 

import java.util.Enumeration;
import java.util.Properties;
import java.io.PrintWriter;
import java.net.URI;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;

import org.semanticweb.owl.align.Visitable;
import org.semanticweb.owl.align.Alignment;
import org.semanticweb.owl.align.AlignmentVisitor;
import org.semanticweb.owl.align.AlignmentException;
import org.semanticweb.owl.align.Cell;
import org.semanticweb.owl.align.Relation;

import fr.inrialpes.exmo.align.impl.rel.*;
import fr.inrialpes.exmo.align.impl.ObjectAlignment;

import fr.inrialpes.exmo.ontowrap.LoadedOntology;
import fr.inrialpes.exmo.ontowrap.OntowrapException;

/**
 * Renders an alignment as a new ontology merging these.
 *
 * @author Jérôme Euzenat
 * @version $Id: SKOSRendererVisitor.java 1299 2010-03-07 19:47:04Z euzenat $ 
 */

public class SKOSRendererVisitor implements AlignmentVisitor {
    PrintWriter writer = null;
    Alignment alignment = null;
    LoadedOntology onto1 = null;
    LoadedOntology onto2 = null;
    Cell cell = null;
    boolean embedded = false; // if the output is XML embeded in a structure
    boolean pre2008 = false; // compatibility mode with SKOS early drafts

    public SKOSRendererVisitor( PrintWriter writer ){
	this.writer = writer;
    }

    public void init( Properties p ) {
	if ( p.getProperty( "embedded" ) != null 
	     && !p.getProperty( "embedded" ).equals("") ) embedded = true;
	if ( p.getProperty( "pre2008" ) != null 
	     && !p.getProperty( "pre2008" ).equals("") ) pre2008 = true;
    };

    // This must be considered
    public void visit( Visitable o ) throws AlignmentException {
	if ( o instanceof Alignment ) visit( (Alignment)o );
	else if ( o instanceof Cell ) visit( (Cell)o );
	else if ( o instanceof Relation ) visit( (Relation)o );
    }

    public void visit( Alignment align ) throws AlignmentException {
	alignment = align;
	if ( align instanceof ObjectAlignment ) {
	    onto1 = (LoadedOntology)((ObjectAlignment)align).getOntologyObject1();
	    onto2 = (LoadedOntology)((ObjectAlignment)align).getOntologyObject2();
	}
	if ( embedded == false ) {
	    writer.print("<?xml version='1.0' encoding='utf-8");
	    writer.print("' standalone='no'?>\n");
	}
	writer.print("<rdf:RDF\n");
	writer.print("  xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\n"); 
	writer.print("  xmlns:rdfs=\"http://www.w3.org/2000/01/rdf-schema#\"\n");
	if ( pre2008 ) {
	    writer.print("  xmlns:skos=\"http://www.w3.org/2004/02/skos/core#\">\n\n");
	} else {
	    writer.print("  xmlns:skos=\"http://www.w3.org/2008/05/skos#\">\n\n");
	}
	writer.print("  <!-- Generated by fr.inrialpes.exmo.impl.renderer.SKOSRendererVisitor -->\n");
	for ( String[] ext : align.getExtensions() ){
	    writer.print("  <!-- "+ext[1]+": "+ext[2]+" -->\n");
	}
	writer.print("\n");
	for( Cell c : align ){
	    c.accept( this );
	} //end for
	writer.print("</rdf:RDF>\n");
    }

    public URI getURI2() throws AlignmentException {
	if ( onto2 != null ) {
	    try {
		return onto2.getEntityURI( cell.getObject2() );
	    } catch ( OntowrapException owex ) {
		throw new AlignmentException( "Cannot find entity URI", owex );
	    }
	} else {
	    return cell.getObject2AsURI( alignment );
	}
    }

    public void visit( Cell cell ) throws AlignmentException {
	this.cell = cell;
	if ( onto1 != null ) {
	    try {
		writer.print("  <skos:Concept rdf:about=\""+onto1.getEntityURI( cell.getObject1() )+"\">\n");
	    } catch ( OntowrapException owex ) {
		throw new AlignmentException( "Cannot find entity URI", owex );
	    }
	} else {
	    writer.print("  <skos:Concept rdf:about=\""+cell.getObject1AsURI( alignment )+"\">\n");
	}
	cell.getRelation().accept( this );
	writer.print("  </skos:Concept>\n\n");
    }
    public void visit( EquivRelation rel ) throws AlignmentException {
	if ( pre2008 ) {
	    writer.print("    <skos:related rdf:resource=\""+getURI2()+"\"/>\n");
	} else {
	    writer.print("    <skos:exactMatch rdf:resource=\"" + getURI2() + "\"/>\n");
	}
    }
    public void visit( SubsumeRelation rel ) throws AlignmentException {
	if ( pre2008 ) {
	    writer.print("    <skos:narrower rdf:resource=\""+getURI2()+"\"/>\n");
	} else {
	    writer.print("    <skos:narrowMatch rdf:resource=\""+getURI2()+"\"/>\n");
	}
    }
    public void visit( SubsumedRelation rel ) throws AlignmentException {
	if ( pre2008 ) {
	    writer.print("    <skos:broader rdf:resource=\""+getURI2()+"\"/>\n");
	} else {
	    writer.print("    <skos:broadMatch rdf:resource=\""+getURI2()+"\"/>\n");
	}
    }
    public void visit( IncompatRelation rel ) throws AlignmentException {
	throw new AlignmentException("Cannot translate in SKOS"+rel);
    }
    public void visit( Relation rel ) throws AlignmentException {
	// JE: I do not understand why I need this,
	// but this seems to be the case...
	try {
	    Method mm = null;
	    if ( Class.forName("fr.inrialpes.exmo.align.impl.rel.EquivRelation").isInstance(rel) ){
		mm = this.getClass().getMethod("visit",
					       new Class [] {Class.forName("fr.inrialpes.exmo.align.impl.rel.EquivRelation")});
	    } else if (Class.forName("fr.inrialpes.exmo.align.impl.rel.SubsumeRelation").isInstance(rel) ) {
		mm = this.getClass().getMethod("visit",
					       new Class [] {Class.forName("fr.inrialpes.exmo.align.impl.rel.SubsumeRelation")});
	    } else if (Class.forName("fr.inrialpes.exmo.align.impl.rel.SubsumedRelation").isInstance(rel) ) {
		mm = this.getClass().getMethod("visit",
					       new Class [] {Class.forName("fr.inrialpes.exmo.align.impl.rel.SubsumedRelation")});
	    } else if (Class.forName("fr.inrialpes.exmo.align.impl.rel.IncompatRelation").isInstance(rel) ) {
		mm = this.getClass().getMethod("visit",
					       new Class [] {Class.forName("fr.inrialpes.exmo.align.impl.rel.IncompatRelation")});
	    }
	    if ( mm != null ) mm.invoke(this,new Object[] {rel});
	} catch (IllegalAccessException e) {
	    e.printStackTrace();
	} catch (ClassNotFoundException e) {
	    e.printStackTrace();
	} catch (NoSuchMethodException e) {
	    e.printStackTrace();
	} catch (InvocationTargetException e) { 
	    e.printStackTrace();
	}
    };
}
