/*
 * $Id: ArchiveCategory.java,v 1.29 2007/11/16 14:47:31 marbobo Exp $
 * 
 * Copyright (c) Siemens AG 2008 
 *           (c) Universität Paderborn 2008 
 * Licensed under EUPL 1.1. 
 *
 * $Log: ArchiveCategory.java,v $
 * Revision 1.29  2007/11/16 14:47:31  marbobo
 * + Jetzt werden beim Archivieren auch alle Versionen für ein Dokument angezeigt
 *
 * Revision 1.28  2007/11/05 08:53:41  marbobo
 * + Bugfix (Statt List werden HashSet benutzt)
 *
 * Revision 1.27  2007/10/31 10:19:31  marbobo
 * + Dateinamen für HTML-Dokumente beim Archivieren korrigiert
 *
 * Revision 1.26  2007/10/30 15:48:03  korowa
 * Beim Archivieren und Exportieren werden bei Documentennamen die Ids miteingegeben
 *
 * Revision 1.25  2007/10/25 14:50:40  korowa
 * Readme Datei wird bei jedem Archiv hinzugefügt
 *
 * Revision 1.24  2007/09/17 13:23:27  marbobo
 * + Bugfix: Beim Archivieren werden im Filesystem statt den Kategorienamen die Id als Ordnername verwendet (Fix.bzgl Dateinamenlängenbeschränkung)
 *
 * Revision 1.23  2007/09/17 08:38:07  marbobo
 * + catName2PathName und docName2PathName als "public static final" deklariert
 *
 * Revision 1.22  2007/08/28 12:16:10  korowa
 * Export- und Importfunktionen hinzugefügt
 *
 * Revision 1.21  2006/04/11 19:45:03  marbobo
 * + Komprimierung auf 7z-Format umgestellt
 *
 * Revision 1.20  2006/03/29 00:06:37  marbobo
 * + Bugfix
 *
 * Revision 1.19  2005/07/28 19:58:53  marbobo
 * + Bugfix bei Dateigrößeanzeige
 *
 * Revision 1.18  2005/03/15 22:32:41  marbobo
 * + Unicode-Unterstützung
 *
 * Revision 1.17  2004/11/10 15:02:45  koppy
 * Projektweite Überarbeitung von Error-Logs, Stacktraces,..
 *
 * Revision 1.16  2004/07/20 08:33:14  jonyp
 * added copyright info + cvs log
 *
 * Revision 1.15  2004/06/21 14:55:12  marbobo
 * Bugfix
 *
 * Revision 1.14  2004/05/09 16:25:53  marbobo
 * + Internationalisierung
 *
 * Revision 1.13  2004/04/27 18:56:23  marbobo
 * + Virtuelle Dokumente archivieren
 *
 * Revision 1.12  2004/04/15 20:37:17  marbobo
 * + Archivierung von Projekten hinzugefügt
 *
 * Revision 1.11  2004/03/13 19:15:11  marbobo
 * Unbenutze lokale Variablen projektweit entsorgt
 *
 * Revision 1.10  2004/03/12 15:44:17  bigmac
 * Methoden für (lokale) zusätzliche Attribute aus VKCDynAttributeSerializer 
 * nach VKCAddAttributeSerializer verschoben
 * 
 * Unbenutze private Methoden projektweit entsorgt
 *
 * Revision 1.9  2004/03/07 13:45:49  bigmac
 * Anpassungen an VKCDocumentTypeSerializer, VKCDocumentClassSerializer, VKCDocumentStatusSerializer
 *
 * Revision 1.8  2004/03/05 16:53:06  teng
 * Templatepfade fuer index und xml-datei werden direkt in vkc.misc.ArchiveCategoryTemplateParser definiert.
 *
 * Revision 1.7  2004/03/02 23:05:47  bigmac
 * Anpassungen an VKCDocumentSerializer und VKCCategorySerializer
 *
 */

package vkc.misc;

import java.io.File;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.zip.ZipFile;

