/* $This file is distributed under the terms of the license in LICENSE$ */

package edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.generators;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.servlet.http.HttpSession;

import org.apache.jena.rdf.model.Literal;
import org.apache.jena.vocabulary.RDFS;
import org.apache.jena.vocabulary.XSD;

import edu.cornell.mannlib.vitro.webapp.auth.identifier.IdentifierBundle;
import edu.cornell.mannlib.vitro.webapp.auth.identifier.RequestIdentifiers;
import edu.cornell.mannlib.vitro.webapp.auth.identifier.common.HasAssociatedIndividual;
import edu.cornell.mannlib.vitro.webapp.beans.VClass;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.controller.admin.ShowAuthController.AssociatedIndividual;
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationUtils;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationVTwo;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.fields.FieldVTwo;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.validators.AntiXssValidation;

/**
 * Generates the edit configuration for a default property form.
 * ModelChangePreprocessor creates the rdfs:label statement.
 */
public class CompilationGenerator extends BaseEditConfigurationGenerator implements EditConfigurationGenerator {

    private String associatedProfile;
		private int excerptsCounter;

		@Override
    public EditConfigurationVTwo getEditConfiguration(VitroRequest vreq, HttpSession session) {

    	EditConfigurationVTwo config = new EditConfigurationVTwo();

    	associatedProfile = getAssociatedProfile(vreq);
  		excerptsCounter = parseCounter(vreq);
  		
    	config.setTemplate( "compilationForm.ftl" );

    	config.setN3Required( generateN3Required(vreq));
    	//Optional because user may have selected either person or individual of another kind
    	//Person uses first name and last name whereas individual of other class would use label
    	//middle name is also optional
    	config.setN3Optional(generateN3Optional());

    	config.addNewResource("newCompilation", vreq.getWebappDaoFactory().getDefaultNamespace());
    	config.addNewResource("newCompilationTOC", vreq.getWebappDaoFactory().getDefaultNamespace());
			for (int itemN = 1; itemN <= excerptsCounter; itemN++) {
				String tocItem = "tocItem" + itemN;
				config.addNewResource(tocItem, vreq.getWebappDaoFactory().getDefaultNamespace());
				String tocLevel = "tocLevel" + itemN;
				config.addNewResource(tocLevel, vreq.getWebappDaoFactory().getDefaultNamespace());
				
				String tocItemName = tocItem + "Name";
				config.addField(new FieldVTwo().
  	        setName(tocItemName).
  	        setRangeDatatypeUri(XSD.xstring.getURI()).
  	        setValidators(getCompilationNameValidators(vreq)));

				String tocLevelName = tocLevel + "Name";
				config.addField(new FieldVTwo().
  	        setName(tocLevelName).
  	        setRangeDatatypeUri(XSD.xstring.getURI()).
  	        setValidators(getCompilationNameValidators(vreq)));
				
				String excerpt = "excerpt" + itemN;
				config.addField(new FieldVTwo().
  	        setName(excerpt).
  	        setRangeDatatypeUri(XSD.xstring.getURI()).
  	        setValidators(getCompilationNameValidators(vreq)));
				
			}
    	config.setUrisOnform(getUrisOnForm());
      config.setLiteralsOnForm( getLiteralsOnForm());
    	setUrisAndLiteralsInScope(config);
    	//No SPARQL queries for existing since this is only used to create new, never for edit

    	config.addField(new FieldVTwo().
    	        setName("newCompilationLabel").
    	        setRangeDatatypeUri(XSD.xstring.getURI()).
    	        setValidators(getCompilationNameValidators(vreq)));

    	
        addFormSpecificData(config, vreq);

        config.addValidator(new AntiXssValidation());

        //This combines the first and last name into the rdfs:label
        // currently being done via javascript in the template. May use this again
        // when/if updated to ISF ontology.  tlw72
//        config.addModelChangePreprocessor(new FoafNameToRdfsLabelPreprocessor());

        String formUrl = EditConfigurationUtils.getFormUrlWithoutContext(vreq);
        config.setFormUrl(formUrl);

        //Note, the spaces are important - they were added by ProcessRdfFormController earlier
        //as a means of ensuring the substitution worked correctly - as the regex expects spaces
        config.setEntityToReturnTo(" ?newCompilation ");
        prepare(vreq, config);
    	return config;
    }

