/**
 * 
 */
package cz.hsrs.db.util;

import java.sql.Array;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.PatternSyntaxException;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import cz.hsrs.db.model.NoItemFoundException;
import cz.hsrs.db.model.Unit;
import cz.hsrs.db.pool.SQLExecutor;

/**
 * Class for running analysis implemented in the database
 * @author mkepka
 *
 */
public class AnalystUtil extends DBUtil{

    static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssZ");

    private final String ANALYST_SCHEMA = "computations";
    private final String FOODIE_SCHEMA = "foodie";
    
    public AnalystUtil(){ }

    /**
     * Method runs analysis for calculating working time on particular sites
     * @param userName id of user identifies available units
     * @param date of the analysis
     * @param unitId id of the unit for analysis
     * @return ResultSet with results of the analysis 
     * @throws SQLException
     */
    public ResultSet getWorkDurationOnSites(String userName, String date, long unitId) throws SQLException{
        boolean isAffiliated = checkUnitAffiliation2User(userName, unitId);
        
        if(isAffiliated){
            if(date != null){
                if(!date.isEmpty() && unitId != 0){
                    String query = "SELECT calendar_date, unit_id, site_code, move_time, description, "
                            + "rfid, description_cze, test_flow "
                            + "FROM "+ANALYST_SCHEMA+".work_duration_sites_period('"+date+"', '"+date+"', "+unitId+");";
                    return stmt.executeQuery(query);
                } else{
                    throw new SQLException("Parameter date or unit_id was not defined!");
                }
            } else{
                throw new SQLException("Parameter date was not defined!");
            }
        } else{
            throw new SQLException("Specified unit does not belong in any group of the user!");
        }
    }
    
    /**
     * Method runs analysis for calculating working time on particular sites
     * @param userName id of user identifies available units
     * @param fromDate begin date of the period, if null it is substituted by toDate -7 days 
     * @param toDate end date of the period, if null it is substituted by fromDate + 7 days
     * @param unitId id of the unit for analysis
     * @return ResultSet with results of the analysis 
     * @throws SQLException
     */
    public ResultSet getWorkDurationOnSitesPeriod(String userName, String fromDate, String toDate, long unitId) throws SQLException{
        boolean isAffiliated = checkUnitAffiliation2User(userName, unitId);
        
        if(isAffiliated){
            if(fromDate != null && toDate != null){
                if(!fromDate.isEmpty() && !toDate.isEmpty() && unitId != 0){
                    String query = "SELECT calendar_date, unit_id, site_code, move_time, description, "
                            + "rfid, description_cze, test_flow "
                            + "FROM "+ANALYST_SCHEMA+".work_duration_sites_period('"+fromDate+"', '"+toDate+"', "+unitId+");";
                    return stmt.executeQuery(query);
                } else{
                    throw new SQLException("Parameters dates or unit_id were not defined!");
                }
            }
            else if(fromDate != null && toDate == null){
                if(!fromDate.isEmpty() && unitId != 0){
                    String query = "SELECT calendar_date, unit_id, site_code, move_time, description, "
                            + "rfid, description_cze, test_flow "
                            + "FROM "+ANALYST_SCHEMA+".work_duration_sites_period('"+fromDate+"', ('"+fromDate+"'::date + '7 day'::interval)::date, "+unitId+");";
                    return stmt.executeQuery(query);
                }
                else{
                    throw new SQLException("Parameters from_date or unit_id were not defined!");
                }
            }
            else if(fromDate == null && toDate != null){
                if(!toDate.isEmpty() && unitId != 0){
                    String query = "SELECT calendar_date, unit_id, site_code, move_time, description, "
                            + "rfid, description_cze, test_flow "
                            + "FROM "+ANALYST_SCHEMA+".work_duration_sites_period(('"+toDate+"'::date - '7 day'::interval)::date, '"+toDate+"', "+unitId+");";
                    return stmt.executeQuery(query);
                } else{
                    throw new SQLException("Parameters to_date or unit_id were not defined!");
                }
            } else{
                throw new SQLException("Parameters from_date and to_date were not defined!");
            }
        } else{
            throw new SQLException("Specified unit does not belong in any group of the user!");
        }
    }
    
