package eu.dnetlib.enabling.inspector;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.List;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.cxf.helpers.IOUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import eu.dnetlib.xml.database.XMLDatabase;

/**
 * This controller offers a simple way to backup the xmldb.
 *
 * @author michele
 *
 */
@Controller
public class BackupController extends AbstractInspectorController {

	/**
	 * BUFFER size for file copy.
	 */
	private static final int BUFFER_SIZE = 4096;

	/**
	 * logger.
	 */
	private static final Log log = LogFactory.getLog(BackupController.class); // NOPMD by marko on 11/24/08 5:02 PM

	/**
	 * xmldb.
	 */
	@Resource(name = "existDatabase")
	private transient XMLDatabase xmlDatabase;

	/**
	 * run a query.
	 *
	 * @param model
	 *            mvc model
	 * @param exec
	 *            = 1 perform a new backup (optional)
	 * @param delete
	 *            a filename of a backup to delete (optional)
	 * @throws
	 */
	@RequestMapping(value = "/inspector/backup.do")
	void backup(
			final Model model,
			@RequestParam(value = "exec", required = false) final String exec,
			@RequestParam(value = "delete", required = false) final String delete) {

		if (exec != null && exec.equals("1")) {
			execBackup(model);
		}
		if (delete != null) {
			deleteBackup(model, delete);
		}

		listBackups(model);
	}

	/**
	 * Executes a backup.
	 *
	 * @param model
	 *            mvc model
	 */
	private void execBackup(final Model model) {
		try {
			xmlDatabase.backup();
			log.info("Backup done");
		} catch (final Exception e) {
			model.addAttribute("message", "failed: " + e.getMessage());
			log.info("Backup failed: " + e.getMessage());
		}
	}

	/**
	 * Deletes a backup.
	 *
	 * @param model
	 *            mvc model
	 * @param backup
	 *            backup file name
	 */
	private void deleteBackup(final Model model, final String backup) {
		try {
			final String logFile = backup.replaceFirst("data-", "report-").replace(".zip", ".log");
			(new File(xmlDatabase.getBackupDir() + "/" + backup)).delete();
			(new File(xmlDatabase.getBackupDir() + "/" + logFile)).delete();
			log.info("Backup deleted");
		} catch (final Exception e) {
			model.addAttribute("message", "failed: " + e.getMessage());
			log.info("Backup deletion failed: " + e.getMessage());
		}
	}

	/**
	 * List backups.
	 *
	 * @param model
	 *            mvc mode
	 */
	private void listBackups(final Model model) {
		final File dir = new File(xmlDatabase.getBackupDir());
		if (dir.exists() && dir.isDirectory()) {
			final FilenameFilter filter = new FilenameFilter() { // NOPMD
				public boolean accept(final File dir, final String name) {
					return (name.startsWith("data-") && name.endsWith(".zip"));
				}
			};
			final List<File> list = Arrays.asList(dir.listFiles(filter));
			if (list == null) {
				model.addAttribute("size", 0);
			} else {
				model.addAttribute("size", list.size());
				model.addAttribute("backups", list);
			}
		}
	}

	/**
	 * get a backup Log.
	 *
	 * @throws IOException
	 *             could happen
	 * @param model
	 *            mvc model
	 * @param backup
	 *            the name of backup
	 * @throws
	 */
	@RequestMapping(value = "/inspector/backupLog.do")
	void backupLog(final Model model, @RequestParam(value = "backup") final String backup) throws IOException {
		final String logFile = backup.replaceFirst("data-", "report-").replace(".zip", ".log");
		final FileReader freader = new FileReader(xmlDatabase.getBackupDir() + "/" + logFile);
		final StringWriter blog = new StringWriter();

		try {
			IOUtils.copy(freader, blog, BUFFER_SIZE);
		} finally {
			freader.close();
		}

		model.addAttribute("log", blog);
		model.addAttribute("backup", backup);
	}

	/**
	 * Download a backup.
	 *
	 * @param response
	 *            response
	 * @param backup
	 *            the name of backup
	 * @param out
	 *            response stream
	 * @throws IOException
	 *             could happen
	 */
	@RequestMapping(value = "/inspector/backupDownload.do")
	public void downloadBackup(
			final HttpServletResponse response,
			@RequestParam(value = "backup", required = true) final String backup,
			final OutputStream out) throws IOException {
		response.setContentType("application/zip");

		final String endUserFilename = backup.replaceFirst("data", "dlib-backup");
		response.addHeader("Content-disposition", "attachment; filename=" + endUserFilename);

		final InputStream backupStream = new FileInputStream(xmlDatabase.getBackupDir() + "/" + backup);

		try {
			IOUtils.copy(backupStream, out);
		} finally {
			backupStream.close();
		}

		out.flush();
		out.close();
	}
}
