package jnet.server;

import java.sql.Timestamp;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import jnet.lib.LogFile;
import jnet.lib.object.Event;
import jnet.lib.object.Map;
import jnet.lib.object.MapObject;
import jnet.lib.object.ObjectType;
import jnet.lib.object.SnmpProbe;
import jnet.lib.object.SnmpProfile;
import jnet.lib.object.User;

public class Database {

    private static Connection connection = null;
    private static String host;
    private static String port;
    private static String database;
    private static String user;
    private static String password;


    public void connect(String host, String port, String database, String user, String password) {
        this.host = host;
        this.port = port;
        this.database = database;
        this.user = user;
        this.password = password;

        connection = connect();
    }

    private static Connection connect() {
        try {
            LogFile.printInfo("Connecting to a selected database ...");
            Connection connection = DriverManager.getConnection("jdbc:mysql://" + host + ":" + port + "/" + database + ""
                    + "?useUnicode=true"
                    + "&characterEncoding=UTF-8"
                    + "&autoReconnect=true"
                    + "&serverTimezone=UTC", user, password);
            LogFile.printInfo(" successfully");
            return connection;
        } catch (SQLException ex) {
            LogFile.printErr("MySQL connection exception: " + ex.getMessage());
            LogFile.printErr("MySQL host: " + host + " port: " + port + " database: " + database + " user: " + user);
            System.exit(0);
        }
        return null;
    }

    /**
     * vrati aktualni spojeni
     *
     * @return
     * @throws SQLException
     */
    public static Connection getCurrentConnection() throws SQLException {
        if (connection != null || connection.isClosed()) {
            return connection;
        } else {
            connection = connect();
        }
        return null;
    }

    /**
     * insert
     *
     * @param sql dotaz
     * @return pri uspesnem vlozeni vrati posledni vlozeny id
     */
    public static int insert(String sqlQuery) {
        long id = -1;
        try {
            Statement statement = getCurrentConnection().createStatement();
            statement.executeUpdate(sqlQuery, Statement.RETURN_GENERATED_KEYS);

            PreparedStatement getLastInsertId = getCurrentConnection().prepareStatement("SELECT LAST_INSERT_ID()");
            ResultSet rs = getLastInsertId.executeQuery();
            if (rs.next()) {
                id = rs.getLong("last_insert_id()");
            }
        } catch (SQLException ex) {
            LogFile.printErr("MySQL exception: " + ex.getMessage() + " SQL: " + sqlQuery);
        }
        return (int) id;
    }

    /**
     * update
     *
     * @param sql sql dotaz
     */
    public static void update(String sql) {
        try {
            Statement statement = getCurrentConnection().createStatement();
            statement.executeUpdate(sql);
            statement.close();
        } catch (SQLException ex) {
            LogFile.printErr("MySQL exception: " + ex.getMessage() + " SQL: " + sql);
        }
    }

    /**
     * smaze zaznam z tabulky
     *
     * @param table nazev tabulky
     * @param id id rádku
     * @return
     */
    static boolean remove(String table, int id) {
        try {
            String sql = "DELETE FROM " + table + " WHERE id = ?";

            PreparedStatement stmt = getCurrentConnection().prepareStatement(sql);

            stmt.setInt(1, id);

            int affectedRows = stmt.executeUpdate();
            if (affectedRows > 0) {
                return true;
            } else {
                return false;
            }
        } catch (SQLException ex) {
            LogFile.printErr("Database error: " + ex.getMessage());
        }
        return false;
    }

    /**
     * vytvoří tabulku
     *
     * @param sql string
     */
    public static void createTable(String sql) {
        try {
            Statement statement = getCurrentConnection().createStatement();
            statement.executeUpdate(sql);
        } catch (SQLException e) {
            System.out.println("An error has occured on Table Creation: " + e.getMessage());
        }
    }

    /**
     * vráti seznam vsech map
     *
     * @return ArrayList<Map>
     */
    public static ArrayList<Map> getMapList() {
        String sql = "SELECT * FROM maps";
        ArrayList<Map> list = new ArrayList<Map>();
        try {
            Statement statement = getCurrentConnection().createStatement();
            ResultSet rs = statement.executeQuery(sql);
            while (rs.next()) {
                Map m = new Map(
                        rs.getInt("id"),
                        rs.getString("name"),
                        rs.getBoolean("locked"),
                        getMapObject(rs.getInt("id")));
                list.add(m);
            }
            rs.close();
        } catch (SQLException ex) {
            LogFile.printErr("MySQL exception: " + ex.getMessage() + "query: " + sql);
        }
        return list;
    }