    /**
     * Method runs analysis for utilization of given tractor during given date 
     * @param userName id of user identifies available units
     * @param date date in which analysis should be run
     * @param unitId id of the unit for analysis
     * @return ResultSet with analysis result
     * @throws SQLException
     */
    public ResultSet getUnitUtilization(String userName, String date, long unitId) throws SQLException{
        boolean isAffiliated = checkUnitAffiliation2User(userName, unitId);
        
        if(isAffiliated){
            if(date != null){
                if(!date.isEmpty()){
                    String query = "SELECT calendar_date, unit_id, description, move_field, move_other, stand "
                        + "FROM "+ANALYST_SCHEMA+".tractor_utilization_period('"+date+"'::date, '"+date+"'::date, "+unitId+");";
                    return stmt.executeQuery(query);
                } else{
                    throw new SQLException("Parameter date was not defined!");
                }
            } else{
                throw new SQLException("Parameter toTime was not defined!");
            }
        } else{
            throw new SQLException("Specified unit does not belong in any group of the user!");
        }
    }
    
    /**
     * Method runs analysis for utilization of given tractor during given period 
     * @param userName id of user identifies available units
     * @param fromDate begin date of the period, if null it is substituted by toDate -7 days 
     * @param toDate end date of the period, if null it is substituted by fromDate + 7 days
     * @param unitId id of the unit for analysis
     * @return ResultSet with analysis result
     * @throws SQLException
     */
    public ResultSet getUnitUtilizationInPeriod(String userName, String fromDate, String toDate, long unitId) throws SQLException{
        boolean isAffiliated = checkUnitAffiliation2User(userName, unitId);
        
        if(isAffiliated){
            if(fromDate != null && toDate != null){
                if(!fromDate.isEmpty() && !toDate.isEmpty()){
                    String query = "SELECT calendar_date, unit_id, description, move_field, move_other, stand "
                        + "FROM "+ANALYST_SCHEMA+".tractor_utilization_period('"+fromDate+"'::date, '"+toDate+"'::date, "+unitId+");";
                    return stmt.executeQuery(query);
                } else{
                    throw new SQLException("Time parameters were not correct!");
                }
            }
            else if(fromDate != null && toDate == null){
                if(!fromDate.isEmpty()){
                    String query = "SELECT calendar_date, unit_id, description, move_field, move_other, stand "
                        + "FROM "+ANALYST_SCHEMA+".tractor_utilization_period('"+fromDate+"'::date, ('"+fromDate+"'::date + '7 day'::interval)::date, "+unitId+");";
                    return stmt.executeQuery(query);
                } else{
                    throw new SQLException("Parameter \"from\" was not correct!");
                }
            }
            else if(fromDate == null && toDate != null){
                if(!toDate.isEmpty()){
                    String query = "SELECT calendar_date, unit_id, description, move_field, move_other, stand "
                            + "FROM "+ANALYST_SCHEMA+".tractor_utilization_period(('"+toDate+"'::date - '7 day'::interval)::date, '"+toDate+"'::date, "+unitId+");";
                    return stmt.executeQuery(query);
                } else{
                    throw new SQLException("Parameter \"to\" was not correct!");
                }
            } else{
                throw new SQLException("Wrong combination of time parameters!");
            }
        } else{
            throw new SQLException("Specified unit does not belong in any group of the user!");
        }
    }

