package OWLpreprocessing;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException ;
import java.nio.file.Files ;
import java.nio.file.Paths ;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.ArrayList;
import java.util.ListIterator;

public class OWL_CreaObjectPropertyData 
{
    /**
     * Common file handling methods
     */
    private OWL_BufferedWriterUtils bwu        = new OWL_BufferedWriterUtils() ;
    
    private OWL_EvalObjectPropertyAttributes attr = null ;
    private String              topObjProp = "owl:topObjectProperty" ;
    private String              DBname     = "" ;
    private int                 itemCount  = 0 ;
    
    private ArrayList<ObjPropertyData> opList = new ArrayList<ObjPropertyData>() ; 
    /**
     *  switches between the two processing steps: processing  
     *  the "normal" and the inverse object properties    
     */
    private boolean             processingInverseObjectProperties =  false ;
	
	OWL_CreaObjectPropertyData(String DBname,
                             String objPropertyDefFileName,   //  input
                             String objPropertyAttributes,    //  input
                             String objPropertyAnnotations,   //  output
                             String inverseObjProperties,     //  output
                             String superObjProperties        //  output
                            )
	{
		this.DBname = DBname ;
		
		this.attr = new OWL_EvalObjectPropertyAttributes(objPropertyDefFileName) ;
		
		writeSQLfileHdr(objPropertyAnnotations, "Object property annotations") ; 
        writeSQLfileHdr(inverseObjProperties,   "Inverse object properties") ;
		writeSQLfileHdr(superObjProperties,     "Super object properties") ;

	    buildObjectPropList(objPropertyDefFileName,

	    		              objPropertyAnnotations,
                              inverseObjProperties,
                              superObjProperties
                             ) ;
	    /**
	     *  processing the "NON-INVERSE" object properties
	     */
	    this.processingInverseObjectProperties = false ;
	    processObjectPropList(objPropertyAnnotations,
                              inverseObjProperties,
                              superObjProperties
                             ) ;
	    /**
	     *  processing the INVERSE object properties
	     */
	    this.processingInverseObjectProperties = true ;
	    processObjectPropList(objPropertyAnnotations,
                              inverseObjProperties,
                              superObjProperties
                             ) ;

	    commitSQLfiles(objPropertyAnnotations,
                       inverseObjProperties,
                       superObjProperties
                      ) ;
                       
	    //  Logging of the result
	    bwu.log(new StringBuilder()
	                .append("    asserted  " + this.itemCount + " object properties\n")
	                .toString()
	           ) ;
	    
	}   //  end of constructor

	
    /**
     *  generate SQL files from object properties
     *  input:  this.ArrayList<ObjPropertyData> opList
     *          names of the SQL files
     *         
     *  output: SQL file contents
     */
    private void processObjectPropList(String objPropertyAnnotations,
    		                           String inverseObjProperties,
                                       String superObjProperties
                                      )
    {
    	String objPropName          = "" ;
    	String superObjPropName     = "" ;
    	String lastSuperObjPropName = "" ;
    	
        ListIterator<ObjPropertyData> itr = this.opList.listIterator();            
        while (itr.hasNext())
        {
        	ObjPropertyData opItem = itr.next(); 

    		if (this.processingInverseObjectProperties == false)
    		{
               	objPropName = opItem.objPropName ;  //  processing the "normal" obj. props

               	if (opItem.isSuperObjProperty)
               	{
               		lastSuperObjPropName = opItem.objPropName ;
             		superObjPropName     = this.topObjProp ;
               	}
               	else
               	{
               		superObjPropName  = lastSuperObjPropName ;
               	}
    		}
    		else
    		{
            	objPropName = opItem.invObjPropName ; //  processing the INVERSE obj. props

               	if (opItem.isSuperObjProperty)
               	{
               		lastSuperObjPropName = opItem.invObjPropName ;
             		superObjPropName     = this.topObjProp ;
               	}
               	else
               	{
               		superObjPropName  = lastSuperObjPropName ;
               	}
               	
        		/** 
        		 *  writing inverse object property relation 
        		 *  into one file (only one time... in the
        		 *  2nd processing step)
        		 */
        	    writeInverseObjPropData(opItem.objPropName,
        	    		                opItem.invObjPropName,
                                        inverseObjProperties
                                       ) ;
    		}   //  end of if-else

    		/** 
    		 *  writing super/sub property relation and 
    		 *  property annotation into two different files
    		 */
    	    writeSuperObjDataAndAnnotations(objPropName, 
    	    		                        superObjPropName,
    	    		                        opItem.objPropAnnotation,
                                            superObjProperties,
                                            objPropertyAnnotations
                                           ) ;
        }   //  end of while loop

    }   //  end of method processObjectPropList() 
	
	private void addSQLcontent(String SQLfile, String content, boolean appendMode)
	{
        try  
        {
        	FileWriter fw = new FileWriter(new File(SQLfile), appendMode) ;

            fw.write(content) ;
            fw.flush();
        }
        catch (IOException e) { e.printStackTrace() ; }
		
	}   //  end of method commitSQLfiles()
	
	private void addSQLcontentAndClose(String SQLfile, String content, boolean appendMode)
	{
        try  
        {
        	FileWriter fw = new FileWriter(new File(SQLfile), appendMode) ;

            fw.write(content) ;
            fw.flush();
            fw.close() ;
        }
        catch (IOException e) { e.printStackTrace() ; }
		
	}   //  end of method commitSQLfiles()
	
