/*
 * Copyright (C) 2003-2007 eXo Platform SAS.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License
 * as published by the Free Software Foundation; either version 3
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see<http://www.gnu.org/licenses/>.
 */

package org.exoplatform.processes.publishing;

import java.text.DateFormatSymbols;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;

import javax.jcr.Node;
import javax.jcr.Session;

import org.exoplatform.container.ExoContainer;
import org.exoplatform.container.ExoContainerContext;
import org.exoplatform.services.jcr.RepositoryService;
import org.exoplatform.services.jcr.core.ExtendedNode;
import org.exoplatform.services.jcr.core.ManageableRepository;
import org.exoplatform.services.jcr.ext.app.SessionProviderService;
import org.exoplatform.services.jcr.ext.common.SessionProvider;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.services.resources.LocaleConfigService;
import org.jbpm.context.exe.ContextInstance;
import org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.graph.exe.Token;
import org.jbpm.job.Timer;
import org.jbpm.scheduler.SchedulerService;
import org.jbpm.svc.Services;

/**
 * Created by The eXo Platform SARL
 * Author : Pham Xuan Hoa
 *          hoa.pham@exoplatform.com
 * Dec 18, 2007  
 */
public class ProcessUtil {
  
  public final static String CURRENT_STATE = "exo:currentState".intern();
  public final static String CURRENT_LOCATION = "exo:currentLocation".intern();
  public final static String REQUEST_FOR_VALIDATION = "Request For Validation".intern();
  public final static String VALIDATED = "Validated".intern();
  public final static String PENDING = "Waiting For Publishing".intern();
  public final static String REFUSED = "Refused".intern();
  public final static String DISAPPROVED = "Disapproved".intern();
  public final static String DELEGATED = "Delegated".intern();
  public final static String ABORTED = "Aborted".intern();
  public final static String EXPIRED = "Expired".intern();
  public final static String LIVE = "Live".intern();
  public final static String BACKUP = "Backup".intern();
  public final static String IN_TRASH = "In Trash".intern();
  
  public final static String EXO_PUBLISH_LOCATION = "exo:publishLocation".intern();
  public final static String EXO_PENDING_LOCATION = "exo:pendingLocation".intern();
  public final static String EXO_BACKUP_LOCATION = "exo:backupLocation".intern();
  public final static String EXO_TRASH_LOCATION = "exo:trashLocation".intern();
   
  public final static String ACTION_REASON = "exo:actionComment".intern();
  
  public final static int REPOSITORY_INDEX = 0;
  public final static int WORKSPACE_INDEX = 1;
  public final static int PATH_INDEX = 2;
  
  public final static String EXO_VALIDATIONREQUEST = "exo:validationRequest".intern();
  public final static String EXO_CONENT_STATE = "exo:publishingState".intern();
  public final static String CURRENT_STATE_PROP = "exo:currentState".intern();
  public final static String CURRENT_WORKSPACE_PROP = "exo:currentWorkspace".intern();
  public final static String CURRENT_REPOSITORY_PROP = "exo:currentRepository".intern();
  public final static String CURRENT_PATH_PROP = "exo:currentPath".intern();
  
  private static Log log = ExoLogger.getLogger(ProcessUtil.class);
  
  public static void createTimer(ExecutionContext context, Timer timer) {
  	SchedulerService schedulerService = (SchedulerService) Services.getCurrentService(Services.SERVICENAME_SCHEDULER);
    schedulerService.createTimer(timer);
  }
  
  public static void deleteTimer(ExecutionContext context, String timer, Token token) {
  	SchedulerService schedulerService = (SchedulerService) Services.getCurrentService(Services.SERVICENAME_SCHEDULER);
    schedulerService.deleteTimersByName(timer, token);
  }
  