    /**
     * Method runs analysis Working Log for given unitId in given date
     * @param userName id of user to confirm that unitId is affiliated
     * @param date in which analysis should be run 
     * @param unitId id of the unit for analysis
     * @param minStopDuration parameter for analysis - minimal duration of stop as hh:mm:ss 
     * @param minWorkDuration parameter for analysis - minimal duration of work as hh:mm:ss
     * @param allowedTimeOutside parameter for analysis - maximal duration of spent time outside field as hh:mm:ss
     * @return ResultSet with analysis result
     * @throws SQLException
     */
    public ResultSet getTractorWorkDay(String userName, String date, long unitId, String minStopDuration, String minWorkDuration, String allowedTimeOutside) throws SQLException{
        if ((minStopDuration == null) || (minStopDuration != null && minStopDuration.isEmpty())){
            minStopDuration = "00:05:00";
        }
        if ((minWorkDuration == null) || (minWorkDuration != null && minWorkDuration.isEmpty())){
            minWorkDuration = "00:20:00";
        }
        if ((allowedTimeOutside == null) || (allowedTimeOutside != null && allowedTimeOutside.isEmpty())){
            allowedTimeOutside = "00:03:00";
        }
        
        boolean isAffiliated = checkUnitAffiliation2User(userName, unitId);
        if(isAffiliated){
            if(date != null){
                if(!date.isEmpty()){
                    String query = "SELECT * FROM "+ANALYST_SCHEMA+".tractor_work_day('"+date+"', "+unitId+", '"+minStopDuration+"', '"+minWorkDuration+"', '"+allowedTimeOutside+"');";
                    return stmt.executeQuery(query);
                } else{
                    throw new SQLException("Parameter date was not defined!");
                }
            } else{
                throw new SQLException("Parameter date was not defined!");
            }
        } else{
            throw new SQLException("Specified unit does not belong in any group of the user!");
        }
    }
    
    /**
     * Method gets result of analysis activities_field_overview() by specifying site by ID
     * @param siteId Id of site
     * @return ResultSet with result of analysis
     * @throws SQLException
     */
    public ResultSet getActivitiesFieldOverview(int siteId) throws SQLException{
        String query = "SELECT * FROM "+ANALYST_SCHEMA+".activities_field_overview("+siteId+");";
        return stmt.executeQuery(query);
    }

    /**
     * Method gets result of analysis activities_field_overview() by specifying plot by ID
     * @param plotId Id of plot
     * @return ResultSet with result of analysis
     * @throws SQLException
     */
    public ResultSet getActivitiesFieldOverviewByPlot(int plotId) throws SQLException{
        String query = "SELECT * FROM "+ANALYST_SCHEMA+".activities_field_overview("+plotId+");";
        return stmt.executeQuery(query);
    }

    /**
     * Method gets result of analysis activities_field_overview() by specifying plot by coordinates
     * @param point coordinates inside of a plot
     * @return ResultSet with result of analysis
     * @throws SQLException
     */
    public ResultSet getActivitiesFieldOverviewByPoint(String point) throws SQLException{
        int plotId = getPlotIdByPoint(point);
        return getActivitiesFieldOverview(plotId);
    }
    
    /**
     * Method gets result of analysis activities_field_overview_period() by specifying plot by ID
     * @param plotId - ID of plot
     * @param fromDate beginning of period, can be null
     * @param toDate end of period, can be null
     * @return ResultSet with result of analysis
     * @throws SQLException 
     */
    public ResultSet getActivitiesFieldOverviewPeriod(int plotId, String fromDate, String toDate) throws SQLException{
        String sql;
        if(fromDate == null && toDate != null){
            sql = "SELECT * FROM "+ANALYST_SCHEMA+".activities_field_overview_period("+plotId+", null, '"+toDate+"');";
        }
        else if(fromDate != null && toDate == null){
            sql = "SELECT * FROM "+ANALYST_SCHEMA+".activities_field_overview_period("+plotId+", '"+fromDate+"', null);";
        }
        else if(fromDate != null && toDate != null){
            sql = "SELECT * FROM "+ANALYST_SCHEMA+".activities_field_overview_period("+plotId+", '"+fromDate+"', '"+toDate+"');";
        }
        else{
            sql = "SELECT * FROM "+ANALYST_SCHEMA+".activities_field_overview_period("+plotId+", null, null);";
        }
        return stmt.executeQuery(sql);
    }
    
    /**
     * Method gets result of analysis activities_field_overview_period() by specifying plot by coordinates
     * @param point coordinates inside of a plot
     * @param fromDate beginning of period, can be null
     * @param toDate end of period, can be null
     * @return ResultSet with result of analysis
     * @throws SQLException
     */
    public ResultSet getActivitiesFieldOverviewPeriodByPoint(String point, String fromDate, String toDate) throws SQLException{
        int plotId = getPlotIdByPoint(point);
        return getActivitiesFieldOverviewPeriod(plotId, fromDate, toDate);
    }
    
