package honk.tools.anidb;

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 java.util.Collections;
import java.util.List;

/**
 * Wraps a hsqldb connection.
 * @author honk
 */
public class Database implements AniDBConnectionCache {
    final Statement statement;
    private PreparedStatement animeStatement;
    private PreparedStatement fileStatement;
    private PreparedStatement animePutStatement;
    private PreparedStatement filePutStatement;
    private PreparedStatement allAnimeStatement;
    private PreparedStatement animeSetPreferedNameStatement;


    /**
     * Creates a connection to a hsqldb and creates tables if necessary.
     * @throws SQLException if an sql exception occurs
     * @throws ClassNotFoundException if the hsqldb driver cannot be found
     */
    public Database() throws SQLException, ClassNotFoundException {
        Class.forName("org.hsqldb.jdbcDriver");

        Connection c = DriverManager.getConnection("jdbc:hsqldb:file:anidb", "sa", "");
        statement = c.createStatement();
        try {
            statement.executeUpdate("CREATE TABLE anime (" + //
                    "aid int, " + //
                    "episodes int, " + //
                    "normalEpisodes int, " + //
                    "specialEpisodes int, " + //
                    "rating int, " + //
                    "voteCount int, " + //
                    "tempRating int, " + //
                    "tempVoteCount int, " + //
                    "picName VARCHAR, " + //
                    "year VARCHAR, " + //
                    "type VARCHAR, " + //
                    "preferedName VARCHAR, " + //
                    "romanjiName VARCHAR, " + //
                    "kanjiName VARCHAR, " + //
                    "englishName VARCHAR, " + //
                    "shortNameList VARCHAR, " + //  "'" seperated list
                    "categoryList VARCHAR," + //      "," seperated list
                    "description VARCHAR," + //
                    "usefulInfo VARCHAR," + //
                    "CONSTRAINT anime_pkey PRIMARY KEY (aid) )");
        } catch (SQLException e) {
            if (e.getMessage() == null || !e.getMessage().startsWith("Table already exists")) {
                e.printStackTrace();
            }
        }

        try {
            statement.executeUpdate("CREATE TABLE file (" + //
                    "size int, " + //
                    "hash VARCHAR, " + //
                    "animeID int, " + //
                    "fileID int, " + //
                    "episodeNumber VARCHAR, " + //
                    "episodeName VARCHAR, " + //
                    "description VARCHAR, " + //
                    "quality VARCHAR, " + //
                    "subLang VARCHAR, " + //
                    "dubLang VARCHAR, " + //
                    "length int, " + //
                    "fileType VARCHAR, " + //
                    "source VARCHAR, " + //
                    "CONSTRAINT file_pkey PRIMARY KEY (size, hash)," + //
                    "UNIQUE (fileID) )");
        } catch (SQLException e) {
            if (e.getMessage() == null || !e.getMessage().startsWith("Table already exists")) {
                e.printStackTrace();
            }
        }
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                try {
                    statement.execute("SHUTDOWN");
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        });

        
        animeStatement = c.prepareStatement("SELECT * from anime WHERE aid = ?");
        allAnimeStatement = c.prepareStatement("SELECT aid from anime ORDER BY kanjiName");
        animePutStatement = c.prepareStatement("INSERT into anime VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
        animeSetPreferedNameStatement = c.prepareStatement("UPDATE anime SET preferedName = ? WHERE aid = ?");
        fileStatement = c.prepareStatement("SELECT * from file WHERE size = ? AND hash = ?");
        filePutStatement = c.prepareStatement("INSERT into file VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?)");
    }

    /* (non-Javadoc)
     * @see honk.tools.anidb.AniDBConnection#getAnimes()
     */
    public List<Integer> getAnimes() {
        try {
            ResultSet rs = allAnimeStatement.executeQuery();
            
            List<Integer> anime = new ArrayList<Integer>();
            while (rs.next()) {
                anime.add(rs.getInt(1));
            }
            return anime;
        } catch (SQLException e) {
            e.printStackTrace();
            return Collections.emptyList();
        }
    }
    
