/*
STORYBOOK: Open Source software for novelists and authors.
Copyright (C) 2008 - 2012 Martin Mustun

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package storybook.model;

import assistant.Assistant;
import i18n.I18N;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import javax.swing.JOptionPane;
import org.h2.tools.Backup;
import org.h2.tools.Restore;
import org.h2.util.JdbcUtils;
import storybook.App;
import storybook.Const.STORYBOOK;
import storybook.model.book.Book;
import storybook.tools.LOG;
import storybook.tools.file.EnvUtil;
import storybook.tools.html.Html;
import storybook.ui.MainFrame;
import storybook.ui.dialog.ExceptionDlg;

/**
 * @author martin
 *
 */
public class H2File {

	private static final String TT = "H2File";

	private File file;		// the real file
	private File fileH2;	// the h2 file
	private String h2Name;	// the complete dbname with the h2.db or mv.db extension
	private String path;	// only the path to the file
	private String name;	// the real name without path and extension
	private String ext;
	private boolean opened = false;
	private String extH2;

	public H2File(String dbName) {
		this(new File(dbName));
	}

	public H2File(File file) {
		if (file == null) {
			return;
		}
		this.file = file;
		String absPath = file.getAbsolutePath();
		path = file.getParent();
		ext = STORYBOOK.FILE_EXT_H2DB.toString();
		int idx = absPath.lastIndexOf(ext);
		if (idx < 0) {
			// Using new "mv.db" name
			ext = STORYBOOK.FILE_EXT_MVDB.toString();
			idx = absPath.lastIndexOf(ext);
			if (idx < 0) {
				// for new ".osbk" name
				ext = STORYBOOK.FILE_EXT_OSBK.toString();
				idx = absPath.lastIndexOf(STORYBOOK.FILE_EXT_OSBK.toString());
			}
		}
		h2Name = absPath.substring(0, idx);
		fileH2 = new File(h2Name + ext);
		String fileName = file.getName();
		idx = fileName.lastIndexOf(ext);
		extH2 = fileName.substring(idx);
		idx = path.length();
		name = h2Name.substring(idx);
		if (name.startsWith(File.separator)) {
			name = name.substring(1);
		}
		idx = name.lastIndexOf(ext);
		if (idx > 1) {
			idx--;
			name = name.substring(0, idx);
		}
	}

	public static String getFileInfo(MainFrame mainFrame, boolean detailed) {
		Book book = mainFrame.getBook();
		StringBuilder buf = new StringBuilder();
		if (detailed) {
			buf.append(Html.toHtml("date.creation", book.getCreation()));
			buf.append(Html.toHtml("date.maj", book.getMaj()));
		}
		buf.append(Html.toHtml("book.title", book.getTitle()));
		buf.append(Html.toHtml("book.subtitle", book.getSubtitle()));
		buf.append(Html.toHtml("book.author", book.getAuthor()));
		buf.append(Html.toHtml("book.copyright", book.getCopyright()));
		if (detailed) {
			buf.append(Html.toHtml("book.blurb", book.getBlurb()));
			buf.append(Html.toHtml("book.dedication", book.getDedication()));
			buf.append(Html.toHtml("notes", book.getNotes()));
			buf.append(Html.toHtml("book.UUID", book.getUUID()));
			buf.append(Html.toHtml("book.ISBN", book.getISBN()));
			buf.append(Html.toHtml("assistant", Assistant.toHtml(book.info.assistantGet())));
		}
		return (buf.toString());
	}

	public File getFile() {
		return file;
	}

	public File getH2File() {
		return fileH2;
	}

	public String getPath() {
		String f = file.getAbsolutePath();
		f = f.substring(0, f.lastIndexOf(File.separator));
		return (f);
	}

	public String getH2Name() {
		return h2Name;
	}

	public String getName() {
		return name;
	}

	public static boolean exists(File f) {
		if (f.exists()) {
			return (true);
		} else {
			return (false);
		}
	}

	@Override
	public boolean equals(Object obj) {
		if (obj == null) {
			return false;
		}
		if (this.getClass() != obj.getClass()) {
			return false;
		}
		H2File test = (H2File) obj;
		return file.equals(test.file);
	}

	@Override
	public int hashCode() {
		int hash = super.hashCode();
		hash = hash * 31 + (file != null ? file.hashCode() : 0);
		return hash;
	}

	@Override
	public String toString() {
		return file.getPath();
	}

	public String getExt() {
		int beginIndex = file.getName().indexOf(".");
		return (file.getName().substring(beginIndex));
	}

	public boolean isOK() {
		//LOG.trace("DbFile.isOK()");
		boolean rc = true;
		// file doesn't exist
		if (!file.exists()) {
			JOptionPane.showMessageDialog(null,
			   I18N.getMsg("project.not.exist.text", file.getPath()),
			   I18N.getMsg("project.not.exist.title"),
			   JOptionPane.ERROR_MESSAGE);
			App.getInstance().reloadMenuBars();
			return false;
		}
		// file is read-only
		if (!file.canWrite()) {
			JOptionPane.showMessageDialog(null,
			   I18N.getMsg("err.db.read.only", file.getPath()),
			   I18N.getMsg("warning"),
			   JOptionPane.ERROR_MESSAGE);
			rc = false;
		}
		return rc;
	}