    /**
     * 
     * @param plotId
     * @param unitId
     * @param fromDate
     * @param toDate
     * @param propertyName
     * @return
     * @throws SQLException
     */
    public ResultSet getManagementZonesOverviewByPlotId(int plotId, long unitId, String fromDate, String toDate, String propertyName) throws SQLException {
        String query; 
        if (fromDate != null && toDate == null){
            query = "SELECT * FROM "+ANALYST_SCHEMA+".management_zones_overview ("+plotId+", "+unitId+", '"+propertyName+"', '"+fromDate+"', NULL);";
        } else if (fromDate == null && toDate != null){
            query = "SELECT * FROM "+ANALYST_SCHEMA+".management_zones_overview ("+plotId+", "+unitId+", '"+propertyName+"', NULL, '"+toDate+"');";
        } else {
            query = "SELECT * FROM "+ANALYST_SCHEMA+".management_zones_overview ("+plotId+", "+unitId+", '"+propertyName+"', '"+fromDate+"', '"+toDate+"');";
        }
        return stmt.executeQuery(query);
    }
    
    /**
     * Method runs Management Zones Overview analysis
     * @param plotId - ID of plot
     * @param fromDate - beginning date
     * @param toDate - end date
     * @param propertyName - name of property to get overview
     * @return
     * @throws SQLException
     */
    public ResultSet getManagementZonesOverviewByPlotId2(int plotId, String fromDate, String toDate, String propertyName) throws SQLException {
        String query; 
        if (fromDate != null && toDate == null){
            query = "SELECT * FROM "+ANALYST_SCHEMA+".management_zones_overview2 ("+plotId+", '"+propertyName+"', '"+fromDate+"', NULL);";
        } else if (fromDate == null && toDate != null){
            query = "SELECT * FROM "+ANALYST_SCHEMA+".management_zones_overview2 ("+plotId+", '"+propertyName+"', NULL, '"+toDate+"');";
        } else {
            query = "SELECT * FROM "+ANALYST_SCHEMA+".management_zones_overview2 ("+plotId+", '"+propertyName+"', '"+fromDate+"', '"+toDate+"');";
        }
        return stmt.executeQuery(query);
    }
    
    /**
     * 
     * @param point
     * @param unitId
     * @param fromDate
     * @param toDate
     * @param propertyName
     * @return
     * @throws SQLException
     */
    public ResultSet getManagementZonesOverviewByPoint(String point, long unitId, String fromDate, String toDate, String propertyName) throws SQLException {
        int plotId = getPlotIdByPoint(point);
        return getManagementZonesOverviewByPlotId(plotId, unitId, fromDate, toDate, propertyName);
    }
    
    /**
     * 
     * @param point
     * @param fromDate
     * @param toDate
     * @param propertyName
     * @return
     * @throws SQLException
     */
    public ResultSet getManagementZonesOverviewByPoint2(String point, String fromDate, String toDate, String propertyName) throws SQLException {
        int plotId = getPlotIdByPoint(point);
        return getManagementZonesOverviewByPlotId2(plotId, fromDate, toDate, propertyName);
    }
    
    /**
     * Method runs analysis for Daily Field Activities (daily_field_activities)
     * @param plotId - ID of plot where activities took place
     * @param fromDate - beginning of time range
     * @param toDate - end of time range
     * @return ResultSet containing analysis result 
     * @throws SQLException 
     */
    public ResultSet getDailyFieldActivities(int plotId, String fromDate, String toDate) throws SQLException {
        String query;
        if(fromDate == null && toDate == null){
            query = "SELECT * FROM "+ANALYST_SCHEMA+".daily_field_activities("+plotId+", NULL, NULL);";
        } else if(fromDate != null && toDate == null){
            query = "SELECT * FROM "+ANALYST_SCHEMA+".daily_field_activities("+plotId+", '"+fromDate+"', NULL);";
        } else if(fromDate == null && toDate != null){
            query = "SELECT * FROM "+ANALYST_SCHEMA+".daily_field_activities("+plotId+", NULL, '"+toDate+"');";
        } else {
            query = "SELECT * FROM "+ANALYST_SCHEMA+".daily_field_activities("+plotId+", '"+fromDate+"', '"+toDate+"');";
        }
        return stmt.executeQuery(query);
    }
    