import vkc.db.entities.VKCCategory;
import vkc.db.entities.VKCDocument;
import vkc.db.entities.VKCSubCategory;
import vkc.db.entities.VKCUser;
import vkc.db.serializer.VKCCategorySerializer;
import vkc.db.serializer.VKCDocumentSerializer;
import vkc.exception.DBException;
import vkc.util.FileUtils;
import vkc.util.VKCStringUtils;
import vkc.util.ZipUtils;
import vkc.view.WebShare;
import vkc.view.bean.ResourceInfo;

/**
 * a class for creating zip file using the documents under a category.
 */
public class ArchiveCategory {
	public static final String indexFileName = "vkc_index.html";
	public static final String projectcontactFileName = "vkc_index_projectcontacts.html";
	public static final String projectstateFileName = "vkc_index_projectstate.html";
	public static final String xmlFileName = "vkc_index.xml";
    public static final String documentIndexFileName = "vkc_doc_";

	/**
	 * hold the names of all categories
	 */
	private Vector categories = new Vector();

	/**
	 * This method compresses all documents under a category in one zip file.
	 * @param cat 
	 * @param targetDirectory Where the zip file should be created
	 * @param zipFileName If null the zip file has the name likes the category name
	 * @param recursivly If true the subcategories will be included
	 * @param includeVersion If true the documents of all versions will be included
	 * @return
	 * @throws Exception
	 */
	public File createZipFile(VKCCategory cat, String targetDirectory, String zipFileName, boolean recursivly, boolean includeVersion, boolean withVirtualDocs, VKCUser user, ResourceInfo resource) throws Exception {
		String currentPath = targetDirectory;
		//target path for saving the zip file later
        currentPath = createFiles(currentPath, cat, recursivly, includeVersion, withVirtualDocs, user, resource);

        //the path corresponding to the topmost category
		File inputFile = new File(VKCStringUtils.decodeXmlEntities(currentPath));

		String dateString = new SimpleDateFormat("-yyyy-MM-dd-kk-mm-ss").format(new Date());

		if (zipFileName == null) {
            //zipFileName = inputFile.getName() + dateString;
			zipFileName = catName2PathName(VKCStringUtils.decodeXmlEntities(cat.getName())) + dateString;
		}

		File readmeFile = new File(inputFile.getParent(), resource.getMessage("vkc.archive.readme.name"));
        FileUtils.saveString(readmeFile, resource.getMessage("vkc.archive.readme"), "UTF-8", true);       

		File zipFile = new File(targetDirectory + File.separator + zipFileName + ".7z");
		List source = new ArrayList();
		source.add(readmeFile);
		source.add(inputFile);
		ZipUtils.sevenZip(source, zipFile);

		//delete the directory (keep the zip file only)
		FileUtils.deleteDirectory(inputFile);
		readmeFile.delete();
		return zipFile;
	}