    /**
     * vrati seznam objektu odpovidajici mape
     *
     * @param mapId id mapy
     * @return ArrayList<MapObject>
     */
    public static ArrayList<MapObject> getMapObject(int mapId) {
        ArrayList<MapObject> list = new ArrayList<>();
        String sql = "SELECT * FROM objects WHERE map = " + mapId;
        try {
            Statement statement = getCurrentConnection().createStatement();
            ResultSet r = statement.executeQuery(sql);
            while (r.next()) {
                int objId = r.getInt("id");
                list.add(new MapObject(
                        objId,
                        r.getString("name"),
                        r.getInt("map"),
                        r.getInt("objectType"),
                        r.getString("ip"),
                        r.getInt("x"),
                        r.getInt("y"),
                        r.getString("user"),
                        r.getString("password"),
                        r.getString("description"),
                        r.getString("location"),
                        r.getInt("snmpProfile"),
                        getSnmpProbe(objId),
                        (boolean) (r.getInt("active") == 1),
                        (r.getInt("winbox") == 1),
                        r.getString("winboxPort"),
                        (boolean) (r.getInt("ssh") == 1),
                        r.getString("sshPort"),
                        (boolean) (r.getInt("web") == 1),
                        r.getString("webPort"),
                        r.getInt("webVerze"),
                        (boolean) (r.getInt("telnet") == 1),
                        r.getString("telnetPort"),
                        (boolean) (r.getInt("sms") == 1),
                        r.getString("smsPort"),
                        r.getInt("smsVerze")
                ));
            }
            r.close();
            statement.close();
        } catch (SQLException ex) {
            LogFile.printErr("MySQL exception: " + ex.getMessage() + "query: " + sql);
        }
        return list;
    }
    
    /**
     * vrati seznam objektu
     *
     * @return ArrayList<MapObject>
     */
    public static ArrayList<MapObject> getAllMapObject() {
        ArrayList<MapObject> list = new ArrayList<>();
        String sql = "SELECT * FROM objects";
        try {
            Statement statement = getCurrentConnection().createStatement();
            ResultSet r = statement.executeQuery(sql);
            while (r.next()) {
                int objId = r.getInt("id");
                list.add(new MapObject(
                        objId,
                        r.getString("name"),
                        r.getInt("map"),
                        r.getInt("objectType"),
                        r.getString("ip"),
                        r.getInt("x"),
                        r.getInt("y"),
                        r.getString("user"),
                        r.getString("password"),
                        r.getString("description"),
                        r.getString("location"),
                        r.getInt("snmpProfile"),
                        getSnmpProbe(objId),
                        (boolean) (r.getInt("active") == 1),
                        (r.getInt("winbox") == 1),
                        r.getString("winboxPort"),
                        (boolean) (r.getInt("ssh") == 1),
                        r.getString("sshPort"),
                        (boolean) (r.getInt("web") == 1),
                        r.getString("webPort"),
                        r.getInt("webVerze"),
                        (boolean) (r.getInt("telnet") == 1),
                        r.getString("telnetPort"),
                        (boolean) (r.getInt("sms") == 1),
                        r.getString("smsPort"),
                        r.getInt("smsVerze")
                ));
            }
            r.close();
            statement.close();
        } catch (SQLException ex) {
            LogFile.printErr("MySQL exception: " + ex.getMessage() + "query: " + sql);
        }
        return list;
    }

    /**
     * vrati seznam SNMP probe
     *
     * @return ArrayList<SnmpProbe>
     */
    public static ArrayList<SnmpProbe> getSnmpProbe(int objId) {
        ArrayList<SnmpProbe> list = new ArrayList<>();
        String sql = "SELECT * FROM snmpProbe WHERE object = " + objId;
        try {
            Statement statement = getCurrentConnection().createStatement();
            ResultSet r = statement.executeQuery(sql);
            while (r.next()) {
                list.add(new SnmpProbe(
                        r.getInt("id"),
                        r.getString("name"),
                        r.getInt("object")
                ));
            }
            r.close();
            statement.close();
        } catch (SQLException ex) {
            LogFile.printErr("MySQL exception: " + ex.getMessage() + "query: " + sql);
        }
        return list;
    }