    /**
     * Method runs analysis for Daily Field Activities (daily_field_activities) by giving coordinates
     * @param point - coordinates inside of a plot
     * @param fromDate - beginning of time range
     * @param toDate - end of time range
     * @return ResultSet containing analysis result 
     * @throws SQLException 
     */
    public ResultSet getDailyFieldActivitiesByPoint(String point, String fromDate, String toDate) throws SQLException {
        int plotId = getPlotIdByPoint(point);
        return getDailyFieldActivities(plotId, fromDate, toDate);
    }
    
    /**
     * Method gets result of analysis field_overview_fuel_summary() by specifying site by ID
     * @param siteId ID of site
     * @param fromDate beginning of period, can be null
     * @param toDate end of period, can be null
     * @return ResultSet with result of analysis
     * @throws SQLException
     */
    public ResultSet getFieldFuelSummary(int siteId, String fromDate, String toDate) throws SQLException{
        String sql;
        if(fromDate == null && toDate != null){
            sql = "SELECT * FROM "+ANALYST_SCHEMA+".field_overview_fuel_summary("+siteId+", null, '"+toDate+"');";
        }
        else if(fromDate != null && toDate == null){
            sql = "SELECT * FROM "+ANALYST_SCHEMA+".field_overview_fuel_summary("+siteId+", '"+fromDate+"', null);";
        }
        else if(fromDate != null && toDate != null){
            sql = "SELECT * FROM "+ANALYST_SCHEMA+".field_overview_fuel_summary("+siteId+", '"+fromDate+"', '"+toDate+"');";
        }
        else{
            sql = "SELECT * FROM "+ANALYST_SCHEMA+".field_overview_fuel_summary("+siteId+", null, null);";
        }
        return stmt.executeQuery(sql);
    }
    
    /**
     * Method gets result of analysis field_overview_fuel_summary() by specifying site by coordinates
     * @param point coordinates inside of site
     * @param fromDate beginning of period, can be null
     * @param toDate end of period, can be null
     * @return ResultSet with result of analysis
     * @throws SQLException
     */
    public ResultSet getFieldFuelSummaryByPoint(String point, String fromDate, String toDate) throws SQLException{
        int siteId = getSiteIdByPoint(point);
        return getFieldFuelSummary(siteId, fromDate, toDate);
    }
    
    /**
     * Method checks if specified unit was active during given time period
     * @param unit_id ID of unit
     * @param fromDate beginning date of the period, can be null
     * @param toDate end date of the period, can be null
     * @return List of days with boolean true if unit was active, or false if not
     * @throws SQLException 
     */
    public ResultSet isTractorActiveInPeriod(String userName, long unitId, String fromDate, String toDate) throws SQLException {
        boolean isAffiliated = checkUnitAffiliation2User(userName, unitId);
        if(isAffiliated){
            String sql = "";
            if(fromDate != null && toDate != null){
                sql = "SELECT * FROM "+ANALYST_SCHEMA+".tractor_active_period("+unitId+", '"+fromDate+"', '"+toDate+"');";
            }
            else if(fromDate != null && toDate == null){
                sql = "SELECT * FROM "+ANALYST_SCHEMA+".tractor_active_period("+unitId+", '"+fromDate+"', ('"+fromDate+"'::date + '1 day'::interval)::date);";
            }
            else if(fromDate == null && toDate != null){
                sql = "SELECT * FROM "+ANALYST_SCHEMA+".tractor_active_period("+unitId+", ('"+toDate+"'::date - '1 day'::interval)::date, '"+toDate+"');";
            }
            else{
                throw new SQLException("Any parameters from_date or to_date were not defined!");
            }
            return stmt.executeQuery(sql);
        }else{
            throw new SQLException("Specified unit does not belong in any group of the user!");
        }
    }
    
    /**
     * Method gets array of active tractors during given period
     * @param userName of user
     * @param fromDate begin date of period
     * @param toDate end date of period, if it is null, then end is now()
     * @return List<Long> with unit_ids of active tractors
     * @throws SQLException if problem occurs during execution of SQL
     * @throws NoItemFoundException if user doesn't have groupId, should never happen
     */
    public List<Long> getActiveTractorsDuringPeriod(String userName, String fromDate, String toDate) throws SQLException, NoItemFoundException{
        int groupId = UserUtil.getUserGroupId(userName);
        return getActiveTractorsDuringPeriod(groupId, fromDate, toDate);
    }
    
