/***********************************************************************************
 *
 * Copyright (c) 2003, 2004, 2005, 2006 The Sakai Foundation.
 * 
 * Licensed under the Educational Community License, Version 1.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.opensource.org/licenses/ecl1.php
 * 
 * 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.sakaiproject.archive.impl;

import java.io.File;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;
import java.util.Vector;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.authz.api.AuthzGroup;
import org.sakaiproject.authz.api.GroupNotDefinedException;
import org.sakaiproject.authz.api.Role;
//import org.sakaiproject.authz.cover.AuthzGroupService;
import org.sakaiproject.authz.api.AuthzGroupService;
import org.sakaiproject.component.api.ServerConfigurationService;
import org.sakaiproject.entity.api.EntityManager;
import org.sakaiproject.entity.api.EntityProducer;
import org.sakaiproject.exception.IdUnusedException;
import org.sakaiproject.site.api.Site;
import org.sakaiproject.site.api.SiteService;
//import org.sakaiproject.site.cover.SiteService;
import org.sakaiproject.time.api.Time;
//import org.sakaiproject.time.cover.TimeService;
import org.sakaiproject.time.api.TimeService;
import org.sakaiproject.user.api.User;
import org.sakaiproject.user.api.UserDirectoryService;
import org.sakaiproject.content.api.ContentHostingService;
//import org.sakaiproject.user.cover.UserDirectoryService;
import org.sakaiproject.util.Xml;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

public class SiteArchiver {
	
	private static Log M_log = LogFactory.getLog(SiteArchiver.class);
	
	/** Dependency: ServerConfigurationService. */
	protected ServerConfigurationService m_serverConfigurationService = null;
	public void setServerConfigurationService(ServerConfigurationService service) {
		m_serverConfigurationService = service;
	}
	/** Dependency: EntityManager. */
	protected EntityManager m_entityManager = null;
	public void setEntityManager(EntityManager service) {
		m_entityManager = service;
	}
	
	/** Dependency: SiteService */
	protected SiteService m_siteService = null;
	public void setSiteService(SiteService service) {
		m_siteService = service;
	}
	
	/** Dependency: AuthzService */
	protected AuthzGroupService m_authzGroupService = null;
	public void setAuthzGroupService(AuthzGroupService service) {
		m_authzGroupService = service;
	}
	
	/** Dependency: UserDirectoryService */
	protected UserDirectoryService m_userDirectoryService = null;
	public void setUserDirectoryService(UserDirectoryService service) {
		m_userDirectoryService = service;
	}
	
	/** Dependency: TimeService */
	protected TimeService m_timeService = null;
	public void setTimeService(TimeService service) {
		m_timeService = service;
	}
	
	/** Dependency: ContentHosting */
	protected ContentHostingService m_contentHostingService = null;
	public void setContentHostingService(ContentHostingService service) {
		m_contentHostingService = service;
	}

	public String archive(String siteId, String m_storagePath, String fromSystem)
	{
		StringBuffer results = new StringBuffer();

		if (M_log.isDebugEnabled())
			M_log.debug("archive(): site: " + siteId);

		Site theSite = null;
		try
		{
			theSite = m_siteService.getSite(siteId);
		}
		catch (IdUnusedException e)
		{
			results.append("Site: " + siteId + " not found.\n");
			M_log.warn("archive(): site not found: " + siteId);
			return results.toString();
		}

		// collect all the attachments we need
		List attachments = m_entityManager.newReferenceList();

		Time now = m_timeService.newTime();

		// this is the folder we are writing files to
		String storagePath = m_storagePath + siteId + "-archive/";

		// create the directory for the archive
		File dir = new File(m_storagePath + siteId + "-archive/");
		dir.mkdirs();

		// for each registered ResourceService, give it a chance to archve
		List services = m_entityManager.getEntityProducers();
		for (Iterator iServices = services.iterator(); iServices.hasNext();)
		{
			EntityProducer service = (EntityProducer) iServices.next();
			if (service == null) continue;
			if (!service.willArchiveMerge()) continue;

			Document doc = Xml.createDocument();
			Stack stack = new Stack();
			Element root = doc.createElement("archive");
			doc.appendChild(root);
			root.setAttribute("source", siteId);
			root.setAttribute("server", m_serverConfigurationService.getServerId());
			root.setAttribute("date", now.toString());
			root.setAttribute("system", fromSystem);
			
			stack.push(root);

			try {
				String msg = service.archive(siteId, doc, stack, storagePath, attachments);
				results.append(msg);
			}
			catch (Throwable t)
			{
				results.append(t.toString() + "\n");
			}

			stack.pop();
			
			String fileName = storagePath + service.getLabel() + ".xml";
			Xml.writeDocument(doc, fileName);
		}

		// archive the collected attachments
		if (attachments.size() > 0)
		{
			Document doc = Xml.createDocument();
			Stack stack = new Stack();
			Element root = doc.createElement("archive");
			doc.appendChild(root);
			root.setAttribute("source", siteId);
			root.setAttribute("server", m_serverConfigurationService.getServerId());
			root.setAttribute("date", now.toString());
			root.setAttribute("system", fromSystem);
			
			stack.push(root);

			//SWG String msg = org.sakaiproject.content.cover.ContentHostingService.archiveResources(attachments, doc, stack, storagePath);
			String msg = m_contentHostingService.archiveResources(attachments, doc, stack, storagePath);
			results.append(msg);

			stack.pop();

			String fileName = storagePath + "attachment.xml";
			Xml.writeDocument(doc, fileName);
		}

		// *** Site

		
		Document doc = Xml.createDocument();
		Stack stack = new Stack();
		Element root = doc.createElement("archive");
		doc.appendChild(root);
		root.setAttribute("site", siteId);
		root.setAttribute("date", now.toString());
		root.setAttribute("system", fromSystem);
		
		stack.push(root);

		String msg = archiveSite(theSite, doc, stack);
		results.append(msg);
		
		stack.pop();
		Xml.writeDocument(doc, m_storagePath + siteId + "-archive/site.xml");


		// *** Users
		doc = Xml.createDocument();
		stack = new Stack();
		root = doc.createElement("archive");
		doc.appendChild(root);
		root.setAttribute("site", siteId);
		root.setAttribute("date", now.toString());
		root.setAttribute("system", fromSystem);
		
		stack.push(root);
		
		msg = archiveUsers(theSite, doc, stack);
		results.append(msg);

		stack.pop();
		Xml.writeDocument(doc, m_storagePath + siteId + "-archive/user.xml");


		return results.toString();

	}	// archive
	
	/**
	* Archive the site definition.
	* @param site the site.
	* @param doc The document to contain the xml.
	* @param stack The stack of elements, the top of which will be the containing
	* element of the "site" element.
	*/
	
	protected String archiveSite(Site site, Document doc, Stack stack)
	{
		Element element = doc.createElement(SiteService.APPLICATION_ID);
		((Element)stack.peek()).appendChild(element);
		stack.push(element);
		
		Element siteNode = site.toXml(doc, stack);
		stack.push(siteNode);	
		
		// to add the realm node with user list into site
		List roles = new Vector();
		String realmId = m_siteService.siteReference(site.getId()); //SWG "/site/" + site.getId();
		try
		{
			Role role = null;
			AuthzGroup realm = m_authzGroupService.getAuthzGroup(realmId);
			
			Element realmNode = doc.createElement("roles");
			((Element)stack.peek()).appendChild(realmNode);
			stack.push(realmNode);
			
			roles.addAll(realm.getRoles());
			
			for (int i = 0; i< roles.size(); i++)
			{
				role = (Role) roles.get(i);
				String roleId = role.getId();
				Element node = doc.createElement(roleId);
				realmNode.appendChild(node);
				
				List users = new Vector();
				users.addAll(realm.getUsersHasRole(role.getId()));
				for (int j = 0; j < users.size(); j++)
				{			
					Element abilityNode = doc.createElement("ability");
					abilityNode.setAttribute("roleId", roleId);
					abilityNode.setAttribute("userId", ((String)users.get(j)));
					node.appendChild(abilityNode);
				}
			}
		}
		catch(Exception any)
		{
			M_log.warn("archve: exception archiving site: "+ site.getId() + ": ", any);
		}
	
		stack.pop();
		
		return "archiving Site: " + site.getId() + "\n";
	
	}	// archiveSite

	/**
	* Archive the users defined in this site (internal users only).
	* @param site the site.
	* @param doc The document to contain the xml.
	* @param stack The stack of elements, the top of which will be the containing
	* element of the "site" element.
	*/
	protected String archiveUsers(Site site, Document doc, Stack stack)
	{
		Element element = doc.createElement(UserDirectoryService.APPLICATION_ID);
		((Element)stack.peek()).appendChild(element);
		stack.push(element);
	
		try
		{
			// get the site's user list
			List users = new Vector();
			String realmId = m_siteService.siteReference(site.getId()); //SWG "/site/" + site.getId();
			try
			{
				AuthzGroup realm = m_authzGroupService.getAuthzGroup(realmId);
				users.addAll(m_userDirectoryService.getUsers(realm.getUsers()));
				Collections.sort(users);
				for (int i = 0; i < users.size(); i++)
				{
					User user = (User) users.get(i);
					user.toXml(doc, stack);
				}
			}
			catch (GroupNotDefinedException e)
			{
				M_log.warn(e, e);
			}
			catch (Exception any) {
				M_log.warn(any,any);
			}
	
		}
		catch (Exception any)
		{
			M_log.warn(any,any);
		}
	
		stack.pop();
		
		return "archiving the users for Site: " + site.getId() + "\n";
	
	}	// archiveUsers
}
