package eu.dnetlib.xml.database;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.StringWriter;
import java.net.URL;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathExpressionException;

import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.core.io.Resource;
import org.xml.sax.SAXException;
import org.xmldb.api.base.XMLDBException;

import eu.dnetlib.enabling.tools.OpaqueResource;
import eu.dnetlib.enabling.tools.ResourceLoaderHelper;
import eu.dnetlib.enabling.tools.StreamOpaqueResource;
import eu.dnetlib.xml.database.exist.ExistDatabase;

public class OfflineDatabaseClient extends AbstractDatabaseClient {
	private ResourceLoaderHelper resourceLoader;

	@Override
	public void dump() {
		notImplemented("offline dump");
	}

	@Override
	public void load(String loadDir) {
		if (loadDir == null)
			throw new IllegalArgumentException("--load-dir is mandatory");

		ExistDatabase db = (ExistDatabase) getContext().getBean("existDatabase", ExistDatabase.class);
		System.out.println("importing into " + loadDir);


		String schemas = "file://" + loadDir + "/schemas/**/*.xsd";
		String resources = "file://" + loadDir + "/profiles/**/*.xml";

		try {
			if (new File(loadDir + "/schemas").exists()) {
				for (Resource res : resourceLoader.getResourcePatternResolver().getResources(schemas)) {
					registerSchema(db, res.getURL());
				}
			}

			if (new File(loadDir + "/profiles").exists()) {
				for (Resource res : resourceLoader.getResourcePatternResolver().getResources(resources)) {
					registerProfile(db, res.getURL());
				}
			}
		} catch (IOException e) {
			throw new IllegalStateException(e);
		} catch (XPathExpressionException e) {
			throw new IllegalStateException(e);
		} catch (SAXException e) {
			throw new IllegalStateException(e);
		} catch (ParserConfigurationException e) {
			throw new IllegalStateException(e);
		} catch (XMLDBException e) {
			throw new IllegalStateException(e);
		}
	}

	@Override
	public void recover(String backupFile) {
		if (backupFile == null)
			throw new IllegalArgumentException("--backup-file is mandatory");
		
		ExistDatabase db = (ExistDatabase) getContext().getBean("recoverExistDatabase", ExistDatabase.class);
		
		System.out.println("Recovering from " + backupFile);
	
		try {

			if (db.collectionExists("/db/DRIVER")) {
				System.err.println("Error: xml-db is not empty");
				throw new IllegalStateException("xml-db is not empty");
			}

			ZipInputStream zip = new ZipInputStream(new FileInputStream(backupFile));
			
			ZipEntry entry = null;
		    while ((entry = zip.getNextEntry()) != null) {
		    	File f = new File(entry.getName());
		    	String collection = f.getParent();
		    	
		    	if (collection==null) collection = "/";
		    	if (!collection.startsWith("/")) collection = "/" + collection;
		    	
		    	String name = f.getName();

		    	name = name.replaceAll("\\.xml$", "");
		    	name = name.replaceAll("\\.xsd$", "");
		    	
		    	System.out.println(" -- Adding " + name + " [ " + collection + " ]");
		    	
		    	ByteArrayOutputStream out = new ByteArrayOutputStream();
		    	for (int c = zip.read(); c != -1; c = zip.read()) {
		    		out.write(c);
		    	}
		    	out.close();
		    	zip.closeEntry();
		    	
		    	String profile = out.toString();

		    	db.create(name, collection, profile);
		    }
		    zip.close();
		} catch (FileNotFoundException e) {
			System.err.println("Error: backup file is missing");
			throw new IllegalStateException(e);
		} catch (IOException e) {
			System.err.println("Error: invalid backup file");
			throw new IllegalStateException(e);
		} catch (XMLDBException e) {
			System.err.println("Error: problem with the xml-db");
			System.err.println("Tip: check the zip file format, ensure that it doesn't contain directory entries. (zip -rD foo.zip foo)");
			throw new IllegalStateException(e);
		}
	}
	
	
	private void registerProfile(ExistDatabase db, URL url) throws XPathExpressionException, SAXException, IOException, ParserConfigurationException, XMLDBException {
		String name = fileName(url);
		String collection = "/db/DRIVER/" + resourceKind(url) + "/" + resourceType(url);

		System.out.println("registering profile " + collection + " " + name);

		
		
		String oldContent = db.read(name, collection);
		
		StringWriter newContent = new  StringWriter();
		IOUtils.copy(url.openStream(), newContent);
		
		if(oldContent != null)
			db.update(name, collection, newContent.toString());
		else
			System.out.println("INSERTING...");
//			db.create(name, collection, content);
		
	}

	private void registerSchema(ExistDatabase db, URL url) {
		System.out.println("registering schema " + url);
	}

	private String fileName(URL url) throws XPathExpressionException, SAXException, IOException, ParserConfigurationException {
		OpaqueResource res = new StreamOpaqueResource(url.openStream());
		String id = res.getResourceId();
		return id.split("_")[0];
	}

	private String resourceType(URL url) {
		return new File(url.getPath()).getParentFile().getName();
	}

	private String resourceKind(URL url) {
		return new File(url.getPath()).getParentFile().getParentFile().getName();
	}

	@Override
	public String mode() {
		return "offline";
	}

	public ResourceLoaderHelper getResourceLoader() {
		return resourceLoader;
	}

	@Required
	public void setResourceLoader(ResourceLoaderHelper resourceLoader) {
		this.resourceLoader = resourceLoader;
	}

}