	public boolean isAlreadyOpened() {
		//LOG.trace("DbFile.isAlreadyOpened("+h2Name+")");
		boolean rc = false;
		List<MainFrame> mainFrames = App.getInstance().getMainFrames();
		if (!mainFrames.isEmpty()) {
			for (MainFrame mainFrame : mainFrames) {
				if (mainFrame.isBlank()) {
					continue;
				}
				if (mainFrame.getH2File().getH2Name().equals(h2Name)) {
					rc = true;
					mainFrame.setVisible(true);
					break;
				}
			}
		} else {
			//LOG.trace("==>frames are empty");
		}
		//LOG.trace("==>File "+h2Name+" "+(rc?"already opened":"not already opened"));
		return (rc);
	}

	/**
	 * saving the file to a zip sql file suffix is osdk
	 *
	 * @return : true if save is ok, false if the SQL statement return a SQLException
	 */
	public boolean save() {
		//LOG.trace(TT + ".save() fileH2=" + fileH2.getAbsolutePath());
		return (doSql(path, true));
	}

	/**
	 * Open the osbk file if file exist check if a h2file exists then replace it, else create a
	 * h2file from the osbk
	 *
	 * @return : true if open is ok, else return false
	 */
	public boolean open() {
		//LOG.trace("Open file: path=" + path + ", name=" + name + ", ext=" + ext);
		if (opened) {
			return (true);
		}
		//LOG.trace("check if file is a h2file (h2.db or mv.db");
		if (ext.contains(STORYBOOK.FILE_EXT_H2DB.toString())
		   || ext.contains(STORYBOOK.FILE_EXT_MVDB.toString())) {
			//LOG.trace("yes it is!");
			return (true);
		}
		//LOG.trace("no it isn't! create a h2file from the osbk file");
		String filename = path + File.separator + name + STORYBOOK.FILE_EXT_OSBK.toString();
		String url = "jdbc:h2:" + getH2Name();
		filename = filename.replaceAll("'", "''");
		try {
			org.h2.Driver.load();
			Connection conn = DriverManager.getConnection(url, "sa", "");
			Statement stat = conn.createStatement();
			stat.execute("DROP ALL OBJECTS");
			String sql = "RUNSCRIPT FROM '" + filename + "' " + "COMPRESSION ZIP";
			stat.execute(sql);
			JdbcUtils.closeSilently(stat);
			JdbcUtils.closeSilently(conn);
			extH2 = STORYBOOK.FILE_EXT_MVDB.toString();
			File f = new File(path + File.separator + name + extH2);
			if (!f.exists()) {
				extH2 = STORYBOOK.FILE_EXT_H2DB.toString();
			}
		} catch (SQLException ex) {
			ExceptionDlg.show(this.getClass().getSimpleName()
			   + ".open() SQLException:\n" + ex.getLocalizedMessage(), ex);
			return false;
		}
		//LOG.trace("created file =" + url);
		return (true);
	}

	public String getOSBK() {
		return path + File.separator + getName() + ".osbk";
	}

	public boolean doSql(String dir, boolean compress) {
		//LOG.trace("H2File.doSql(dir=" + dir + ", compress=" + (compress ? "true" : "false") + ") name=" + getName());
		Connection conn = null;
		String filename = dir + File.separator + getName() + (compress ? ".osbk" : ".sql");
		filename = filename.replace("'", "''");
		try {
			org.h2.Driver.load();
			conn = DriverManager.getConnection("jdbc:h2:" + getH2Name(), "sa", "");
			Statement stat = conn.createStatement();
			if (compress) {
				stat.execute("SCRIPT TO '" + filename + "' COMPRESSION ZIP");
			} else {
				stat.execute("SCRIPT TO '" + filename + "'");
			}
		} catch (SQLException ex) {
			ExceptionDlg.show(this.getClass().getSimpleName()
			   + ".doSql() SQLException:\n" + ex.getLocalizedMessage(), ex);
			return (false);
		} finally {
			JdbcUtils.closeSilently(conn);
		}
		return (true);
	}

	public void doBackup() {
		doBackup(getName(), false);
	}

	public void doBackup(String toFileName, boolean increment) {
		String backup = toFileName;
		if (increment) {
			backup += "_" + EnvUtil.getDateString();
		}
		backup += ".backup";
		try {
			Backup.execute(backup, this.getPath(), getName(), true);
		} catch (SQLException ex) {
			LOG.err(H2File.class.getName(), ex);
		}
		LOG.trace("Backup file to " + backup);
	}

	public void doRestore(File file) {
		Restore.execute(file.getAbsolutePath(), this.getPath(), getName());
	}

	public void doRestore(File dir, File file) {
		Restore.execute(file.getAbsolutePath(), dir.getPath(), "");
	}

	public void remove() {
		//LOG.trace("H2File.remove() file="+getH2Name()+H2ext);
		File f = new File(getH2Name() + ".osbk");
		if (f.exists()) {
			f = new File(getH2Name() + extH2);
			try {
				Files.delete(f.toPath());
			} catch (IOException ex) {
				// empty
			}
		}
	}

}
