/*
 * Copyright (C) 2003-2010 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.services.jcr.ext.backup.impl;

import org.exoplatform.commons.utils.PrivilegedFileHelper;
import org.exoplatform.services.jcr.RepositoryService;
import org.exoplatform.services.jcr.config.RepositoryConfigurationException;
import org.exoplatform.services.jcr.config.RepositoryEntry;
import org.exoplatform.services.jcr.config.WorkspaceEntry;
import org.exoplatform.services.jcr.ext.backup.BackupChain;
import org.exoplatform.services.jcr.ext.backup.BackupConfig;
import org.exoplatform.services.jcr.ext.backup.BackupConfigurationException;
import org.exoplatform.services.jcr.ext.backup.BackupJob;
import org.exoplatform.services.jcr.ext.backup.BackupManager;
import org.exoplatform.services.jcr.ext.backup.BackupOperationException;
import org.exoplatform.services.jcr.ext.backup.RepositoryBackupChain;
import org.exoplatform.services.jcr.ext.backup.RepositoryBackupChainLog;
import org.exoplatform.services.jcr.ext.backup.RepositoryBackupConfig;
import org.exoplatform.services.jcr.util.IdGenerator;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;

import java.io.File;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Created by The eXo Platform SAS.
 * 
 * <br>Date: 2010
 *
 * @author <a href="mailto:alex.reshetnyak@exoplatform.com.ua">Alex Reshetnyak</a> 
 * @version $Id$
 */