    /**
     * Method gets array of active tractors during given period
     * @param groupId id of group
     * @param fromDate begin date of period
     * @param toDate end date of period, if it is null, then end is now()
     * @return List<Long> with unit_ids of active tractors
     * @throws SQLException  if problem occurs during execution of SQL
     */
    public List<Long> getActiveTractorsDuringPeriod(int groupId, String fromDate, String toDate) throws SQLException{
        String sql = "";
        if(fromDate != null && toDate != null){
            sql = "SELECT * FROM "+ANALYST_SCHEMA+".all_active_tractors_period("+groupId+", '"+fromDate+"', '"+toDate+"');";
        }
        else if(fromDate != null && toDate == null){
            sql = "SELECT * FROM "+ANALYST_SCHEMA+".all_active_tractors_period("+groupId+", '"+fromDate+"', (select now())::date);";
        }
        else{
            throw new SQLException("Parameter fromDate can't be null!");
        }
        ResultSet res = stmt.executeQuery(sql);
        List<Long> idsList = new ArrayList<Long>();
        if(res.next()){
            Array ids = res.getArray(1);
            if(ids != null){
                idsList = Arrays.asList((Long[]) ids.getArray());
            }
            return idsList;
        }
        else{
            return idsList;
        }
    }
    
    /**
     * Method selects all management zones inside of given plot 
     * in GeoJSON format
     * @param plotId - ID of plot
     * @return Management zones in GeoJSON format as JSONObject
     * @throws SQLException
     */
    public JSONObject getManagementZonesGeoJSON(int plotId) throws SQLException{
        String query = "SELECT management_zone_id, code, code_space, code_version, valid_from, valid_to,"
                + " begin_life_span_version, end_life_span_zone, notes, plot_id,"
                + " st_asgeojson(st_transform(geometry, 3857), 10, 2) FROM foodie_model_test.management_zone"
                + " WHERE plot_id = "+plotId+";";
        ResultSet res = SQLExecutor.getInstance().executeQuery(query);
        if(res != null){
            LinkedList<JSONObject> mZonesList = new LinkedList<>();
            while(res.next()){
                // feature
                JSONObject feature = new JSONObject();
                feature.put("type", "Feature");
                feature.put("geometry", res.getString("st_asgeojson"));
                // properties
                JSONObject properties = new JSONObject();
                properties.put("management_zone_id", res.getInt("management_zone_id"));
                properties.put("code", res.getString("code"));
                properties.put("code_space", res.getString("code_space"));
                properties.put("code_version", res.getString("code_version"));
                properties.put("valid_from", res.getString("valid_from"));
                properties.put("valid_to", res.getString("valid_to"));
                properties.put("begin_life_span_version", res.getString("begin_life_span_version"));
                properties.put("end_life_span_zone", res.getString("end_life_span_zone"));
                properties.put("notes", res.getString("notes"));
                properties.put("plot_id", res.getInt("plot_id"));
                feature.put("properties", properties);
                
                mZonesList.add(feature);
            }
            JSONObject featureCollection = new JSONObject();
            // Features
            JSONArray featureList = new JSONArray();
            featureList.addAll(mZonesList);
            // FeatureCollection
            featureCollection.put("type", "FeatureCollection");
            featureCollection.put("features", featureList);
            
            return featureCollection;
        }
        else{
            return new JSONObject();
        }
    }
    
