package org.gcube.application.cms.sdi.engine;

import lombok.extern.slf4j.Slf4j;
import org.gcube.application.cms.sdi.faults.DataParsingException;
import org.gcube.application.geoportal.common.model.legacy.BBOX;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class PostgisDBManager implements PostgisDBManagerI {

    private Connection conn=null;


    public PostgisDBManager(Connection conn) throws SQLException {
        this.conn=conn;
    }

    @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, PostgisTable.Field field, Object value) throws SQLException {
        String query=target.getDeleteByFieldStatement(field);
        log.debug("Preparing DELETE SQL {} with field {} = {} ",query,field,value);

        PreparedStatement stmt = conn.prepareStatement(query);
        target.setObjectInPreparedStatement(field, value, stmt, 1);
        int result=stmt.executeUpdate();
        log.debug("DELETED {} rows ",result);
        return result;
    }


    /* (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());
    }

    @Override
    public long count(PostgisTable table) throws SQLException {
        ResultSet rs =  conn.createStatement().executeQuery("Select count(*) from "+table.getTablename());
        rs.next();
        return rs.getLong(1);
    }


    // *********************** INNER UTILS CLASS

    protected static class DBUtils {


        private static Pattern pattern = Pattern.compile("(?!=\\d\\.\\d\\.)([\\d.]+)");

        public static BBOX parseST_Extent(String extent) throws DataParsingException {
            //BOX(11.9122574810083 44.2514144864263,11.9761128271586 44.2912342569845)
            try {
                log.debug("Parsing BBOX "+extent);
                Matcher m=pattern.matcher(extent);

                //				Scanner sc = new Scanner(extent);
                //				double minLong = sc.nextDouble(),
                //				       minLat = sc.nextDouble(),
                //				       maxLong = sc.nextDouble(),
                //				       maxLat= sc.nextDouble();

                if(!m.find()) throw new DataParsingException("Unable to get minLong ");
                Double minLong=Double.parseDouble(m.group(1));

                if(!m.find()) throw new DataParsingException("Unable to get minLat ");
                Double minLat=Double.parseDouble(m.group(1));

                if(!m.find()) throw new DataParsingException("Unable to get maxLong ");
                Double maxLong=Double.parseDouble(m.group(1));

                if(!m.find()) throw new DataParsingException("Unable to get maxLat ");
                Double maxLat=Double.parseDouble(m.group(1));
                return new BBOX(maxLat, maxLong, minLat, minLong);
            }catch(Throwable t) {
                throw new DataParsingException("Invalid BBOX "+extent,t);
            }
        }
    }
}

