package cz.hsrs.servlet.feeder;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URL;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cz.hsrs.db.DatabaseFeedOperation;
import cz.hsrs.db.model.AlertEvent;
import cz.hsrs.db.pool.SQLExecutor;
import cz.hsrs.db.util.AlertUtil;
import cz.hsrs.db.util.ServerUtil;

import static cz.hsrs.servlet.feeder.ServiceParameters.*;


public class FeederServlet extends HttpServlet {
    static final long serialVersionUID = 1L;

    private static final Logger logger = Logger.getLogger(FeederServlet.class.getSimpleName());

    private static final List<URL> BACKUP_URLS = new LinkedList<>();

    @Override
    public void init() throws ServletException {
        String propFile = getServletContext().getRealPath("WEB-INF/database.properties");
        Properties prop = new Properties();
        try {
            prop.load(new FileInputStream(propFile));
            SQLExecutor.setProperties(prop);
        } catch (Exception e) {
            logger.log(Level.SEVERE, e.getMessage(), e);
        }

        /* Initialize logging properties */
        try {
            FileInputStream fstrem = new FileInputStream(new File(getServletContext().getRealPath("WEB-INF/logging.properties")));
            LogManager.getLogManager().readConfiguration(fstrem);
            logger.log(Level.INFO, "Logging inialized Succesefully!");
        } catch (SecurityException | IOException e1) {
            logger.log(Level.SEVERE, e1.getMessage());
        }

        SQLExecutor.setProperties(prop);
        super.init();
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        /* Forward request to other servers */
        try {
            ServerUtil ut = new ServerUtil();
            ut.callServers(BACKUP_URLS, request.getQueryString());
        } catch (Exception e) {
            logger.log(Level.WARNING, e.getMessage() + " query: " + request.getQueryString(), e);
        }

        String requestOperation = request.getParameter(ServiceParameters.OPERATION);

        PrintWriter out = response.getWriter();
        try {
            switch (requestOperation) {
                case INSERT_OBSERVATION: { // Insert observation request
                    out.print(insObservation(request));
                }
                break;
                case INSERT_POSITION: { // Insert position request
                    out.print(insPosition(request));
                }
                break;
                case INSERT_ALERT_EVENT: { // Insert new alert event request
                    out.print(insAlertEvent(request)); // depends on existing older event!!!
                }
                break;
                case SOLVING_ALERT_EVENT: { // Set solving parameter of alert event request
                    out.print(insSolvingAlertEvent(request));
                }
                break;
                case "RESET": { // Set solving parameter of alert event request
                    SQLExecutor.close();
                    init();
                    response.sendRedirect("./monitor.jsp");
                }
                case "SLEEP": {
                    try {
                        Thread.sleep(20000);
                        out.print("uzzzz....");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        } catch (Exception e) {
            logger.log(Level.WARNING, e.getMessage() + " query: " + request.getQueryString(), e);
            out.print(false);
        }
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        /* Insert position via insert.jsp page form */
        if (request.getParameter(ServiceParameters.OPERATION).equals(INSERT_POSITION)) {
            PrintWriter out = response.getWriter();
            try {
                insPosition(request);
                out.print("<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"><title>Insert title here</title></head>");
                out.print(true);
                out.print("<div><a href=\"./vypis.jsp?unit_id="+request.getParameter(ServiceParameters.UNIT_ID)+"\">Back to the list of graphs</a></div>");
                out.print("</body></html>");
            } catch (Exception e) {
                out.print(false);
            }
        }
    }


    private static Date parse(String dateString) throws ParseException {
        final SimpleDateFormat formaterT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
        final SimpleDateFormat formater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssZ");

        if(dateString.contains("T")){
            try {
                return formaterT.parse(dateString);
            } catch (ParseException e) {
                try{
                    return formaterT.parse(dateString+getTimeZone());
                } catch (ParseException e1){
                    throw new ParseException(e1.getMessage(), 1);
                }
            }
        }
        else{
            try {
                return formater.parse(dateString);
            } catch (ParseException e) {
                try{
                    return formater.parse(dateString+getTimeZone());
                } catch (ParseException e1){
                    throw new ParseException(e1.getMessage(), 1);
                }
            }
        }
    }

    private static String getTimeZone(){
        Calendar cal = Calendar.getInstance();
        int z = ((cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET)) / (60 * 1000)/60);
        return "+0"+String.valueOf(z).subSequence(0, 1)+"00";
    }

    private static boolean insObservation(HttpServletRequest request) throws SQLException, ParseException {
        Double value = Double.parseDouble(request.getParameter(ServiceParameters.VALUE));
        String time = request.getParameter(ServiceParameters.DATE);
        long unit_id = Long.parseLong(request.getParameter(ServiceParameters.UNIT_ID));
        long sensor_id = Long.parseLong(request.getParameter(ServiceParameters.SENSOR_ID));

        Date date = parse(time);// "2008-01-02 12:00:00");    
        return DatabaseFeedOperation.insertObservation(date, unit_id, sensor_id, value);
    }

    private static boolean insPosition(HttpServletRequest request) throws SQLException {
        boolean inserted;
        /* Mandatory lat and lon */
        double lat = Double.parseDouble(request.getParameter(ServiceParameters.LAT));
        double lon = Double.parseDouble(request.getParameter(ServiceParameters.LON));

        /* Optional alt */
        double alt = Double.NaN;
        if (request.getParameter(ServiceParameters.ALT) != null && !request.getParameter(ServiceParameters.ALT).isEmpty()) {
            alt = Double.parseDouble(request.getParameter(ServiceParameters.ALT));
        }

        /* Optional speed */
        double speed = Double.NaN;
        if (request.getParameter(ServiceParameters.SPEED) != null && !request.getParameter(ServiceParameters.SPEED).isEmpty()) {
            speed = Double.parseDouble(request.getParameter(ServiceParameters.SPEED));
        }

        /* Mandatory unit_id */
        long unit_id = Long.parseLong(request.getParameter(ServiceParameters.UNIT_ID));

        /* Mandatory date */
        String date = request.getParameter(ServiceParameters.DATE);
        if(date.contains("T")){
            date = date.replace("T", " ");
        }
        Date time;
        try {
            time = parse(date);
        } catch (ParseException e) {
            throw new SQLException(e);
        }

        /* DOP is voluntary parameter */
        if (request.getParameter(ServiceParameters.DOP) != null && !request.getParameter(ServiceParameters.DOP).isEmpty()) {
            double dop = Double.parseDouble(request.getParameter(ServiceParameters.DOP));
            inserted = DatabaseFeedOperation.insertPosition(unit_id, lat, lon, alt, dop, time, speed);
        } else {
            inserted = DatabaseFeedOperation.insertPosition(unit_id, lat, lon, alt, time, speed);
        }
        return inserted;
    }

    private static boolean insAlertEvent(HttpServletRequest request) throws Exception {
        String time = request.getParameter(ServiceParameters.DATE);
        long unit_id = Long.parseLong(request.getParameter(ServiceParameters.UNIT_ID));
        int alert_id = Integer.parseInt(request.getParameter(ServiceParameters.ALERT_ID));

        AlertUtil aUtil = new AlertUtil();
        List<AlertEvent> eventList = aUtil.getUnsolvingAlertEvent(unit_id, alert_id);

        boolean notOlderEvent = eventList.isEmpty();
        if (notOlderEvent) {
            Date date = parse(time);// "2008-01-02 12:00:00");
            DatabaseFeedOperation.insertAlertEvent(date, unit_id, alert_id);
        }
        return notOlderEvent;
    }

    private static boolean insSolvingAlertEvent(HttpServletRequest request) throws Exception {
        int event_id = Integer.parseInt(request.getParameter(ServiceParameters.ALERT_EVENT_ID));
        DatabaseFeedOperation.solvingAlertEvent(event_id);
        return true;
    }
}