public class RepositoryBackupChainImpl
   implements RepositoryBackupChain
{
   /**
    * The apache logger.
    */
   private static final Log LOG = ExoLogger.getLogger("exo.jcr.component.ext.RepositoryBackupChainImpl");
   
   private final RepositoryBackupConfig config;

   private final Set<BackupChain> workspaceBackups;
   
   private final RepositoryBackupChainLog repositoryChainLog;

   private final String repositoryBackupId;
   
   private final Calendar startTime;

   private int state;

   public RepositoryBackupChainImpl(RepositoryBackupConfig config, File logDirectory,
            RepositoryService repositoryService,
            String fullBackupType, String incrementalBackupType, String repositoryBackupId)
            throws BackupOperationException,
            BackupConfigurationException
   {
      this.config = config;
      this.workspaceBackups = Collections.synchronizedSet(new HashSet<BackupChain>());
      this.startTime = Calendar.getInstance();
      this.repositoryBackupId = repositoryBackupId;

      List<String> wsLogFilePathList = new ArrayList<String>();
      
      RepositoryEntry repository;
      try
      {
         repository = repositoryService.getConfig().getRepositoryConfiguration(config.getRepository());
      }
      catch (RepositoryConfigurationException e)
      {
         throw new BackupOperationException("Can not get repository \"" + config.getRepository() + "\"", e);
      }
      
      for (WorkspaceEntry workspaceEntry : repository.getWorkspaceEntries())
      {
         BackupConfig wsBackupConfig = new BackupConfig();
         wsBackupConfig.setRepository(config.getRepository());
         wsBackupConfig.setWorkspace(workspaceEntry.getName());
         wsBackupConfig.setBackupType(config.getBackupType());
         wsBackupConfig.setIncrementalJobNumber(config.getIncrementalJobNumber());
         wsBackupConfig.setIncrementalJobPeriod(config.getIncrementalJobPeriod());

         Calendar startTime = Calendar.getInstance();
         File dir =
                  FileNameProducer.generateBackupSetDir(wsBackupConfig.getRepository(), wsBackupConfig.getWorkspace(),
                           config.getBackupDir().getPath(), startTime);
         PrivilegedFileHelper.mkdirs(dir);
         wsBackupConfig.setBackupDir(dir);

         BackupChain bchain =
                  new BackupChainImpl(wsBackupConfig, wsBackupConfig.getBackupDir(), repositoryService, fullBackupType,
                           incrementalBackupType, IdGenerator.generate(), logDirectory, startTime);
         
         wsLogFilePathList.add(bchain.getLogFilePath());
         workspaceBackups.add(bchain);
      }
      
      this.repositoryChainLog = new RepositoryBackupChainLog(logDirectory, 
                                                             this.config,
                                                             fullBackupType,
                                                             incrementalBackupType,
                                                             repository.getSystemWorkspaceName(),
                                                             wsLogFilePathList, 
                                                             this.repositoryBackupId, 
                                                             startTime,
                                                             repository,
                                                             repositoryService.getConfig());
      
      state = INITIALIZED;
   }

   /**
    * {@inheritDoc}
    */
   public String getLogFilePath()
   {
      return repositoryChainLog.getLogFilePath();
   }

   /**
    * {@inheritDoc}
    */
   public int getState()
   {
      if (state != FINISHED)
      {
         if (LOG.isDebugEnabled())
         {
            for (BackupChain bc : workspaceBackups)
            {
               LOG.debug(repositoryBackupId + " : " + getState(bc.getFullBackupState()));
            }
         }
         
         int fullBackupsState =-1;
         int incrementalBackupsState = -1;

         for (BackupChain bc : workspaceBackups)
         {
            fullBackupsState = bc.getFullBackupState();
            
            if (fullBackupsState != BackupJob.FINISHED)
            {
               break;
            }
         }
         
         for (BackupChain bChein : workspaceBackups)
         {
            if (bChein.getBackupConfig().getBackupType() == BackupManager.FULL_AND_INCREMENTAL)
            {
               incrementalBackupsState = bChein.getIncrementalBackupState();
               
               if (incrementalBackupsState == BackupJob.WORKING )
               {
                  break;
               }
            }
         }

         if (config.getBackupType() == BackupManager.FULL_BACKUP_ONLY)
         {
            if (fullBackupsState == BackupJob.FINISHED)
            {
               state = FINISHED;
            }
            else
            {
               state = WORKING;
            }
         }
         else
         {
            if (fullBackupsState == BackupJob.FINISHED && incrementalBackupsState == BackupJob.FINISHED)
            {
               state = FINISHED;
            }
            else if (fullBackupsState == BackupJob.FINISHED && incrementalBackupsState == BackupJob.WORKING)
            {
               state = FULL_BACKUP_FINISHED_INCREMENTAL_BACKUP_WORKING;
            }
            
            else
            {
               state = WORKING;
            }
         }
      }
      
      return state;
   }

   /**
    * {@inheritDoc}
    */
   public boolean isFinished()
   {
      return this.getState() == FINISHED;
   }

   /**
    * {@inheritDoc}
    */
   public void startBackup()
   {
      for (BackupChain wsChain : workspaceBackups) 
      {
         wsChain.startBackup();
      }
      
      state = WORKING;
   }

   /**
    * {@inheritDoc}
    */
   public void stopBackup()
   {
      if (state != INITIALIZED || this.getState() != FINISHED)
      {
         for (BackupChain wsChain : workspaceBackups) 
         {
            wsChain.stopBackup();
         }
         
         repositoryChainLog.endLog();
      }

      state = this.getState();
   }

   /**
    * {@inheritDoc}
    */
   public String getBackupId()
   {
      return repositoryBackupId;
   }

   /**
    * {@inheritDoc}
    */
   public Calendar getStartedTime()
   {
      return startTime;
   }

   /**
    * {@inheritDoc}
    */
   public RepositoryBackupConfig getBackupConfig()
   {
      return config;
   }

   private String getState(int type)
   {
      String state = "UNDEFINED STATE";

      if (type == BackupJob.FINISHED)
         state = "FINISHED";
      else if (type == BackupJob.STARTING)
         state = "STARTING";
      else if (type == BackupJob.WAITING)
         state = "WAITING";
      else if (type == BackupJob.WORKING)
         state = "WORKING";

      return state;
   }
}
