/**
 * Logback: the reliable, generic, fast and flexible logging framework.
 * 
 * Copyright (C) 1999-2006, QOS.ch
 * 
 * This library is free software, you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation.
 */
package ch.qos.logback.core.rolling;


import java.io.File;
import java.util.Date;

import ch.qos.logback.core.rolling.helper.Compress;
import ch.qos.logback.core.rolling.helper.DateTokenConverter;
import ch.qos.logback.core.rolling.helper.FileNamePattern;
import ch.qos.logback.core.rolling.helper.RenameUtil;
import ch.qos.logback.core.rolling.helper.RollingCalendar;

/**
 * <code>TimeBasedRollingPolicy</code> is both easy to configure and quite 
 * powerful. It allows the rollover to be made based on time conditions. 
 * It is possible to specify that the rollover must occur each day, or month, for example.
 * 
 *  For more information about this policy, please refer to the online manual at
 *  http://logback.qos.ch/manual/appenders.html#TimeBasedRollingPolicy
 * 
 * @author Ceki G&uuml;lc&uuml;
 */
public class TimeBasedRollingPolicy<E> extends RollingPolicyBase implements TriggeringPolicy<E> {
  static final String FNP_NOT_SET =
    "The FileNamePattern option must be set before using TimeBasedRollingPolicy. ";
  static final String SEE_FNP_NOT_SET =
    "See also http://logback.qos.ch/codes.html#tbr_fnp_not_set";
  RollingCalendar rc;
  long currentTime;
  long nextCheck;
  //indicate whether the time has been forced or not
  boolean isTimeForced=false;
  Date lastCheck = new Date();
  String elapsedPeriodsFileName;
  FileNamePattern activeFileNamePattern;
  RenameUtil util = new RenameUtil();
  Compress compress = new Compress();
  String lastGeneratedFileName;
  
  public void setCurrentTime(long timeInMillis) {
	currentTime = timeInMillis;
	isTimeForced = true;
  }
  
  public long getCurrentTime(){
	//if time is forced return the time set by user
	if(isTimeForced){
	  return currentTime;
	}
	else{
	  return System.currentTimeMillis();
	}
  }
  
  public void start() {
    // set the LR for our utility object
    util.setContext(this.context);
    compress.setContext(this.context);
    
    // find out period from the filename pattern
    if (fileNamePatternStr != null) {
      fileNamePattern = new FileNamePattern(fileNamePatternStr, this.context);
      determineCompressionMode();
    } else {
      addWarn(FNP_NOT_SET);
      addWarn(SEE_FNP_NOT_SET);
      throw new IllegalStateException(FNP_NOT_SET + SEE_FNP_NOT_SET);
    }

    DateTokenConverter dtc = fileNamePattern.getDateTokenConverter();

    if (dtc == null) {
      throw new IllegalStateException(
        "FileNamePattern [" + fileNamePattern.getPattern()
        + "] does not contain a valid DateToken");
    }

    int len = fileNamePatternStr.length();
    switch(compressionMode) {
    case Compress.GZ:
      activeFileNamePattern =
        new FileNamePattern(fileNamePatternStr.substring(0, len - 3), this.context);
        
      break;
      case Compress.ZIP:
        activeFileNamePattern =
          new FileNamePattern(fileNamePatternStr.substring(0, len - 4), this.context);
        break;
       case Compress.NONE:
        activeFileNamePattern = fileNamePattern;
     }
     addInfo("Will use the pattern "+activeFileNamePattern+" for the active file");
   
    rc = new RollingCalendar();
    rc.init(dtc.getDatePattern());
    addInfo("The date pattern is '"+dtc.getDatePattern()+"' from file name pattern '"+
        fileNamePattern.getPattern()+"'.");
    rc.printPeriodicity(this);

    //currentTime = System.currentTimeMillis();
    lastCheck.setTime(getCurrentTime());
    nextCheck = rc.getNextCheckMillis(lastCheck);

    //Date nc = new Date();
    //nc.setTime(nextCheck);
  }

  public void rollover() throws RolloverFailure {
    //addInfo("roll-over called");
    //addInfo("compressionMode: " + compressionMode);

    if (getParentFileName() == null) {
      switch (compressionMode) {
      case Compress.NONE:
        // nothing to do;
        break;
      case Compress.GZ:
        addInfo("GZIP compressing ["+elapsedPeriodsFileName+"].");
        compress.GZCompress(elapsedPeriodsFileName);
        break;
      case Compress.ZIP:
        addInfo("ZIP compressing ["+elapsedPeriodsFileName+"]");
        compress.ZIPCompress(elapsedPeriodsFileName);
        break;
      }
    } else {
      switch (compressionMode) {
      case Compress.NONE:
        util.rename(getParentFileName(), elapsedPeriodsFileName);
        break;
      case Compress.GZ:
        addInfo("GZIP compressing ["+elapsedPeriodsFileName+"]");
        compress.GZCompress(getParentFileName(), elapsedPeriodsFileName);
        break;
      case Compress.ZIP:
        addInfo("ZIP compressing ["+elapsedPeriodsFileName+"]");
        compress.ZIPCompress(getParentFileName(), elapsedPeriodsFileName);
        break;
      }
    }
    
    //let's update the parent active file name
    setParentFileName(getNewActiveFileName());
    
  }

  /**
   *
   * The active log file is determined by the value of the parent's filename
   * option. However, in case the file name is left blank,
   * then, the active log file equals the file name for the current period
   * as computed by the <b>FileNamePattern</b> option.
   * 
   * The RollingPolicy must know wether it is responsible for
   * changing the name of the active file or not. If the active file
   * name is set by the user via the configuration file, then the
   * RollingPolicy must let it like it is. If the user does not
   * specify an active file name, then the RollingPolicy generates one.
   * 
   * To be sure that the file name used by the parent class has been
   * generated by the RollingPolicy and not specified by the user, we
   * keep track of the last generated name object and compare its
   * reference to the parent file name. If they match, then
   * the RollingPolicy knows it's responsible for the change of the
   * file name.
   * 
   */
  public String getNewActiveFileName() {
    if (getParentFileName() == null || getParentFileName() == lastGeneratedFileName) {
      String newName = activeFileNamePattern.convertDate(lastCheck);
      addInfo("Generated a new name for RollingFileAppender: " + newName);
      lastGeneratedFileName = newName;
      return newName;
    } else {
      return getParentFileName();
    }
  }

  public boolean isTriggeringEvent(File activeFile, final E event) {
    long time= getCurrentTime();

    if (time >= nextCheck) {
      //addInfo("Time to trigger roll-over");
      // We set the elapsedPeriodsFileName before we set the 'lastCheck' variable
      // The elapsedPeriodsFileName corresponds to the file name of the period
      // that just elapsed.
      elapsedPeriodsFileName = activeFileNamePattern.convertDate(lastCheck);
      //addInfo("elapsedPeriodsFileName set to "+elapsedPeriodsFileName);

      lastCheck.setTime(time);
      nextCheck = rc.getNextCheckMillis(lastCheck);

      Date x = new Date();
      x.setTime(nextCheck);
      //addInfo("Next check on "+ x);

      return true;
    } else {
      return false;
    }
  }  

  @Override
  public String toString() {
    return "c.q.l.core.rolling.TimeBasedRollingPolicy";
  }
  
}
