/*
 * Copyright 2003, 2004  The Apache Software Foundation
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.

 */
package org.apache.ws.jaxme.webapp;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.ws.jaxme.generator.Generator;
import org.apache.ws.jaxme.generator.impl.GeneratorImpl;
import org.apache.ws.jaxme.generator.sg.impl.JAXBSchemaReader;
import org.apache.ws.jaxme.xs.XSParser;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;


/** <p>This class invokes the JaxMe compiler.</p>
 */
public class JaxMeServlet extends BaseServlet {
	private class StoringEntityResolver implements EntityResolver {
		private final File schemaDir;
		private final Map urlMap = new HashMap();
		public StoringEntityResolver(File pSchemaDir) {
			schemaDir = pSchemaDir;
		}
		
		public InputSource resolveEntity(String pPublicId, String pSystemId) throws SAXException, IOException {
			try {
				URL url = new URL(pSystemId);
				String fileName = (String) urlMap.get(url);
				if (fileName != null) {
					FileInputStream istream = new FileInputStream(new File(schemaDir, fileName));
					InputSource isource = new InputSource(istream);
					isource.setSystemId(url.toString());
					return isource;
				}
				
				String file = url.getFile();
				if (file == null) {
					file = "";
				} else {
					int offset = file.lastIndexOf('/');
					if (offset >= 0) {
						file = file.substring(offset+1);
					}
				}
				if ("".equals(file)) {
					file = "schema.xsd";
				}
				int offset = file.lastIndexOf('.');
				String prefix;
				String suffix;
				String numAsStr = "";
				if (offset > 0  &&  offset < file.length()) {
					prefix = file.substring(0, offset);
					suffix = file.substring(offset);
				} else {
					prefix = file;
					suffix = ".xsd";
				}
				File f;
				for (int num = 1;  ;  ++num) {
					f = new File(schemaDir, prefix + numAsStr + suffix);
					if (f.exists()) {
						numAsStr = "_" + num;
					} else {
						break;
					}
				}

				InputStream istream = url.openStream();
				schemaDir.mkdirs();
				FileOutputStream fos = new FileOutputStream(f);
				try {
					byte[] buffer = new byte[1024];
					for (;;) {
						int res = istream.read(buffer);
						if (res == -1) {
							break;
						} else if (res > 0) {
							fos.write(buffer, 0, res);
						}
					}
					istream.close();
					fos.close();
					fos = null;
				} finally {
					if (fos != null) { try { f.delete(); } catch (Throwable ignore) {} }
				}

				urlMap.put(url, f.getName());
				InputSource isource = new InputSource(new FileInputStream(f));
				isource.setSystemId(url.toString());
				return isource;
			} catch (Exception e) {
				JaxMeServlet.this.log("Failed to resolve URL " + pSystemId, e);
			}
			return null;
		}
	}

	public File createTempDir() throws IOException, ServletException {
		File f = File.createTempFile("jaxme", ".tmp", getWorkDir());
		f.delete();
		if (!f.mkdir()) {
			throw new ServletException("Unable to create temporary directory " + f.getAbsolutePath());
		}
		return f;
	}

	public void addContents(ZipOutputStream pZipFile, File pDirectory, String pDirName) throws IOException {
		File[] files = pDirectory.listFiles();
		for (int i = 0;  i < files.length;  i++) {
			File f = files[i];
			String name = pDirName.length() == 0 ? f.getName() : pDirName + "/" + f.getName();
			if (f.isDirectory()) {
				addContents(pZipFile, f, name);
			} else if (f.isFile()) {
				FileInputStream istream = new FileInputStream(f);
				try {
					ZipEntry zipEntry = new ZipEntry(name);
					pZipFile.putNextEntry(zipEntry);
					byte[] buffer = new byte[1024];
					for (;;) {
						int res = istream.read(buffer);
						if (res == -1) {
							break;
						} else if (res > 0) {
							pZipFile.write(buffer, 0, res);
						}
					}
					pZipFile.closeEntry();
					istream.close();
					istream = null;
				} finally {
					if (istream != null) { try { istream.close(); } catch (Throwable ignore) {} }
				}
			}
		}
	}

	public void removeDirectory(File pDirectory) throws ServletException {
		cleanDirectory(pDirectory);
		pDirectory.delete();
	}

	protected void doCompile(boolean pValidating, File pTempDir, URL pURL, HttpServletResponse pResponse)
			throws ServletException, IOException {
		Generator gen = new GeneratorImpl();
		gen.setTargetDirectory(new File(pTempDir, "src"));
		gen.setValidating(pValidating);
		gen.setSchemaReader(new JAXBSchemaReader());
		gen.setEntityResolver(new StoringEntityResolver(new File(pTempDir, "schema")));
		try {
			gen.generate(pURL);
		} catch (Exception e) {
			throw new ServletException(e);
		}
		
		pResponse.setContentType("application/zip");
		pResponse.setHeader("Content-Disposition", "attachment; filename=\"jaxmeGeneratedSrc.zip\"");
		ZipOutputStream zipOutputStream = new ZipOutputStream(pResponse.getOutputStream());
		addContents(zipOutputStream, pTempDir, "");
		zipOutputStream.close();
		removeDirectory(pTempDir);
		pTempDir = null;
	}

	protected void doValidate(boolean pValidating, File pTempDir, URL pURL, HttpServletResponse pResponse)
			throws ServletException, IOException {
		XSParser parser = new XSParser();
		parser.setValidating(pValidating);
		InputSource isource = new InputSource(pURL.toString());
		try {
			parser.parse(isource);
		} catch (Exception e) {
			throw new ServletException(e);
		}
	}

	public void doGet(HttpServletRequest pRequest, HttpServletResponse pResponse)
	    	throws ServletException, IOException {
		String s = pRequest.getParameter("url");
		if (s == null  ||  s.length() == 0) {
			throw new ServletException("Missing or empty request parameter: " + s);
		}
		URL url;
		try {
			url = new URL(s);
		} catch (MalformedURLException e) {
			throw new ServletException("Malformed URL: " + s);
		}
		
		boolean isValidating = Boolean.valueOf(pRequest.getParameter("isValidating")).booleanValue();
		File f = createTempDir();

		String what = pRequest.getParameter("what");

		boolean forward = false;
		try {
			if ("compile".equals(what)) {
				try {
					doCompile(isValidating, f, url, pResponse);
				} catch (ServletException e) {
					pRequest.setAttribute("error", e);
					forward = true;
				}
			} else if ("validate".equals(what)) {
				doValidate(isValidating, f, url, pResponse);
				pRequest.setAttribute("success", Boolean.TRUE);
				forward = true;
			} else {
				throw new ServletException("You must choose a proper action: Either 'compile' or 'validate'.");
			}

			f = null;
		} finally {
			if (f != null) { try { removeDirectory(f); } catch (Throwable ignore) {} }
		}

		if (forward) {
			pRequest.getRequestDispatcher("index.jsp").forward(pRequest, pResponse);
		}
	}

	public void doPost(HttpServletRequest pRequest, HttpServletResponse pResponse)
			throws ServletException, IOException {
		// No actual difference between GET and POST ...
		doGet(pRequest, pResponse);
	}
}