    /**
     * Method selects management zone according to given zoneId 
     * in GeoJSON format
     * @param zoneId - ID of zone
     * @return Management zone in GeoJSON format as JSONObject
     * @throws SQLException
     * @throws NoItemFoundException 
     */
    public JSONObject getManagementZoneGeoJSON(int zoneId) throws SQLException, NoItemFoundException{
        String query = "SELECT management_zone_id, code, code_space, code_version, valid_from, valid_to,"
                + " begin_life_span_version, end_life_span_zone, notes, plot_id,"
                + " st_asgeojson(st_transform(geometry, 3857), 8, 2) FROM foodie_model_test.management_zone"
                + " WHERE management_zone_id = "+zoneId+";";
        ResultSet res = SQLExecutor.getInstance().executeQuery(query);
        if(res != null){
            if(res.next()){
                // feature
                JSONObject feature = new JSONObject();
                feature.put("type", "Feature");
                feature.put("geometry", res.getString("st_asgeojson"));
                // properties
                JSONObject properties = new JSONObject();
                properties.put("management_zone_id", res.getInt("management_zone_id"));
                properties.put("code", res.getString("code"));
                properties.put("code_space", res.getString("code_space"));
                properties.put("code_version", res.getString("code_version"));
                properties.put("valid_from", res.getString("valid_from"));
                properties.put("valid_to", res.getString("valid_to"));
                properties.put("begin_life_span_version", res.getString("begin_life_span_version"));
                properties.put("end_life_span_zone", res.getString("end_life_span_zone"));
                properties.put("notes", res.getString("notes"));
                properties.put("plot_id", res.getInt("plot_id"));
                feature.put("properties", properties);
                
                return feature;
            } else{
                throw new NoItemFoundException("Any management zone wasn't found!");
            }
        } else{
            throw new NoItemFoundException("Any management zone wasn't found!");
        }
    }
    
    /**
     * Method selects management zone according to given zoneId 
     * in GeoJSON format
     * @param zoneId - ID of zone
     * @return Management zone in GeoJSON format as JSONObject
     * @throws SQLException
     * @throws NoItemFoundException 
     */
    public JSONObject getManagementZoneGeoJSONByPoint(String point) throws SQLException, NoItemFoundException{
        String[] coords = point.split(",");
        double x = Double.parseDouble(coords[0]);
        double y = Double.parseDouble(coords[1]);
        
        String query = "SELECT management_zone_id, code, code_space, code_version, valid_from, valid_to,"
                    + " begin_life_span_version, end_life_span_zone, notes, plot_id,"
                    + " st_asgeojson(st_transform(geometry, 3857), 8, 2)"
                    + " FROM foodie_model_test.management_zone"
                    + " WHERE ST_Intersects(geometry, ST_Transform(ST_SetSRID(ST_Point("+x+", "+y+"), 3857),"
                            + " (SELECT distinct ST_SRID(geometry) as srid FROM foodie_model_test.management_zone)));";
        ResultSet res = SQLExecutor.getInstance().executeQuery(query);
        if(res != null){
            if(res.next()){
                // feature
                JSONObject feature = new JSONObject();
                feature.put("type", "Feature");
                feature.put("geometry", res.getString("st_asgeojson"));
                // properties
                JSONObject properties = new JSONObject();
                properties.put("management_zone_id", res.getInt("management_zone_id"));
                properties.put("code", res.getString("code"));
                properties.put("code_space", res.getString("code_space"));
                properties.put("code_version", res.getString("code_version"));
                properties.put("valid_from", res.getString("valid_from"));
                properties.put("valid_to", res.getString("valid_to"));
                properties.put("begin_life_span_version", res.getString("begin_life_span_version"));
                properties.put("end_life_span_zone", res.getString("end_life_span_zone"));
                properties.put("notes", res.getString("notes"));
                properties.put("plot_id", res.getInt("plot_id"));
                feature.put("properties", properties);
                
                return feature;
            } else{
                throw new NoItemFoundException("Any management zone wasn't found!");
            }
        } else{
            throw new NoItemFoundException("Any management zone wasn't found!");
        }
    }
    
