/**_____________________________________________________________________________
 *
 *                                 OATS - INAF
 *  Osservatorio Astronomico di Tireste - Istituto Nazionale di Astrofisica
 *  Astronomical Observatory of Trieste - National Institute for Astrophysics
 * ____________________________________________________________________________
 *
 * Copyright (C) 20016  Istituto Nazionale di Astrofisica
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc., 
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 * _____________________________________________________________________________
 **/

package it.inaf.oats.vospacebackend.implementation;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.ResultSet;
import org.apache.log4j.Logger;

import it.inaf.oats.vospacebackend.exceptions.ExceptionMessage;
import it.inaf.oats.vospacebackend.exceptions.VOSpaceBackendException;

/*
 * FileRecord myParams is used as input record
 * HashMap myResult is used as output record
 * @author bertocco
 */
public class VOSpaceBackendMetadata {
    
    /**
     * This class has two attributes:
     * <p>
     * Logger to perform logging operations
     * <p>
     * HashMap which will contain the metadata set identifying a stored/to store file
     * This hashmap fields will are:
     * boolean ifSuccessfull if db query is successful or not
     * String "original_file_name"
     * String "stored_file_name"
     * String "md5_checksum"
     * String "relative_path"
     */
    
    protected Logger log;
    
    protected FileRecord myResult; 

    /** 
     * Create the log and the HashMap that will be used to store the database
     * query result to return. The field "ifSuccessful" will contain a boolean 
     * value true if operation successfully executed and false if not.
     * Other fields will be added containing the query result set fields, 
     * depending on the executed query.
     */
    public VOSpaceBackendMetadata() {
        
        log = Logger.getLogger(VOSpaceBackendMetadata.class);  
        myResult = new FileRecord();
        myResult.setIfOperationSuccessful(false);
        
    }
    /**
     * 
     * @param fileName Original file name (provided by the client) of the file to store
     * @param stored_f_name name used to store the file in the file system
     * @param md5Checksum MD5 checksum of the file to store
     * @param relativePath relative path (after the root) of the file to store
     * @return boolean true if operation successful false if not
     * @throws VOSpaceBackendException
     */
    public FileRecord setFile(String stored_f_name, String md5Checksum, 
                         String relativePath) 
                                  throws VOSpaceBackendException, SQLException {
       
        String myOp = "SET";
        String myQuery = "INSERT INTO cadctest.StoredFiles " +
                "(stored_file_name, md5_checksum, relative_path)" +
                " VALUES (?, ?, ?);";
        FileRecord myParams = new FileRecord();
        
        myParams.setStoredfileName(stored_f_name);
        myParams.setMD5Checksum(md5Checksum);
        myParams.setRelativePath(relativePath);
        
        return excuteQuery(myOp, myQuery, myParams);
        
    }
    
    public FileRecord getFile(String orig_f_name) 
                                  throws VOSpaceBackendException, SQLException {
        
        String myOp = "GET";
        
        String myQuery = "SELECT * FROM cadctest.StoredFiles " +
                "WHERE original_file_name=?";
        // Da rifare: ricevo o il nodeid e con questo devo beccarmi il file
        // prima becco lo storage fname nella tabella node-storage e con quello
        // becco il file nella tabella storedFiles
        FileRecord myParams = new FileRecord();
        myParams.setStoredfileName(orig_f_name);
        
        return excuteQuery(myOp, myQuery, myParams);
        
    }
    
    
    public FileRecord getRequest(String nodeID) 
                                  throws VOSpaceBackendException, SQLException {
        
        String myOp = "GET_REQ";
       
        String myQuery = "SELECT * FROM cadctest.StoredFileAndNode " +
                "WHERE nodeID=?";
        // Da risistemare, non so neanche piu` se la uso ancora
        FileRecord myParams = new FileRecord();
        myParams.setNodeId(nodeID);
        
        return excuteQuery(myOp, myQuery, myParams);
        
    }
    
    
    public FileRecord setRequest(String stored_f_name, String nodeID) 
                                  throws VOSpaceBackendException, SQLException {
       
        String myOp = "SET_REQ";
        String myQuery = "INSERT INTO cadctest.StoredFileAndNode " +
                "(nodeID, storedFileID)" +
                " VALUES (?, ?);";
        log.debug("query : " + myQuery);
        log.debug("parameters: nodeID = " +  nodeID + " storedFileID = " + stored_f_name);
        FileRecord myParams = new FileRecord();
        log.debug("File record created. Its value is:");
        log.debug(myParams);
        myParams.setStoredfileName(stored_f_name);
        myParams.setNodeId(nodeID);
        log.debug("Going to execute query with FileRecord = " + myParams.toString());
                
        return excuteQuery(myOp, myQuery, myParams);
        
    }
    