		private List<String> getUrisOnForm() {
			List<String> uris = list();
			for (int itemN = 1; itemN <= excerptsCounter; itemN++) {
				String excerpt = "excerpt" + itemN;
				uris.add(excerpt);
			}
			return uris;
		}

		private List<String> getLiteralsOnForm() {
			List<String> literals = list( "newCompilationLabel");
			for (int itemN = 1; itemN <= excerptsCounter; itemN++) {
				String tocItemName = "tocItem" + itemN + "Name";
				String tocLevelName = "tocLevel" + itemN + "Name";
				literals.add(tocItemName);
				literals.add(tocLevelName);
			}
			return literals;
		}

		private String getAssociatedProfile(VitroRequest vreq) {
			String associatedProfile;
			IdentifierBundle ids = RequestIdentifiers.getIdBundleForRequest(vreq);
    	Collection<String> individualURIs = HasAssociatedIndividual.getIndividualUris(ids);
    	Iterator<String> uriIterator = individualURIs.iterator();
    	if (uriIterator.hasNext()) {
    		associatedProfile = uriIterator.next();
    	} else {
    		associatedProfile = "";
    	}
			return associatedProfile;
		}

		private List<String> generateN3Required(VitroRequest vreq) {
		   StringBuilder n3Req = new StringBuilder(); 
		   n3Req.append("@prefix ts: <https://litvinovg.pro/text_structures#> .\n");
		   n3Req.append("?newCompilation <" + VitroVocabulary.RDF_TYPE  + "> <" + getTypeOfNew(vreq) + "> .\n");
		   n3Req.append("?newCompilation <" + RDFS.label.getURI() + ">  ?newCompilationLabel .\n");
		   n3Req.append("?newCompilation ts:hasTOC ?newCompilationTOC .\n");
		   n3Req.append("?newCompilationTOC <" + VitroVocabulary.RDF_TYPE  + "> ts:TOC .\n");
		   n3Req.append("?newCompilationTOC <" + RDFS.label.getURI() + ">  ?newCompilationLabel .\n");
		   if (!associatedProfile.isEmpty()) {
			   n3Req.append("<" + associatedProfile + "> ts:compilatorOf ?newCompilation .\n");

		   }
		   //n3Req.append();
		            	      
		   for (int itemN = 1; itemN <= excerptsCounter; itemN++) {
		  	 String tocItemVar = "?tocItem" + itemN ;
		  	 String tocItemNameVar = tocItemVar + "Name" ;
		  	 String tocLevelVar = "?tocLevel" + itemN ;
		  	 String tocLevelVarName = tocLevelVar + "Name" ;
		  	 String excerptVar = "?excerpt" + itemN ;
		  	 n3Req.append("?newCompilationTOC ts:hasTOCItem " + tocItemVar + " .\n");
		  	 n3Req.append(tocItemVar + " <" + VitroVocabulary.RDF_TYPE  + "> ts:TOCItem .\n");
 			   n3Req.append(tocItemVar + " <" + RDFS.label.getURI() + "> " + tocItemNameVar + " .\n");
 			   n3Req.append(tocItemVar + " ts:itemNumber " + itemN + " .\n");
 			   n3Req.append(tocItemVar + " ts:pointsTo " + tocLevelVar + " .\n");
		  	 n3Req.append(tocLevelVar + " <" + VitroVocabulary.RDF_TYPE  + "> ts:TOCLevel .\n");
 			   n3Req.append(tocLevelVar + " <" + RDFS.label.getURI() + "> " + tocLevelVarName + " .\n");
 			   n3Req.append(tocLevelVar + " ts:hasText " + excerptVar + ".\n");
		   }
    	 return list(n3Req.toString());
		}
		private static Integer parseCounter(VitroRequest vreq) {
			String text = vreq.getParameter("excerptsCount");
			if (text == null ) {
				return 0;
			}
			try {
		    return Integer.parseInt(text);
		  } catch (NumberFormatException e) {
		    return 0;
		  }
		}