    /**
     * vrati informace o uzivateli podle jmena
     *
     * @param username
     * @return User
     */
    static User getUserInfo(String username) {
        User u = null;
        try {
            Statement statement = getCurrentConnection().createStatement();
            ResultSet r_con = statement.executeQuery("SELECT * FROM users WHERE username = '" + username + "'");
            while (r_con.next()) {
                u = new User(r_con.getInt("id"),
                        username,
                        r_con.getString("password"),
                        r_con.getBoolean("add_map"),
                        r_con.getBoolean("remove_map"),
                        r_con.getBoolean("edit_map"),
                        r_con.getBoolean("add_object"),
                        r_con.getBoolean("remove_object"),
                        r_con.getBoolean("edit_object"),
                        r_con.getBoolean("edit_server")
                );
            }
            r_con.close();
        } catch (SQLException ex) {
            LogFile.printErr("MySQL exception: " + ex.getMessage());

        }
        return u;
    }

    /**
     * vrati seznam object type
     *
     * @return ArrayList<ObjectType>
     */
    static ArrayList<ObjectType> getObjectType() {
        ArrayList<ObjectType> objectType = new ArrayList<>();
        try {
            Statement statement = getCurrentConnection().createStatement();
            ResultSet so = statement.executeQuery("SELECT * FROM objectType");
            while (so.next()) {
                objectType.add(new ObjectType(
                        so.getInt("id"),
                        so.getString("name"),
                        so.getString("image")
                ));
            }
            so.close();
        } catch (SQLException ex) {
            System.out.println("MySQL exception: " + ex.getMessage());
        }
        return objectType;
    }

    /**
     * vrati seznam uzivatelu
     *
     * @return ArrayList<User>
     */
    static ArrayList<User> getUsers() {
        ArrayList<User> users = new ArrayList<>();
        try {
            Statement statement = getCurrentConnection().createStatement();
            ResultSet su = statement.executeQuery("SELECT * FROM users");
            while (su.next()) {
                users.add(new User(
                        su.getInt("id"),
                        su.getString("username"),
                        "",
                        su.getBoolean("add_map"),
                        su.getBoolean("remove_map"),
                        su.getBoolean("edit_map"),
                        su.getBoolean("add_object"),
                        su.getBoolean("remove_object"),
                        su.getBoolean("edit_object"),
                        su.getBoolean("edit_server")
                ));
            }
            su.close();
        } catch (SQLException ex) {
            System.out.println("MySQL exception: " + ex.getMessage());
        }
        return users;
    }

    /**
     * vrati snmp profily
     *
     * @return ArrayList<SnmpProfile>
     */
    static ArrayList<SnmpProfile> getSnmpProfile() {
        ArrayList<SnmpProfile> snmpProfile = new ArrayList<>();
        try {
            Statement statement = getCurrentConnection().createStatement();
            ResultSet rs = statement.executeQuery("SELECT * FROM snmpProfile");
            while (rs.next()) {
                snmpProfile.add(new SnmpProfile(
                        rs.getInt("id"),
                        rs.getString("name"),
                        rs.getInt("version"),
                        rs.getString("port"),
                        rs.getString("communityRead"),
                        rs.getString("communityRead")
                ));
            }
            rs.close();
        } catch (SQLException ex) {
            System.out.println("MySQL exception: " + ex.getMessage());
        }
        return snmpProfile;
    }

    /**
     * vrati seznam udalosti
     *
     * @param
     * @return ArrayList<Event>
     */
    public static ArrayList<Event> getEvents() {
        ArrayList<Event> list = new ArrayList<>();
        String sql = "SELECT * FROM events";
        try {
            Statement statement = getCurrentConnection().createStatement();
            ResultSet r = statement.executeQuery(sql);
            while (r.next()) {
                list.add(new Event(
                        r.getInt("id"),
                        r.getInt("object"),
                        r.getLong("start"),
                        r.getLong("finish"),
                        r.getString("description"),
                        r.getInt("type")
                ));
            }
            r.close();
            statement.close();
        } catch (SQLException ex) {
            LogFile.printErr("MySQL exception: " + ex.getMessage() + "query: " + sql);
        }
        return list;
    }