  public static void requestForValidation(ExecutionContext context) {
    context.setVariable(CURRENT_STATE,REQUEST_FOR_VALIDATION);
    String[] location = getCurrentLocation(context);
    String repository = location[REPOSITORY_INDEX];
    String workspace = location[WORKSPACE_INDEX];
    String path = location[PATH_INDEX] ;
    try{
      Node requestNode = getNode(context, repository,workspace,path, getSessionProvider());
      if(!requestNode.isNodeType(EXO_CONENT_STATE)) {
        requestNode.addMixin(EXO_CONENT_STATE) ;   
      } 
      if (requestNode.isNodeType(EXO_CONENT_STATE)) {
    	  requestNode.setProperty(CURRENT_STATE,REQUEST_FOR_VALIDATION);      
      }
      if(!requestNode.isNodeType(EXO_VALIDATIONREQUEST)) {
        requestNode.addMixin(EXO_VALIDATIONREQUEST) ; 
      }      
      if(requestNode.isNodeType(EXO_VALIDATIONREQUEST)) {
      	String requester = ((ExtendedNode)requestNode).getProperty("exo:owner").getString();
      	requestNode.setProperty("exo:requester",requester) ;
      	requestNode.setProperty("exo:requestDate",new GregorianCalendar());
      }
      requestNode.getSession().save();      
    } catch (Exception e) {
      log.error(e);
    }    
  }
  
  public static void approve(ExecutionContext context) {
    context.setVariable(CURRENT_STATE,VALIDATED);
    String[] location = getCurrentLocation(context);
    String repository = location[REPOSITORY_INDEX];
    String workspace = location[WORKSPACE_INDEX];
    String path = location[PATH_INDEX] ;
    try {
      Node validatedNode = getNode(context, repository,workspace,path,getSessionProvider()) ;
      if(!validatedNode.isNodeType("exo:approved")) {
        validatedNode.addMixin("exo:approved");
      }      
      if (validatedNode.isNodeType("exo:approved")) {
    	  validatedNode.setProperty("exo:approver",getActorId(context));
	      validatedNode.setProperty("exo:approvedDate",new GregorianCalendar());
	      String approveComment = (String) context.getVariable(ACTION_REASON) ;
	      if(approveComment != null && approveComment.length()!= 0) {
	        validatedNode.setProperty("exo:approvedComment",approveComment);
	      }
      }
      validatedNode.setProperty(CURRENT_STATE_PROP,"Approved");
      validatedNode.getSession().save();
    } catch (Exception e) {
      log.error(e);
    }
  }
  
  public static void disapprove(ExecutionContext context) {    
    context.setVariable(CURRENT_STATE,DISAPPROVED);
    String[] location = getCurrentLocation(context);
    String repository = location[REPOSITORY_INDEX];
    String workspace = location[WORKSPACE_INDEX];
    String path = location[PATH_INDEX] ;
    try {
      Node disapprovedNode = getNode(context, repository,workspace,path,getSessionProvider()) ;
      if (!disapprovedNode.isNodeType("exo:disapproved")) {
        disapprovedNode.addMixin("exo:disapproved");
      }
      if (disapprovedNode.isNodeType("exo:disapproved")) {
	      disapprovedNode.setProperty("exo:contradictor",getActorId(context));
	      disapprovedNode.setProperty("exo:disaprovedDate",new GregorianCalendar());
	      String approveComment = (String) context.getVariable(ACTION_REASON);
	      if(approveComment != null && approveComment.length()!= 0) {
	        disapprovedNode.setProperty("exo:disapprovedReason",approveComment);
	      } 
	      disapprovedNode.setProperty(CURRENT_STATE_PROP,"Disapproved");
      }
      disapprovedNode.getSession().save();
    } catch (Exception e) {
      log.error(e);
    }    
  } 
  
  public static void publish(ExecutionContext context) {
    context.setVariable(CURRENT_STATE,DISAPPROVED);
    String[] location = getCurrentLocation(context);
    String repository = location[REPOSITORY_INDEX];
    String workspace = location[WORKSPACE_INDEX];
    String path = location[PATH_INDEX] ;
    SessionProvider provider = SessionProvider.createSystemProvider();
    try {
      Node publishedNode = getNode(context, repository,workspace,path,provider) ;
      if(!publishedNode.isNodeType("exo:published")) {
        publishedNode.addMixin("exo:published");
      }    
      if(publishedNode.isNodeType("exo:published")) {
	      Date startDate = (Date)context.getVariable("startDate");
	      Date endDate = (Date)context.getVariable("endDate");
	      Calendar calendar = new GregorianCalendar();
	      calendar.setTime(startDate);      
	      publishedNode.setProperty("exo:startPublication",calendar);
	      if(endDate != null) {
	        calendar.setTime(endDate);
	        publishedNode.setProperty("exo:endPublication",new GregorianCalendar()); 
	      }                  
      }    
      publishedNode.setProperty(CURRENT_STATE_PROP,LIVE);      
      publishedNode.getSession().save();
    } catch (Exception e) {
      log.error(e);
    }
    provider.close();
  }
  