		private List<String> generateN3Optional() {
			return getUrisOnForm();
		}


	//first and last name have validators if is person is true
    private List<String> getCompilationNameValidators(VitroRequest vreq) {
		List<String> validators = new ArrayList<String>();
		if(isCompilationType(vreq)) {
			validators.add("nonempty");
		}
		return validators;
	}



	//Get parameter from HTTP request for type of new individual
    private String getTypeOfNew(VitroRequest vreq) {
        String typeUri = vreq.getParameter("typeOfNew");
        if( typeUri == null || typeUri.trim().isEmpty() )
            return getCompilationClassURI();
        else
            return typeUri;
    }

    //Form specific data
	public void addFormSpecificData(EditConfigurationVTwo editConfiguration, VitroRequest vreq) {
		HashMap<String, Object> formSpecificData = new HashMap<String, Object>();
		formSpecificData.put("typeName", getTypeName(vreq));
		//Put in whether or not person type
		if(isCompilationType(vreq)) {
			//Doing this b/c unsure how freemarker will handle boolean value from JAVA
			formSpecificData.put("isCompilationType", "true");
		} else {
			formSpecificData.put("isCompilationType", "false");

		}
		formSpecificData.put("excerptsCounter", excerptsCounter);

		editConfiguration.setFormSpecificData(formSpecificData);
	}

	private String getTypeName(VitroRequest vreq) {
		String typeOfNew = getTypeOfNew(vreq);
		VClass type = vreq.getWebappDaoFactory().getVClassDao().getVClassByURI(typeOfNew);
		return type.getName();
	}

	public String getCompilationClassURI() {
		return "https://litvinovg.pro/text_structures#compilation";
	}

	public boolean isCompilationType(VitroRequest vreq) {
		WebappDaoFactory wdf = vreq.getWebappDaoFactory();
		Boolean isCompilationType = Boolean.FALSE;
		String foafPersonType = getCompilationClassURI();
		String typeOfNew = getTypeOfNew(vreq);
	    List<String> superTypes = wdf.getVClassDao().getAllSuperClassURIs(typeOfNew);
	    //add the actual type as well so we can add that for the list to be checked
	    superTypes.add(typeOfNew);
	    if( superTypes != null ){
	    	for( String typeUri : superTypes){
	    		if( foafPersonType.equals(typeUri)) {
	    			isCompilationType = Boolean.TRUE;
	    			break;
	    		}
	    	}
	    }
	    return isCompilationType;
	}
	 private void setUrisAndLiteralsInScope(EditConfigurationVTwo editConfiguration) {
	    	HashMap<String, List<String>> urisInScope = new HashMap<String, List<String>>();
	    	//note that at this point the subject, predicate, and object var parameters have already been processed
	    	urisInScope.put(editConfiguration.getVarNameForSubject(),
	    			Arrays.asList(new String[]{editConfiguration.getSubjectUri()}));
	    	urisInScope.put(editConfiguration.getVarNameForPredicate(),
	    			Arrays.asList(new String[]{editConfiguration.getPredicateUri()}));
	    	editConfiguration.setUrisInScope(urisInScope);
	    	//Uris in scope include subject, predicate, and object var

	    	editConfiguration.setLiteralsInScope(new HashMap<String, List<Literal>>());
	    }

	private String N3_PREFIX = "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n";
}