    /**
     * Method provides selection of all plots in GeoJSON format
     * @return FeatureCollection of plots
     */
    public JSONObject getPlotsAsGeoJSON() throws SQLException{
        String query = "SELECT plot_id, code, code_space, code_version, valid_from, valid_to,"
                + " begin_life_span_version, end_life_span_version, description, origin_type_value_id,"
                + " site_id, st_asgeojson(st_transform(geometry, 3857), 10, 2)"
                + " FROM foodie_model_test.plot;";
        ResultSet res = SQLExecutor.getInstance().executeQuery(query);
        if(res != null){
            LinkedList<JSONObject> plotsList = new LinkedList<JSONObject>();
            while(res.next()){
                // feature
                JSONObject feature = new JSONObject();
                feature.put("type", "Feature");
                feature.put("geometry", res.getString("st_asgeojson"));
                // properties
                JSONObject properties = new JSONObject();
                properties.put("plot_id", res.getInt("plot_id"));
                properties.put("code", res.getString("code"));
                properties.put("code_space", res.getString("code_space"));
                properties.put("code_version", res.getString("code_version"));
                properties.put("valid_from", res.getString("valid_from"));
                properties.put("valid_to", res.getString("valid_to"));
                properties.put("begin_life_span_version", res.getString("begin_life_span_version"));
                properties.put("end_life_span_version", res.getString("end_life_span_version"));
                properties.put("description", res.getString("description"));
                properties.put("origin_type_value_id", res.getInt("origin_type_value_id"));
                properties.put("site_id", res.getInt("site_id"));
                feature.put("properties", properties);
                
                plotsList.add(feature);
            }
            JSONObject featureCollection = new JSONObject();
            // Features
            JSONArray featureList = new JSONArray();
            featureList.addAll(plotsList);
            // FeatureCollection
            featureCollection.put("type", "FeatureCollection");
            featureCollection.put("features", featureList);
            
            return featureCollection;
        }
        else{
            return new JSONObject();
        }
    }
    
    /**
     * Method confirms that given unit belongs to given user
     * by selecting affiliated units from database
     * @param userName name of the user
     * @param unitId id of unit
     * @return true if given unit is in any group that user is connected to 
     * @throws SQLException
     */
    public boolean checkUnitAffiliation2User(String userName, long unitId) throws SQLException{
        UserUtil uUtil = new UserUtil();
        boolean confirmed = false;
        List<Unit> unitsOfUser = uUtil.getUnitsByUser(userName);
        Iterator<Unit> unitsIter = unitsOfUser.iterator();
        while (unitsIter.hasNext() && !confirmed){
            if (unitsIter.next().getUnitId() == unitId){
                confirmed = true;
            }
        }
        return confirmed;
    }
    
    /**
     * Method finds site_id by giving point in map
     * @param point coordinates in form "x,y"
     * @return site_id from DB or 0 if identification failed
     * @throws SQLException
     */
    private int getSiteIdByPoint(String point) throws SQLException{
        int siteId = 0;
        try {
            String[] coords = point.split(",");
            double x = Double.parseDouble(coords[0]);
            double y = Double.parseDouble(coords[1]);
            String query = "SELECT site_id FROM "+FOODIE_SCHEMA+".site_4326 WHERE ST_Intersects(the_geom, ST_Transform(ST_SetSRID(ST_Point("+x+", "+y+"),3857), 4326));";
            ResultSet res = stmt.executeQuery(query);
            if(res != null){
                while (res.next()){
                    siteId = res.getInt("site_id");
                }
            }
        } catch (SQLException e) {
            throw new SQLException(e.getMessage());
        } catch(PatternSyntaxException e){
            throw new SQLException("Input point doesn't contain mandatory values!");
        } catch(NumberFormatException e){
            throw new SQLException("Input point doesn't contain numbers!");
        }
        return siteId;
    }
    
    /**
     * "NOT FINAL VERSION YET"
     * Method finds plot_id by giving point in map
     * @param point coordinates in form "x,y"
     * @return plot_id from DB or 0 if identification failed
     * @throws SQLException
     */
    private int getPlotIdByPoint(String point) throws SQLException{
        int plotId = 0;
        try {
            String[] coords = point.split(",");
            double x = Double.parseDouble(coords[0]);
            double y = Double.parseDouble(coords[1]);
            
            String query = "SELECT plot_id FROM foodie_model_test.plot"
                    + " WHERE ST_Intersects(geometry, ST_Transform(ST_SetSRID(ST_Point("+x+", "+y+"),3857),"
                        + "(SELECT distinct ST_SRID(geometry) as srid FROM foodie_model_test.plot)));";
            ResultSet res = stmt.executeQuery(query);
            if(res != null){
                while (res.next()){
                    plotId = res.getInt("plot_id");
                }
            }
        } catch (SQLException e) {
            throw new SQLException(e.getMessage());
        } catch(PatternSyntaxException e){
            throw new SQLException("Input point doesn't contain mandatory values!");
        } catch(NumberFormatException e){
            throw new SQLException("Input point doesn't contain numbers!");
        }
        return plotId;
    }
}