package hu.alkfejl.bookshop.model;

import hu.alkfejl.bookshop.common.Labels;
import hu.alkfejl.bookshop.model.bean.Book;
import hu.alkfejl.bookshop.model.bean.Customer;

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.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;

/**
 * @author Fulop Lajos
 * @leiras Ez az osztály az adatelérést szolgálja.
 */
public class BookShopDAO {

	Map<Integer, Customer> customers = new HashMap<Integer, Customer>();
	Map<Integer, Book> books = new HashMap<Integer, Book>();
	
	//Adatbázis fájlt reprezentáló sztring, melyet a BookShopDB környezeti változóból olvasunk ki (env.bat állítja be)
	private static final String dbfile = System.getenv("BookShopDB");
	//SQL paraméterezhető insert utasítás sztring Customer felvételére
	//Az egyes paramétereket utólagosan állíthatjuk be (PreparedStatement)
	private static final String SQL_addCustomer = 
		"insert into Customer (name, age, female, rented, student, grantee, qualif) values " +
		"(?,?,?,?,?,?,?)";
	//SQL lekérdezés, a Customerek lekérdezéséhez
	private static final String SQL_listCustomers = "select * from Customer";
	private static final String SQL_queryCustomer = "select * from Customer where name=?";
	private static final String SQL_buyBook = 
		"insert into Book (author, title, year, category, price, pieces, ancient) values (?,?,?,?,?,?,?)";
	private static final String SQL_listBooks = "select * from Book";
	private static final String SQL_queryBooks = "select * from Book where author=? and title=? " +
			"and year=? and category=? and price=? and ancient=?";
	private static final String SQL_updateBooks = "update Book set pieces=pieces+? where id=?";
		
	//A konstruktorban inicializáljuk az adatbázist
	public BookShopDAO() throws ClassNotFoundException{
    	//Betoltjuk az Sqlite JDBC drivert, ennek segítségével érjül majd el az Sqlite adatbázist
    	//kulso/java/sqlitejdbc-v054.jar - a classpath-ba is bekerult
    	//build.xml - javac tasknal classpath attributum (nezzuk meg)
    	//Valamint ezt megadtuk a disztribucio futattásánál is a run.bat-ban! (nezzuk meg)
		Class.forName("org.sqlite.JDBC");
	}
	
