package org.gcube.application.geoportal.storage;

import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.gcube.application.geoportal.model.db.DatabaseConnection;
import org.gcube.application.geoportal.model.db.DatabaseConnection.TYPE;
import org.gcube.application.geoportal.model.db.PostgisTable;
import org.gcube.application.geoportal.model.db.PostgisTable.Field;
import org.gcube.application.geoportal.model.fault.ConfigurationException;
import org.gcube.application.geoportal.model.fault.DataParsingException;
import org.gcube.application.geoportal.model.gis.BBOX;
import org.gcube.application.geoportal.utils.DBUtils;

import lombok.Synchronized;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class PostgisDBManager implements PostgisDBManagerI {

	@Synchronized
	public static PostgisDBManager get() throws SQLException, ConfigurationException {
		if(config==null) {
			log.debug("Looking for Default Configuration.. ");
			DatabaseConnection defaultConfiguration=DatabaseConnection.get(TYPE.Concessioni);
			log.debug("Found configuration :  "+defaultConfiguration);
			config=defaultConfiguration;
		}
		return new PostgisDBManager();
	}
	public static PostgisDBManagerI get(boolean autocommit) throws SQLException, ConfigurationException {
		PostgisDBManager toReturn=get();
		toReturn.conn.setAutoCommit(autocommit);
		return toReturn;
	}

	private static DatabaseConnection config;
	
	
	public static void init(DatabaseConnection config) throws SQLException, IOException {

		PostgisDBManager.config=config;
		
		try{
			Class.forName("org.postgresql.Driver");
			Class.forName("org.postgis.DriverWrapper");
		}catch(Exception e){
			throw new RuntimeException(e);
		}
		

	}

	
	
	private static Connection getConnection() throws SQLException {

		Connection toReturn=DriverManager.getConnection(config.getUrl(),config.getUser(),config.getPwd());
		toReturn.setAutoCommit(config.getAutocommit());
		return toReturn;
	}
	
	
	
	private Connection conn=null;
	
	
	private PostgisDBManager() throws SQLException {
		conn=getConnection();		
	}

	@Override
	public void create(PostgisTable toCreate) throws SQLException {
		String createStmt=toCreate.getCreateStatement();
		log.debug("Executing create : "+createStmt);
		conn.createStatement().executeUpdate(createStmt);
	}
	
	
	/* (non-Javadoc)
	 * @see org.gcube.application.geoportal.PostgisDBManagerI#commit()
	 */
	@Override
	public void commit() throws SQLException {
		conn.commit();
	}
	
	/* (non-Javadoc)
	 * @see org.gcube.application.geoportal.PostgisDBManagerI#evaluateBoundingBox(org.gcube.application.geoportal.model.PostgisTable)
	 */
	@Override
	public BBOX evaluateBoundingBox(PostgisTable table) throws SQLException, DataParsingException {
		ResultSet rs=conn.createStatement().executeQuery("Select ST_Extent("+table.getGeometryColumn()+") as extent from "+table.getTablename());
		if(rs.next())
			return DBUtils.parseST_Extent(rs.getString("extent"));
		else throw new SQLException("No extent returned");
	}
	
	/* (non-Javadoc)
	 * @see org.gcube.application.geoportal.PostgisDBManagerI#evaluateBoundingBox(org.gcube.application.geoportal.model.PostgisTable)
	 */
	@Override
	public PostgisTable.POINT evaluateCentroid(PostgisTable table) throws SQLException, DataParsingException {
		ResultSet rs=conn.createStatement().executeQuery("Select ST_AsText(ST_Centroid(ST_Collect("+table.getGeometryColumn()+"))) as centroid from "+table.getTablename());
		if(rs.next())
			return PostgisTable.POINT.parsePOINT(rs.getString("centroid"));
		else throw new SQLException("No extent returned");
	}
	
	/* (non-Javadoc)
	 * @see org.gcube.application.geoportal.PostgisDBManagerI#prepareInsertStatement(org.gcube.application.geoportal.model.PostgisTable, boolean, boolean)
	 */
	@Override
	public PreparedStatement prepareInsertStatement(PostgisTable target, boolean createTable, boolean geometryAsText) throws SQLException {
		if(createTable) {
			create(target);
		}
		String insertStmt=target.getInsertionStatement(geometryAsText);
		log.debug("Preparing insert statement : "+insertStmt);
		return conn.prepareStatement(insertStmt);		
	}
	
	
	@Override
	public int deleteByFieldValue(PostgisTable target, Field field, Object value) throws SQLException {
		PreparedStatement stmt = conn.prepareStatement(target.getDeleteByFieldStatement(field));
		target.setObjectInPreparedStatement(field, value, stmt, 1);
		return stmt.executeUpdate();
	}
	
	
	/* (non-Javadoc)
	 * @see org.gcube.application.geoportal.PostgisDBManagerI#deleteTable(java.lang.String)
	 */
	@Override
	public void deleteTable(String tableName) throws SQLException {
		conn.createStatement().executeUpdate("DROP TABLE "+tableName);
	}
	
	/* (non-Javadoc)
	 * @see org.gcube.application.geoportal.PostgisDBManagerI#truncate(java.lang.String)
	 */
	@Override
	public void truncate(String tableName) throws SQLException{
		conn.createStatement().executeUpdate("TRUNCATE Table "+tableName);
	}
	
	@Override
	public ResultSet queryAll(PostgisTable table) throws SQLException {
		// TODO Check schema
		return conn.createStatement().executeQuery("Select * from "+table.getTablename());
	}
}