    /**
     * pridá objekt
     *
     * @param mo MapObject
     * @return
     */
    static MapObject addMapObject(MapObject mo) {
        String query = "INSERT INTO objects ("
                + "name,"
                + "ip,"
                + "x,"
                + "y,"
                + "map,"
                + "objectType,"
                + "active,"
                + "user,"
                + "password,"
                + "description,"
                + "location,"
                + "snmpProfile,"
                + "winbox,"
                + "winboxPort,"
                + "ssh,"
                + "sshPort,"
                + "web,"
                + "webPort,"
                + "webVerze,"
                + "telnet,"
                + "telnetPort,"
                + "sms,"
                + "smsPort,"
                + "smsVerze"
                + ") VALUES ("
                + "'" + mo.getName() + "',"
                + "'" + mo.getIp() + "',"
                + "'" + mo.getX() + "',"
                + "'" + mo.getY() + "',"
                + "'" + mo.getMap() + "',"
                + "'" + mo.getObjectType() + "',"
                + "'" + (mo.isActive() ? 1 : 0) + "',"
                + "'" + mo.getUser() + "',"
                + "'" + mo.getPassword() + "',"
                + "'" + mo.getDescription() + "',"
                + "'" + mo.getLocation() + "',"
                + "'" + mo.getSnmpProfile() + "',"
                + "'" + (mo.isWinbox() ? 1 : 0) + "',"
                + "'" + mo.getWinboxPort() + "',"
                + "'" + (mo.isSsh() ? 1 : 0) + "',"
                + "'" + mo.getSshPort() + "',"
                + "'" + (mo.isWeb() ? 1 : 0) + "',"
                + "'" + mo.getWebPort() + "',"
                + "'" + mo.getWebVerze() + "',"
                + "'" + (mo.isTelnet() ? 1 : 0) + "',"
                + "'" + mo.getTelnetPort() + "',"
                + "'" + (mo.isSms() ? 1 : 0) + "',"
                + "'" + mo.getSmsPort() + "',"
                + "'" + mo.getSmsVerze() + "'"
                + ")";
        mo.setId(insert(query));
        return mo;
    }

    /**
     * nastaví stav zámku mapy
     *
     * @param state stav
     * @param map map id
     */
    static void mapLock(int state, int map) {
        update("UPDATE maps SET locked='" + state + "' WHERE  id=" + map);
    }

    /**
     * pridá typ objektu
     *
     * @param ot ObjectType
     * @return
     */
    static int addObjectType(ObjectType ot) {
        return insert("INSERT INTO objectType (name, image) VALUES ('" + ot.getName() + "', '" + ot.getImgStr() + "')");
    }

    /**
     * odebere objekt z databaze
     *
     * @param id
     */
    static void removeObject(int id) {
        if (remove("objects", id)) {
            LogFile.printInfo("Obejct ID " + id + " removed from database");
        } else {
            LogFile.printErr("Obejct ID " + id + " not removed from database");
        }
    }

    /**
     * odebere mapu z databaze
     *
     * @param id
     */
    static void removeMap(int id) {
        if (remove("maps", id)) {
            LogFile.printInfo("Map ID " + id + " removed from database");
        } else {
            LogFile.printErr("Map ID " + id + " not removed from database");
        }
    }

    /**
     * pridani mapy
     *
     * @param name nazev mapy
     * @return
     */
    static int addMap(String name) {
        return insert("INSERT INTO maps (name) VALUES ('" + name + "')");
    }

    /**
     * zmeni pozici objektu
     *
     * @param id id objektu
     * @param x pozice x
     * @param y pozice y
     */
    static void objectMove(int id, int x, int y) {
        update("UPDATE objects SET x='" + x + "', y='" + y + "' WHERE  id=" + id);
    }

    /**
     * upravi object
     *
     * @param mo MapObject
     */
    static void updateObject(MapObject mo) {
        update("UPDATE objects SET "
                + "name='" + mo.getName() + "', "
                + "ip='" + mo.getIp() + "', "
                + "x='" + mo.getX() + "', "
                + "y='" + mo.getY() + "',"
                + "map='" + mo.getMap() + "', "
                + "objectType='" + mo.getObjectType() + "', "
                + "active='" + (mo.isActive() ? 1 : 0) + "', "
                + "user='" + mo.getUser() + "', "
                + "password='" + mo.getPassword() + "', "
                + "description='" + mo.getDescription() + "', "
                + "location='" + mo.getLocation() + "', "
                + "snmpProfile='" + mo.getSnmpProfile() + "', "
                + "winbox='" + (mo.isWinbox() ? 1 : 0) + "', "
                + "winboxPort='" + mo.getWinboxPort() + "', "
                + "ssh='" + (mo.isSsh() ? 1 : 0) + "', "
                + "sshPort='" + mo.getSshPort() + "', "
                + "web='" + (mo.isWeb() ? 1 : 0) + "', "
                + "webPort='" + mo.getWebPort() + "', "
                + "webVerze='" + mo.getWebVerze() + "', "
                + "telnet='" + (mo.isTelnet() ? 1 : 0) + "', "
                + "telnetPort='" + mo.getTelnetPort() + "', "
                + "sms='" + (mo.isSms() ? 1 : 0) + "', "
                + "smsPort='" + mo.getSmsPort() + "', "
                + "smsVerze='" + mo.getSmsVerze() + "' "
                + "WHERE  id=" + mo.getId());
    }