	public String createFiles(String parentPath, VKCCategory cat, boolean recursivly, boolean includeVersion, boolean withVirtualDocs, VKCUser user, ResourceInfo resource) throws DBException {
		categories.add(cat);
		//String catName = cat.getName();

		//String pathName = catName2PathName(VKCStringUtils.decodeXmlEntities(catName));
        String pathName = cat.getCategoryId().toString();
		String currentPath = parentPath + File.separator + pathName;

		ArchiveCategoryTemplateParser parser = null;

		try {
			parser = new ArchiveCategoryTemplateParser();
		} catch (Exception ex) {
			VKCLogManager.infoLog(this, "Get template failed:" + ex);
		}

		if (parser != null) {
			parser.setUser(user);
			parser.setCurrentCategory(cat);
		}

		if (!new File(VKCStringUtils.decodeXmlEntities(currentPath)).mkdir()) {
			VKCLogManager.infoLog(this, "Fail to create Path:" + currentPath);
		} else {
			if (recursivly) {
				Iterator cats = cat.getSubCategories().iterator();
				VKCCategory subCat;

				while (cats.hasNext()) {
					try {
						subCat = VKCCategorySerializer.retrieveCategory(((VKCSubCategory) cats.next()).getSubCategoryId());
/*                        
						String subCatName = subCat.getName();

						if (parser != null) {
							parser.addSubCategory(subCat, catName2PathName(VKCStringUtils.decodeXmlEntities(subCatName)) + "/" + ArchiveCategory.indexFileName);
						}
*/

                        String subCatPath = subCat.getCategoryId().toString();

                        if (parser != null) {
                            parser.addSubCategory(subCat, subCatPath + "/" + ArchiveCategory.indexFileName);
                        }

                        createFiles(currentPath, subCat, recursivly, includeVersion, withVirtualDocs, user, resource);
					} catch (Exception ex) {
						VKCLogManager.errorLog(this, "", ex.getMessage(), ex);
					}
				}
			}

			Iterator docs = cat.getDocuments().iterator();
			VKCDocument doc;
	        HashMap versionsMap = new HashMap();
			List documentVersions = null; 

			while (docs.hasNext()) {
				doc = (VKCDocument) docs.next();

				if (parser != null) {
					parser.addDocument(doc);
				}
				
				documentVersions = new ArrayList();
				writeDocumentData(currentPath, doc, includeVersion, parser);
				documentVersions.add(doc);
				
				if (includeVersion) {
					Integer preDocId = doc.getPreDocId();

					while (preDocId != null) {
						try {
							VKCDocument preDoc = VKCDocumentSerializer.retrieveDocument(preDocId);
							writeDocumentData(currentPath, preDoc, includeVersion, parser);
			                documentVersions.add(preDoc);
							preDocId = preDoc.getPreDocId();
						} catch (Exception ex) {
							preDocId = null;
							VKCLogManager.infoLog(this, "retrieve document:" + ex);
						}
					}
				}

				versionsMap.put(currentPath + File.separator + documentIndexFileName + "id_" + doc.getDocumentId() + ".html", documentVersions);
			}

			if (withVirtualDocs) {
				Iterator virtualdocs = cat.getVirtualDocuments(user).iterator();
				VKCDocument virtualdoc;

				while (virtualdocs.hasNext()) {
					virtualdoc = (VKCDocument) virtualdocs.next();

					if (parser != null) {
						parser.addVirtualDocument(virtualdoc);
					}

	                documentVersions = new ArrayList();
					writeDocumentData(currentPath, virtualdoc, includeVersion, parser);
                    documentVersions.add(virtualdoc);

					if (includeVersion) {
						Integer preDocId = virtualdoc.getPreDocId();

						while (preDocId != null) {
							try {
								VKCDocument preDoc = VKCDocumentSerializer.retrieveDocument(preDocId);
								writeDocumentData(currentPath, preDoc, includeVersion, parser);
	                            documentVersions.add(preDoc);
								preDocId = preDoc.getPreDocId();
							} catch (Exception ex) {
								preDocId = null;
								VKCLogManager.infoLog(this, "retrieve document:" + ex);
							}
						}
					}

	                versionsMap.put(currentPath + File.separator + documentIndexFileName + "id_" + virtualdoc.getDocumentId() + ".html", documentVersions);
				}
			}

			if (parser != null) {
				parser.setResources(resource);

				//add categories without the current category
				for (int i = 0; i < categories.size() - 1; i++) {
					parser.addParentCategory((VKCCategory) categories.get(i));
				}

				parser.createIndexFile(currentPath + File.separator + ArchiveCategory.indexFileName, includeVersion);

				for (Iterator iterator = versionsMap.entrySet().iterator(); iterator.hasNext();) {
                    Map.Entry entry = (Map.Entry) iterator.next();
                    String key = (String)entry.getKey();
                    List value = (List)entry.getValue();
                    
                    if (value.size() > 1) {
                        parser.createDocumentIndexFile(key, value);
                    }
                }

                if (cat.isProjectCategory()) {
					parser.createProjectContactFile(currentPath + File.separator + ArchiveCategory.projectcontactFileName);
					parser.createProjectStateFile(currentPath + File.separator + ArchiveCategory.projectstateFileName);
				}

				parser.createXMLFile(currentPath + File.separator + ArchiveCategory.xmlFileName);
				//after the work with current category remove this category
				categories.remove(categories.size() - 1);
			}
		}

		return currentPath;
	}