    private FileRecord excuteQuery(String operation, String query, FileRecord fileToManage) 
                                                throws VOSpaceBackendException, SQLException {
        
        PreparedStatement preparedStatementInsert = null;
       
        Connection dbConnection = null;
        
        try {
	    dbConnection = DBConnectionMng.getDBConnection();
            log.debug("Database connection get");
        } catch (SQLException e) {
            log.fatal(e);
            ExceptionMessage exMsg = new ExceptionMessage();
            throw new VOSpaceBackendException(ExceptionMessage.getMessage("UNABLE_TO_GET_DB_CONNECTION"));
	}

	try {
	    dbConnection.setAutoCommit(false);
            log.debug("Autocommit set false");
	} catch (SQLException e) {
            log.fatal(e);
            ExceptionMessage exMsg = new ExceptionMessage();
            throw new VOSpaceBackendException(ExceptionMessage.getMessage("ERROR_DISABLING_DB_AUTOCOMMIT"));
	}
        
	// Starts JDBC Transaction
        PreparedStatement preparedQuery = null;
        // boolean query result (success or not)
        log.debug("Input file record: " + fileToManage.toString());
        boolean ifQuerySuccessful = false;
	try {
            
            //HashMap fileToManageFields = fileToManage.getFileRecord();
            ResultSet rs = null;   
            int rowCounter = 0;
            switch (operation) {
                case "SET":
                    log.debug("Going to prepare query for SET operation");
                    preparedQuery = dbConnection.prepareStatement(query);
                    preparedQuery.setString(1, fileToManage.getStoredfileName());
                    preparedQuery.setString(2, fileToManage.getMD5Checksum());
                    preparedQuery.setString(3, fileToManage.getRelativePath()); 
                    log.debug("Going to execute query");
                    preparedQuery.executeUpdate();
                    log.debug("Query executed");
                    dbConnection.commit();
                    log.debug("Query committed");
                    ifQuerySuccessful = true;
                    break;
                case "GET":
                    preparedQuery = dbConnection.prepareStatement(query);
                    preparedQuery.setString(1, (String)fileToManage.getNodeId());
                    rs = preparedQuery.executeQuery();
                    rowCounter = 0;
                    while (rs.next()) {
                        rowCounter = rowCounter +1;
                        myResult.setStoredfileName(rs.getString("stored_file_name"));
                        myResult.setMD5Checksum(rs.getString("md5_checksum"));                        
                        // Sistemare questa faccenda del nome file originale
                        // myResult.put("original_file_name", rs.getString("original_file_name"));
                        myResult.setRelativePath(rs.getString("relative_path"));
                    }
                    if (rowCounter == 0) {
                        log.debug("GET: query successfully executed. File not found"); 
                        ifQuerySuccessful = false;
                    } else {
                        log.debug("GET: query successfully executed. File found");
                        ifQuerySuccessful = true;
                    }
                    break;
                case "GET_REQ":
                    preparedQuery = dbConnection.prepareStatement(query);
                    preparedQuery.setString(1, fileToManage.getNodeId());
                    rs = preparedQuery.executeQuery();
                    rowCounter = 0;
                    while (rs.next()) {
                        rowCounter = rowCounter +1;
                        myResult.setStoredfileName(rs.getString("stored_file_name"));
                        myResult.setNodeId(rs.getString("nodeID"));
                    }
                    if (rowCounter == 0) {
                        log.debug("GET_REQ: query NOT executed. File metadata not found in backend");                      
                        ifQuerySuccessful = false;
                    } else {
                        log.debug("GET_REQ: query successfully executed. File found in the backend");
                        ifQuerySuccessful = true;
                    }
                    break;
                case "SET_REQ":                   
                    log.debug("Going to prepare query for SET operation");
                    preparedQuery = dbConnection.prepareStatement(query);
                    preparedQuery.setString(1, fileToManage.getNodeId());
                    preparedQuery.setString(2, fileToManage.getStoredfileName());
                    log.debug("Parameters : nodeID = " + fileToManage.getNodeId() + " StoredfileName = " +fileToManage.getStoredfileName());
                    log.debug("Going to execute query");
                    preparedQuery.executeUpdate();
                    log.debug("Query executed");
                    dbConnection.commit();
                    log.debug("Query committed"); 
                    ifQuerySuccessful = true;
                    break;                    
                    
                default:
                    ifQuerySuccessful = false;
                    ExceptionMessage exMsg = new ExceptionMessage();
                    log.fatal(ExceptionMessage.getMessage("DB_OPERATION_NOT_RECOGNIZED"));
                    throw new VOSpaceBackendException(ExceptionMessage.getMessage("DB_OPERATION_NOT_RECOGNIZED"));                    
                }
            
            dbConnection.setAutoCommit(true);
                        
            dbConnection.close();
            
        } catch (SQLException e) {

            log.error("SQLException exception executing SET file" + e.getMessage());
	    dbConnection.rollback();

	} finally {

            if (preparedQuery != null) {
		preparedQuery.close();
            }

            if (dbConnection != null) {
		dbConnection.close();
            }
            
	} 
        
        myResult.setIfOperationSuccessful(ifQuerySuccessful);
        
        log.debug("Returning " + operation + " result:\n" + myResult.toString());
        
        return myResult;
           
    }
    
}