    public static Event addEvent(int mo_id, String description, int type) {
        Timestamp timestamp = new Timestamp(System.currentTimeMillis());
        Event event = new Event(mo_id,
                timestamp.getTime(),
                description,
                type);
        int id = insert("INSERT INTO events ("
                + "object, "
                + "start, "
                + "description, "
                + "type"
                + ") VALUES ("
                + "'" + event.getObject() + "',"
                + "'" + event.getStart() + "',"
                + "'" + event.getDescription() + "',"
                + "'" + event.getType() + "'"
                + ")");
        event.setId(id);
        return event;

    }

    public static Event endEvent(int objectId, int type) {
        Event event = null;
        try {
            
            String select_query = "SELECT * FROM events WHERE object = '" + objectId + "' && type = '" + type + "' ORDER BY id DESC LIMIT 1";
            PreparedStatement stmt = getCurrentConnection().prepareStatement(select_query);
            ResultSet rs = stmt.executeQuery();
            // Process the result
            if (rs.next()) {
                event = new Event(
                        objectId,
                        rs.getLong("start"),
                        rs.getString("description"),
                        type);
                event.setId(rs.getInt("id"));
            }
            Timestamp timestamp = new Timestamp(System.currentTimeMillis());
            event.setEnd(timestamp.getTime());
            String update_query = "UPDATE events SET finish='" + event.getEnd() + "' WHERE  id=" + event.getId();
            update(update_query);
            
        } catch (SQLException ex) {
            LogFile.printErr("MySQL exception: " + ex.getMessage());
        }
        return event;
    }

    static int addUser(User user) {
        return insert("INSERT INTO users ("
                + "username, "
                + "password, "
                + "add_map, "
                + "remove_map, "
                + "edit_map, "
                + "add_object, "
                + "remove_object, "
                + "edit_object, "
                + "edit_server"
                + ") VALUES ("
                + "'"+user.getUsername()+"',"
                + "'"+user.getPassword()+"',"
                + "'"+(user.isAddMap()?1:0)+"',"
                + "'"+(user.isRemoveMap()?1:0)+"',"
                + "'"+(user.isEditMap()?1:0)+"',"
                + "'"+(user.isAddObject()?1:0)+"',"
                + "'"+(user.isRemoveObject()?1:0)+"',"
                + "'"+(user.isEditObject()?1:0)+"',"
                + "'"+(user.isEditServer()?1:0)+"'"
                + ")");
    }
    
    static void editUser(User ur) {
        String sql = "UPDATE users SET "
                + "username='" + ur.getUsername() + "',"
                + "add_map='" + (ur.isAddMap() ? 1:0) + "',"
                + "remove_map='" + (ur.isRemoveMap() ? 1:0) + "',"
                + "edit_map='" + (ur.isEditMap() ? 1:0) + "',"
                + "add_object='" + (ur.isAddObject() ? 1:0) + "',"
                + "remove_object='" + (ur.isEditObject() ? 1:0) + "',"
                + "edit_object='" + (ur.isRemoveObject() ? 1:0) + "',"
                + "edit_server='" + (ur.isEditServer() ? 1:0) + "'"
                + "WHERE id='" + ur.getId() + "'";
        update(sql);
    }
    
    static void deleteObjectLog(int objId) {
        String sql = "DELETE FROM events WHERE object = " + objId;
        try {
            Statement statement = getCurrentConnection().createStatement();
            statement.executeUpdate(sql);
            statement.close();
        } catch (SQLException ex) {
            LogFile.printErr("MySQL exception: " + ex.getMessage() + " SQL: " + sql);
        }
    }
    
}