  public static void waitForPublish(ExecutionContext context) {    
    context.setVariable(CURRENT_STATE,PENDING);
    String[] location = getCurrentLocation(context);
    String repository = location[REPOSITORY_INDEX];
    String workspace = location[WORKSPACE_INDEX];
    String path = location[PATH_INDEX] ;
    SessionProvider provider = SessionProvider.createSystemProvider();
    try {
      Node pendingNode = getNode(context, repository,workspace,path,provider) ;
      if(!pendingNode.isNodeType("exo:pending")) {
        pendingNode.addMixin("exo:pending");
      }
      if(pendingNode.isNodeType("exo:pending")) {
	      Date startDate = (Date)context.getVariable("startDate");                  
	      pendingNode.setProperty("exo:pendingStart",new GregorianCalendar());
	      Calendar calendar = new GregorianCalendar();
	      calendar.setTime(startDate);
	      pendingNode.setProperty("exo:pendingEnd",calendar);
	      pendingNode.setProperty(CURRENT_STATE_PROP,PENDING);      
      }
      pendingNode.getSession().save();
    } catch (Exception e) {
      log.error(e);
    }
    provider.close();
  }
  
  public static void delegate(ExecutionContext context) {    
    context.setVariable(CURRENT_STATE,DELEGATED);
    String[] location = getCurrentLocation(context);
    String repository = location[REPOSITORY_INDEX];
    String workspace = location[WORKSPACE_INDEX];
    String path = location[PATH_INDEX] ;
    try {
      Node delegateNode = getNode(context, repository,workspace,path,getSessionProvider()) ;
      if(!delegateNode.isNodeType("exo:delegated")) {
        delegateNode.addMixin("exo:delegated");
      }
      if(delegateNode.isNodeType("exo:delegated")) {
	      delegateNode.setProperty("exo:assigner",getActorId(context));
	      delegateNode.setProperty("exo:delegatedDate",new GregorianCalendar());
	      String delegator = (String)context.getVariable("delegator") ;
	      delegateNode.setProperty("exo:delegator",delegator);      
	      String delegatedComment = (String) context.getVariable(ACTION_REASON);
	      if(delegatedComment != null && delegatedComment.length()!= 0) {
	        delegateNode.setProperty("exo:delegatedComment",delegatedComment);
	      }
      }
      delegateNode.setProperty(CURRENT_STATE_PROP,DELEGATED);
      delegateNode.getSession().save();
    } catch (Exception e) {
      log.error(e);
    }
  }
  
  public static void backup(ExecutionContext context) {
    context.setVariable(CURRENT_STATE,BACKUP);
    String[] location = getCurrentLocation(context);
    String repository = location[REPOSITORY_INDEX];
    String workspace = location[WORKSPACE_INDEX];
    String path = location[PATH_INDEX] ;
    SessionProvider provider = SessionProvider.createSystemProvider();
    try {
      Node backupNode = getNode(context, repository,workspace,path,provider) ;
      if(!backupNode.isNodeType("exo:backup")) {
        backupNode.addMixin("exo:backup");
      }      
      if(backupNode.isNodeType("exo:backup")) {
	      backupNode.setProperty("exo:backupDate",new GregorianCalendar());
	      backupNode.setProperty("exo:backupReason","DOCUMENT EXPIRED");
      }
      backupNode.setProperty(CURRENT_STATE_PROP,BACKUP);
      backupNode.getSession().save();      
    } catch (Exception e) {
      log.error(e);
    }
   provider.close(); 
  }
  
  public static void moveTrash(ExecutionContext context) {
    String sourceTransition = (String)context.getVariable(CURRENT_STATE);
    context.setVariable(CURRENT_STATE,IN_TRASH);
    String[] location = getCurrentLocation(context);
    String repository = location[REPOSITORY_INDEX];
    String workspace = location[WORKSPACE_INDEX];
    String path = location[PATH_INDEX] ;
    SessionProvider provider = SessionProvider.createSystemProvider();
    try {
      Node trashNode = getNode(context, repository,workspace,path,provider) ;
      if(!trashNode.isNodeType("exo:trashMovement")) {
        trashNode.addMixin("exo:trashMovement");
      }
      if(trashNode.isNodeType("exo:trashMovement")) {
	      trashNode.setProperty("exo:moveDate",new GregorianCalendar());
	      trashNode.setProperty("exo:moveReason",sourceTransition);
      }
      trashNode.setProperty(CURRENT_STATE_PROP,IN_TRASH);
      trashNode.getSession().save();
    } catch (Exception e) {
      log.error(e);
    }
    provider.close();
  } 
  