	private void commitSQLfiles(String SQLfile_1,
                                String SQLfile_2,
                                String SQLfile_3
                               )
	{   //                                                                                 append
		addSQLcontentAndClose(SQLfile_1, "\nCOMMIT ; \n\n--   end of " + SQLfile_1 + "\n", true) ;
		addSQLcontentAndClose(SQLfile_2, "\nCOMMIT ; \n\n--   end of " + SQLfile_2 + "\n", true) ;
		addSQLcontentAndClose(SQLfile_3, "\nCOMMIT ; \n\n--   end of " + SQLfile_3 + "\n", true) ;
		
	}   //  end of method commitSQLfiles()
		
	private void buildObjectPropList(String objPropertyDefFileName, 
			                           String objPropertyAnnotations,
                                       String inverseObjProperties,
                                       String superObjProperties
                                      )
	{
		try
		{
            Files.lines(Paths.get(objPropertyDefFileName))
            .forEach(line -> {   
            	                 if (line.charAt(0) != '#')
                                     this.opList.add(new ObjPropertyData(line)) ;
                             }
                    ) ;
		}
        catch (IOException e) { e.printStackTrace() ; }
		
	}  //  end of method buildObjectPropList()
	
	
    private void writeSQLfileHdr(String outFile,
    		                     String description
    		                    )
    {
    	String  content = 
    			"/*\n" +          
                "    Title:       " + outFile + "\n" +         
                "    Description: " + description + "\n" +         
                "    Format:      OWL/XML encoding" + "\n" +         
                "    Written by:  Java class " + this.getClass().getSimpleName() + "\n" +         
                "    Date:        " + bwu.getCurrTime() + "\n" +         
                " */ \n" +          
                "USE   " + this.DBname + " ; \n" +         
                "\n" +          
                "SET   TRANSACTION READ WRITE ; \n" +         
                "START TRANSACTION ; \n" ;         

    	//                              appendMode
    	addSQLcontent(outFile, content, false) ;
    	
    }   //  end of method writeSQLfileHdr()
	
    private void writeSuperObjDataAndAnnotations(String objProp, 
    		                                     String superObjProp,
    		                                     String annotation,
    		                                     String superObjProperties,
    		                                     String objPropertyAnnotations
                                                )
	{
    	String content = "INSERT INTO SUPER_OBJECT_PROPERTIES " + 
    	                 "(object_property_IRI, super_object_property_IRI)\n" +
    	                 "    VALUES ('" + objProp + "', '" + superObjProp + "');\n" ;
    	addSQLcontent(superObjProperties, content, true) ;
    	
    	
    	String attributes = this.attr.getSQLattributeFragment(objProp) ;
    	
    	content = "INSERT INTO OBJECT_PROPERTIES (object_property_IRI, annotation_type_IRI, language, annotation, " +
                  "funct, invFunct, symm, aSymm, trans, refl, irRefl)\n" +

    	          
    	          "    VALUES ('" + objProp +  "', 'skos:definition', 'en', \n" +
    	          "    '" + annotation + "', " + attributes + ") ;\n\n" ;
        addSQLcontent(objPropertyAnnotations, content, true) ;
    	
        this.itemCount++ ;
        
	}   //  end of method writeSuperObjDataAndAnnotations()
    
    private void writeInverseObjPropData(String objProp, 
    		                             String invObjProp,
    		                             String outFile
    		                            )
	{
        //  System.out.println("--    >" + objProp + "<  vs  >" + invObjProp + "<") ;
        
    	String content = "INSERT INTO INVERSE_OBJECT_PROPERTIES (object_property_IRI, inverse_object_property_IRI)\n" +
                         "    VALUES ('" + objProp + "',  '" + invObjProp + "') ; \n" ;
    	
        addSQLcontent(outFile, content, true) ;
    	
	}   //  end of method writeInverseObjPropData()

    /**
     *  Inner class  objPropertyData   --------------------------------------------------- 
     */
    class ObjPropertyData
    {
        /*
         *  because of simplicity no getter/setter will be used
         */
        String  objPropName ;
        String  invObjPropName ;
        String  objPropAnnotation ;
        boolean isSuperObjProperty ;  

        ObjPropertyData(String line)
        {
            /**
             *  evaluate indentation            
             */
            if (line.substring(0, 8).compareTo("        ") == 0) 
            {
                //  case of SUPER object property
                //  the super object properties of them 
                //  is always the owl:topObjectProperty !
                
                isSuperObjProperty = false ;  
            }
            else if (line.substring(0, 4).compareTo("    ") == 0) 
            {
                //  case of SUB object property
                
                isSuperObjProperty = true ;  
            }
            else
            {
                System.err.println("1 Fatal error: erronneous line in " +
                                   "object property definition.\n" +
                                   ">" + line + "<") ; 
                System.exit(-1);
            }
        
            /**
             *  Pattern to match: <spaces> <word1> {"/"} <word2> <spaces> <annotation>
             *  This handles cases where word2 might be empty (just "-")
             */
            Pattern pattern = Pattern.compile("^\\s*(\\S+)/(\\S+)\\s+(.+)$");
            Matcher matcher = pattern.matcher(line);
            
            if (matcher.find()) 
            {
                line = line.trim();    
            
                this.objPropName       = matcher.group(1);
                this.invObjPropName    = matcher.group(2); // might be null if no "/"
                this.objPropAnnotation = matcher.group(3).trim();
                
                /**
                 *  If this.invObjPropName is null, it means there 
                 *  was no "/" separator. This is a fatal error
                 */
                if (this.invObjPropName == null) 
                {
                    System.err.println("2 Fatal error: erronneous line in " +
                               "object property definition.\n" +
                               ">" + line + "<") ; 
                    System.exit(-1);
                }

            }   //  end of if (matcher.find())
                
        }   //  end of constructor

    }   //  end of inner class ObjPropertyData
            
}   //  end of class CreateObjectPropertyData
    