	/**
	 * 
	 * @param currentPath The directory to write the file if this doc is a document other than link and note
	 * @param doc
	 * @param if true, document will have a file name with the profix "-Version-VersionNo.fileTypeExtension" 
	 */
	private void writeDocumentData(String currentPath, VKCDocument doc, boolean includeVersion, ArchiveCategoryTemplateParser parser) {
		String docTypeExtension = doc.getDocumentType().getExtension();

		if (!docTypeExtension.equalsIgnoreCase("link") && !docTypeExtension.equalsIgnoreCase("note")) {
            String fileName = docName2PathName(VKCStringUtils.decodeXmlEntities(doc.getName()));

			try {
				if (includeVersion) {
					int i = fileName.lastIndexOf('.');

					if (i > -1) {
						if (fileName.endsWith(".")) {
							fileName = fileName + "-Version-" + doc.getVersionNo()+"-Id-" + doc.getDocumentId();
						} else {
							fileName = fileName.substring(0, i)
									+ "-Version-"
									+ doc.getVersionNo()
									+"-Id-"
									+doc.getDocumentId()
									+ "."
									+ (docTypeExtension.equals("?") 
										? fileName.substring(i+1, fileName.length())
										: docTypeExtension);
						}
					} else {
						fileName = fileName + "-Version-" + doc.getVersionNo()+ "-Id-" + doc.getDocumentId();
					}
				} else {
					int i = fileName.lastIndexOf('.');

					if (i > -1) {
						if (fileName.endsWith(".")) {
							fileName = fileName +"-Id-"+doc.getDocumentId();
						} else {
							fileName = fileName.substring(0, i)									
									+"-Id-"
									+doc.getDocumentId()
									+ "."
									+ (docTypeExtension.equals("?") 
										? fileName.substring(i+1, fileName.length())
										: docTypeExtension);
						}
					} else {
						fileName = fileName +"-Id-"+doc.getDocumentId();
					}
				
				}
				
				InputStream stream = null;
				
				if (doc.getZipped() != null && doc.getZipped().intValue() != 0 ) {
                    File zippedFile = WebShare.archive.getFile(doc.getArchiveId());
                    ZipFile zipFile = new ZipFile(zippedFile);
                    String fn = doc.getDisplayName();
                    stream = ZipUtils.getUnzippedStream(zipFile, fn);
				} else {
				    stream = doc.getInputStream();
				}
				
				FileUtils.saveInputStream(new File(VKCStringUtils.decodeXmlEntities(currentPath) + File.separator + fileName), stream, false);
			} catch (Exception ex) {
				VKCLogManager.infoLog(this, "writedocumentdata:" + ex);
			}

            parser.addDocumentFilename(doc.getDocumentId(), fileName);
		} else {
			parser.addDocumentFilename(doc.getDocumentId(), VKCStringUtils.decodeXmlEntities(doc.getName()));
		}
	}

	public static final String catName2PathName(String catName) {
		char[] result = catName.toCharArray();

		for (int i = result.length - 1; i >= 0; i--) {
			switch (result[i]) {
				case '.':
				case ':':
				case '*':
				case '?':
				case '<':
				case '>':
				case '|':
				case '\\':
				case '/':
				case '"':
					result[i] = '_';
					break;
				default:
					if (result[i] < 32) {
						result[i] = '_';
					}
			}
		}

		return new String(result).trim();
	}

    public static final  String docName2PathName(String docName) {
		char[] result = docName.toCharArray();

		for (int i = result.length - 1; i >= 0; i--) {
			switch (result[i]) {
				case ':':
				case '*':
				case '?':
				case '<':
				case '>':
				case '|':
				case '\\':
				case '/':
				case '"':
					result[i] = '_';
					break;
				default:
					if (result[i] < 32) {
						result[i] = '_';
					}
			}
		}

		return new String(result).trim();
	}
}