  public static Node getNode(ExecutionContext context, String repositoryName, String workspace, String path, SessionProvider provider) throws Exception {
    RepositoryService repositoryService = getService(context, RepositoryService.class);
    ManageableRepository repository= repositoryService.getRepository(repositoryName);
    Session session = provider.getSession(workspace,repository);    
    return (Node)session.getItem(path);
  }
  
  public static String getActorId(ExecutionContext context) {
    return (String)context.getVariable("initiator");
  }
  
  public static void setCurrentLocation(ExecutionContext context,String currentWorkspace,String currentPath) {
    String repository = (String)context.getVariable("repository");
    StringBuilder locationBuilder = new StringBuilder();
    locationBuilder.append(repository).append("::").append(currentWorkspace).append("::").append(currentPath);    
    context.setVariable(CURRENT_LOCATION,locationBuilder.toString());
  }
  
  public static String[] getCurrentLocation(ExecutionContext context) {
    String currentLocation = (String)context.getVariable(CURRENT_LOCATION);
    return currentLocation.split("::");
  }
  
  public static <T> T getService(ExecutionContext context, Class<T> type) {
  	ContextInstance contextInstance = context.getContextInstance();
    ExoContainer container = ExoContainerContext.getContainerByName((String)contextInstance.getVariable("exocontainer"));
    return  type.cast(container.getComponentInstanceOfType(type));       
  }
  
  public static String getAuthor(ExecutionContext context){
    String[] location = getCurrentLocation(context);
    SessionProvider provider = SessionProvider.createSystemProvider();    
    try{
      Node node = getNode(context, location[0],location[1],location[2],provider);
      return node.getProperty("exo:owner").getString();
    }catch (Exception e) {
    }finally {
      provider.close();
    }
    return getActorId(context);
  }
  
  public static String computeDestinationPath(ExecutionContext context, String srcPath,String destPath) {
    String realDestPath;
    String datePath = getDateLocation(context);
    String nodeName = srcPath.substring(srcPath.lastIndexOf("/")+1);
    if(destPath.endsWith("/")) {
      realDestPath = destPath.concat(datePath).concat(nodeName);
    }else {
      realDestPath = destPath.concat("/").concat(datePath).concat(nodeName);      
    }
    return realDestPath;
  }
  
  public static String getDateLocation(ExecutionContext context) { 
    LocaleConfigService configService = getService(context, LocaleConfigService.class);
    Locale locale = configService.getDefaultLocaleConfig().getLocale();
    Calendar calendar = new GregorianCalendar(locale);
    String[] monthNames = new DateFormatSymbols().getMonths();
    String currentYear  = Integer.toString(calendar.get(Calendar.YEAR)) ;    
    String currentMonth = monthNames[calendar.get(Calendar.MONTH)] ;    
    int weekday = calendar.get(Calendar.DAY_OF_WEEK);        
    int diff = 2 - weekday ;        
    calendar.add(Calendar.DATE, diff);    
    SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy") ;
    String startDateOfWeek = dateFormat.format(calendar.getTime());
    String[] arrStartDate = startDateOfWeek.split("/") ;
    String startWeekDay = arrStartDate[0] ;       
    calendar.add(Calendar.DATE, 6);
    String endDateOfWeek = dateFormat.format(calendar.getTime());
    String[] arrEndDate = endDateOfWeek.split("/") ;
    String endWeekDay = arrEndDate[0] ;       
    StringBuilder builder = new StringBuilder();
    //Year folder
    builder.append(currentYear).append("/")
    //Month folder
           .append(currentMonth).append("/")
    //week folder
           .append(startWeekDay).append(" ").append(currentMonth)
           .append("-")
           .append(endWeekDay).append(" ").append(currentMonth)
           .append(" ").append(currentYear).append("/");
    return builder.toString();
  }

	/**
	 * Get SessionProvider of user. Only take this in current operating of user.
	 * DO NOT use in Job executing
	 * 
	 * @return SessionProvider
	 */
  private static SessionProvider getSessionProvider() {
	SessionProviderService sessionProviderService = (SessionProviderService) ExoContainerContext
		.getCurrentContainer().getComponentInstanceOfType(SessionProviderService.class);
	return sessionProviderService.getSessionProvider(null);
  }
}