    /* (non-Javadoc)
     * @see honk.tools.anidb.AniDBCache#getAnime(int)
     */
    public AnimeInfo getAnime(int aid) {
        try {
            animeStatement.setInt(1, aid);
            ResultSet rs = animeStatement.executeQuery();
            if (rs.next()) {
                //int animeID = rs.getInt("animeID");
                int episodes = rs.getInt("episodes");
                int normalEpisodes = rs.getInt("normalEpisodes");
                int specialEpisodes = rs.getInt("specialEpisodes");
                int rating = rs.getInt("rating");
                int voteCount = rs.getInt("voteCount");
                int tempRating = rs.getInt("tempRating");
                int tempVoteCount = rs.getInt("tempVoteCount");
                String picName = rs.getString("picName");
                String year = rs.getString("year");
                String type = rs.getString("type");
                String preferedName = rs.getString("preferedName");
                String romajiName = rs.getString("romanjiName");
                String kanjiName = rs.getString("kanjiName");
                String englishName = rs.getString("englishName");
                String[] shortNameList = AnimeInfo.stringToArray(rs.getString("shortNameList"), AnimeInfo.SEPERATOR_SHORTNAME);
                String[] categoryList = AnimeInfo.stringToArray(rs.getString("categoryList"), AnimeInfo.SEPERATOR_CATEGORY);
                String description = rs.getString("description");
                String relations = rs.getString("relations");


                return new AnimeInfo(aid, episodes, normalEpisodes, specialEpisodes, rating, voteCount, tempRating,
                        tempVoteCount, picName, year, type, preferedName, romajiName, kanjiName, englishName,
                        shortNameList, categoryList, description, relations);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    /* (non-Javadoc)
     * @see honk.tools.anidb.AniDBCache#getFile(int, java.lang.String)
     */
    public FileInfo getFile(int size, String hash) {
        try {
            fileStatement.setInt(1, size);
            fileStatement.setString(2, hash);
            ResultSet rs = fileStatement.executeQuery();
            if (rs.next()) {
                //int size = rs.getInt("size");
                //String hash = rs.getString("hash");
                int animeID = rs.getInt("animeID");
                int fileID = rs.getInt("fileID");
                String episodeNumber = rs.getString("episodeNumber");
                String episodeName = rs.getString("episodeName");
                String description = rs.getString("description");
                String quality = rs.getString("quality");
                String subLang = rs.getString("subLang");
                String dubLang = rs.getString("dubLang");
                int length = rs.getInt("length");
                String fileType = rs.getString("fileType");
                String source = rs.getString("source");

                return new FileInfo(size, hash, animeID, fileID, episodeNumber, episodeName, description, quality,
                        subLang, dubLang, length, fileType, source);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    public void putAnime(AnimeInfo info) {
        /*
          1  "aid int, " + //
          2  "episodes int, " + //
          3  "normalEpisodes int, " + //
          4  "specialEpisodes int, " + //
          5  "rating int, " + //
          6  "voteCount int, " + //
          7  "tempRating int, " + //
          8  "tempVoteCount int, " + //
          9  "picName VARCHAR, " + //
          10  "year VARCHAR, " + //
          11  "type VARCHAR, " + //
          12  "preferedName VARCHAR, " + //
          13  "romanjiName VARCHAR, " + //
          14  "kanjiName VARCHAR, " + //
          15  "englishName VARCHAR, " + //
          16  "shortNameList VARCHAR, " + //  "'" seperated list
          17  "categoryList VARCHAR," + //      "," seperated list
            */
        
        try {
            animePutStatement.setInt(1, info.getAnimeID());
            animePutStatement.setInt(2, info.getEpisodes());
            animePutStatement.setInt(3, info.getNormalEpisodes());
            animePutStatement.setInt(4, info.getSpecialEpisodes());
            animePutStatement.setInt(5, info.getRating());
            animePutStatement.setInt(6, info.getVoteCount());
            animePutStatement.setInt(7, info.getTempRating());
            animePutStatement.setInt(8, info.getTempVoteCount());
            animePutStatement.setString(9, info.getPicName());
            animePutStatement.setString(10, info.getYear());
            animePutStatement.setString(11, info.getType());
            animePutStatement.setString(12, info.getPreferedName());
            animePutStatement.setString(13, info.getRomajiName());
            animePutStatement.setString(14, info.getKanjiName());
            animePutStatement.setString(15, info.getEnglishName());
            animePutStatement.setString(16, AnimeInfo.arrayToString(info.getShortNameList(), AnimeInfo.SEPERATOR_SHORTNAME));
            animePutStatement.setString(17, AnimeInfo.arrayToString(info.getCategoryList(), AnimeInfo.SEPERATOR_CATEGORY));
            animePutStatement.setString(18, info.getDescription());
            animePutStatement.setString(19, info.getRelations());
            
            
            animePutStatement.execute();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        
    }


    public void putFile(FileInfo info) {
        /*
        1 "size int, " + //
        2 "hash VARCHAR, " + //
        3 "animeID int, " + //
        4 "fileID int, " + //
        5 "episodeNumber VARCHAR, " + //
        6 "episodeName VARCHAR, " + //
        7 "description VARCHAR, " + //
        8 "quality VARCHAR, " + //
        9 "subLang VARCHAR, " + //
        10 "dubLang VARCHAR, " + //
        11 "length int, " + //
        12 "fileType VARCHAR, " + //
        13 "source VARCHAR, " + //
*/
        try {
            filePutStatement.setInt(1, info.getSize());
            filePutStatement.setString(2, info.getHash());
            filePutStatement.setInt(3, info.getAnimeID());
            filePutStatement.setInt(4, info.getFileID());
            filePutStatement.setString(5, info.getEpisodeNumber());
            filePutStatement.setString(6, info.getEpisodeName());
            filePutStatement.setString(7, info.getDescription());
            filePutStatement.setString(8, info.getQuality());
            filePutStatement.setString(9, info.getSubLang());
            filePutStatement.setString(10, info.getDubLang());
            filePutStatement.setInt(11, info.getLength());
            filePutStatement.setString(12, info.getFileType());
            filePutStatement.setString(13, info.getSource());
            
            filePutStatement.execute();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public void changePreferedName(int aid, String name) {
        try {
            animeSetPreferedNameStatement.setInt(2, aid);
            animeSetPreferedNameStatement.setString(1, name);
            
            animeSetPreferedNameStatement.execute();
        } catch (SQLException e) {
            e.printStackTrace();
        }    
    }


}