	public boolean addCustomer(Customer c) throws SQLException{
		if(checkIfCustomerExists(c))
			return false;
		//Adatbázis kapcsolatot reprezentáló objektum
		Connection conn = null;
		PreparedStatement pst = null;
		try {
			//Az adatbázis kapcsolatunkat a DriverManager segítségével hozzuk létre
			//Megadjuk hogy a jdbc milyen driveren keresztul milyen fájlt keressen
			conn = DriverManager.getConnection("jdbc:sqlite:"+dbfile);
			
			//Uj Customer felvetele eseten egy PreparedStatement objektumot kerunk a kapcsolat objektumtol
			//Ez egy paraméterezhető sql utasitást vár, a paraméterek ?-kent jelennek meg (lsd. SQL_addCustomer attr.)
			pst = conn.prepareStatement(SQL_addCustomer);
			int index = 1;
			//Az egyes parametereket sorban kell megadni, pozicio alapjan, ami 1tol indul
			//Celszeru egy indexet inkrementalni, mivel ha az egyik parameter kiesik az elejerol, akkor nem kell az utana kovetkezoeket ujra szamozni... 
			pst.setString(index++, c.getName());
			pst.setInt(index++, c.getAge());
			pst.setInt(index++, c.isFemale()?1:0);
			pst.setInt(index++, c.isRented()?1:0);
			pst.setInt(index++, c.isStudent()?1:0);
			pst.setInt(index++, c.isGrantee()?1:0);
			pst.setString(index++, c.getQualif());
			//az ExecuteUpdate paranccsal vegrehajtjuk az utasitast
			//Ezzel azt is megmondjuk, hogy az utasitas modositja az adatbazist
			pst.executeUpdate();
		} finally {
			//NAGYON FONTOS, hogy minden adatbazis objektumot finallyban le kell zarni, mivel ha ezt nem tesszuk meg akkor elofordulhat
			//hogy nyitott kapcsolatok maradnak az adatbazis fele. Az adatbazis pedig korlatozott szamban tart fennt kapcsolatokat, ezert
			//egy ido utan akar ez be is telhet!
			//Minden egyes objektumot kulon try catch agban kell megprobalni bezarni!
			try {
				if(pst != null)
					pst.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			try {
				if(conn != null)
					conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return true;
	}

	public Map<Integer, Customer> getCustomers() throws SQLException{
		Connection conn = null;
		Statement st = null;
		//Toroljuk a memoriabol a customereket (azert tartjuk bennt, mert lehetnek kesobb olyan muveletek, melyekhez nem kell frissiteni)
		customers.clear();
		
		try {
			//Az adatbázis kapcsolatunkat a DriverManager segítségével hozzuk létre
			//Megadjuk hogy a jdbc milyen driveren keresztul milyen fájlt keressen
			conn = DriverManager.getConnection("jdbc:sqlite:"+dbfile);
			
			//a kapcsolat(conn) objektumtol kerunk egy egyszeru (nem parameterezheto) utasitast
			st = conn.createStatement();
			//Az utasitas objektumon keresztul inditunk egy queryt(SQL_listCustomers)
			//Az eredmenyeket egy ResultSet objektumban kapjuk vissza
			ResultSet rs = st.executeQuery(SQL_listCustomers);
			//Bejarjuk a visszakapott ResultSet-et (ami a customereket fogja tartalmazni)
			while(rs.next()){
				//Uj Customert hozunk letre
				Customer c = new Customer();
				//A customer nevet a resultSet aktualis soraban talalhato name rekordra allitjuk be
				c.setName(rs.getString("name"));
				//A customer korat a resultSet aktualis soraban talalhato age rekordra allitjuk be
				c.setAge(rs.getInt("age"));
				c.setFemale(rs.getInt("female") == 1);
				c.setRented(rs.getInt("rented") == 1);
				c.setStudent(rs.getInt("student") == 1);
				c.setGrantee(rs.getInt("grantee") == 1);
				c.setQualif(rs.getString("qualif"));
				customers.put(rs.getInt("id"), c);
			}
		} finally {
			try {
				if(st != null)
					st.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			try {
				if(conn != null)
					conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		
		return customers;
	}
	
	private boolean checkIfCustomerExists(Customer c) throws SQLException {
		Connection conn = null;
		PreparedStatement pst = null;
		ResultSet rs = null;
		try {
			conn = DriverManager.getConnection("jdbc:sqlite:"+dbfile);
			
			pst = conn.prepareStatement(SQL_queryCustomer);
			int index = 1;
			pst.setString(index++, c.getName());
			rs = pst.executeQuery();
			if (rs.next())
				return true;
			return false;			
		} finally {
			try {
				if(pst != null)
					pst.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			try {
				if(rs != null)
					rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			try {
				if(conn != null)
					conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}	
	
	private int checkIfBookExists(Book b) throws SQLException {
		Connection conn = null;
		PreparedStatement pst = null;
		ResultSet rs = null;
		try {
			conn = DriverManager.getConnection("jdbc:sqlite:"+dbfile);
			
			pst = conn.prepareStatement(SQL_queryBooks);
			int index = 1;
			pst.setString(index++, b.getAuthor());
			pst.setString(index++, b.getTitle());
			pst.setInt(index++, b.getYear());
			pst.setString(index++, b.getCategory());
			pst.setInt(index++, b.getPrice());
			pst.setInt(index++, b.isAncient() ? 1:0);
			rs = pst.executeQuery();
			int bookid = -1;
			if (rs.next())
				bookid = rs.getInt("id");
			return bookid;			
		} finally {
			try {
				if(pst != null)
					pst.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			try {
				if(rs != null)
					rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			try {
				if(conn != null)
					conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
	
	public boolean buyBook(Book b) throws SQLException {
		Connection conn = null;
		PreparedStatement pst = null;
		try {
			conn = DriverManager.getConnection("jdbc:sqlite:"+dbfile);
			
			int bookid = checkIfBookExists(b);
			if (bookid>0) {
				pst = conn.prepareStatement(SQL_updateBooks);
				int index = 1;
				pst.setInt(index++, b.getPiece());
				pst.setInt(index++, bookid);
				
				pst.executeUpdate();
			} else {
				pst = conn.prepareStatement(SQL_buyBook);
				int index = 1;
				pst.setString(index++, b.getAuthor());
				pst.setString(index++, b.getTitle());
				pst.setInt(index++, b.getYear());
				pst.setString(index++, b.getCategory());
				pst.setInt(index++, b.getPrice());
				pst.setInt(index++, b.getPiece());
				pst.setInt(index++, b.isAncient() ? 1:0);
				
				pst.executeUpdate();
			}
		} finally {
			try {
				if(pst != null)
					pst.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			try {
				if(conn != null)
					conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return true;
	}
	
	public Map<Integer, Book> getBooks() throws SQLException{
		Connection conn = null;
		Statement st = null;
		ResultSet rs = null;
		books.clear();
		
		try {
			conn = DriverManager.getConnection("jdbc:sqlite:"+dbfile);
			
			st = conn.createStatement();
			rs = st.executeQuery(SQL_listBooks);
			while(rs.next()){
				Book b = new Book();
				b.setAuthor(rs.getString("author"));
				b.setTitle(rs.getString("title"));
				b.setYear(rs.getInt("year"));
				b.setCategory(rs.getString("category"));
				b.setPrice(rs.getInt("price"));
				b.setAncient(rs.getInt("ancient") == 1);
				b.setPiece(rs.getInt("pieces"));
				books.put(rs.getInt("id"), b);
			}
		} finally {
			try {
				if(st != null)
					st.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			try {
				if(rs != null)
					rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			try {
				if(conn != null)
					conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		
		return books;
	}
	
	public void testCustomer() throws SQLException{
		//Teszteljuk 10-szer az addCustomert es utana a getCustomert
		//A neve mindig legyen TestCustomer
		//A kora legyen veletlen, 0 es 100 kozott
		for(int i = 0; i < 10; i++){
			Customer c = new Customer();
			c.setName("TestCustomer");
			Random r = new Random();
			int randint = r.nextInt(100);
			c.setAge(randint);
			addCustomer(c);
		}
		//A vegen kerdezzuk le az adatbazist, hogy milyen Customerek vannak (valamit ellenorizzuk Sqlite shellben is)
		String s = getCustomers().toString();
		System.out.println(s);

	}
	
	public void testBook() throws SQLException{
		for(int i = 0; i < 10; i++){
			Book b = new Book();
			b.setAuthor("TestAuthor");
			b.setTitle("TestTitle");
			b.setYear(1899);
			b.setCategory(Labels.adventure);
			b.setPrice(12345);
			b.setPiece(1);
			b.setAncient(true);
			buyBook(b);
		}
		String s = getBooks().toString();
		System.out.println(s);

	}
	
	public static void main(String args[]) throws SQLException, ClassNotFoundException{
		BookShopDAO dao = new BookShopDAO();
		dao.testCustomer();
		dao.testBook();
		System.exit(0);
	}	
}
