/*
 * Aipo is a groupware program developed by Aimluck,Inc.
 * Copyright (C) 2004-2008 Aimluck,Inc.
 * http://aipostyle.com/
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU 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 com.aimluck.eip.document.daemon;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Properties;

import org.apache.cayenne.DataRow;
import org.apache.cayenne.access.DataContext;
import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.ExpressionFactory;
import org.apache.cayenne.query.SQLTemplate;
import org.apache.cayenne.query.SelectQuery;
import org.apache.jetspeed.daemon.Daemon;
import org.apache.jetspeed.daemon.DaemonConfig;
import org.apache.jetspeed.daemon.DaemonEntry;
import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
import org.apache.jetspeed.services.logging.JetspeedLogger;
import org.apache.jetspeed.services.resources.JetspeedResources;

import com.aimluck.commons.field.ALDateTimeField;
import com.aimluck.eip.cayenne.om.portlet.EipTNote;
import com.aimluck.eip.cayenne.om.portlet.EipTNoteMap;
import com.aimluck.eip.cayenne.om.portlet.ImwTDocumentFile;
import com.aimluck.eip.cayenne.om.security.TurbineUser;
import com.aimluck.eip.document.util.DocumentUtils;
import com.aimluck.eip.note.util.NoteUtils;
import com.aimluck.eip.orm.DatabaseOrmService;
import com.aimluck.eip.util.ALEipUtils;
import com.aimluck.eip.whatsnew.util.WhatsNewUtils;

import java.text.DateFormat;

/**
 * 文書管理用の日時バッチを管理するためのクラスです。 <br />
 */
public class DailyBatchDocumentDaemon implements Daemon {
  private int kikanOpen = 0;    //期間：公開
  private int kikanSave = 1;    //期間：保存
  private int timingBefore = 0; //タイミング：事前
  private int timingJust = 1;   //タイミング：当日
  
  private int status = Daemon.STATUS_NOT_PROCESSED;
  private int result = Daemon.RESULT_UNKNOWN;

  private DaemonConfig config = null;
  private DaemonEntry entry = null;
  
  // 日時バッチ最終処理日を保持するpropertiesファイルのパス
  private final String FILEPATH_PROPERTIES
    = JetspeedResources.getString("aipo.conf", "") +"/DailyBatchDocument.properties";

  /**
   * 実行可能かを判定．サーバー起動時のみfalse． それ以降はtrueにする．
   */
  private boolean enable_run = false;

  private DataContext dataContext;

  private int beforedaycount;
  private Calendar lastdate = Calendar.getInstance();
  private Calendar today = Calendar.getInstance();
  private Calendar beforelastdate = Calendar.getInstance();
  private Calendar beforetoday = Calendar.getInstance();
  private String lineSeparator = System.getProperty("line.separator");

  private ArrayList folder_hierarchy_list;

  /**
   * Static initialization of the logger for this class
   */
  private static final JetspeedLogger logger = JetspeedLogFactoryService
      .getLogger(DailyBatchDocumentDaemon.class.getName());

  /**
   * 日時バッチ処理
   */
  public void run() {
    if (!enable_run) {
      // サーバー起動時は実行しないようにする．
      enable_run = true;
      return;
    }

    // システム日付
    today = Calendar.getInstance();

    // 実行時刻の判定
    int runhour = JetspeedResources.getInt("dailyBatchDocument.runhour");
    if (today.get(Calendar.HOUR_OF_DAY) < runhour) {
      return;
    }
    
    // メール通知猶予日数
    beforedaycount = JetspeedResources.getInt("dailyBatchDocument.beforedaycount");

    // バッチ最終処理日
    //String workdate = JetspeedResources.getString("dailyBatchDocument.lastdate");
    String workdate = getDailyBatchDocumentProperty("dailyBatchDocument.lastdate");
    if (workdate != null) {
      try {
        lastdate.setTime(DateFormat.getDateInstance().parse(workdate));
        // 既に処理済の場合は中止
        if (today.get(Calendar.YEAR) == lastdate.get(Calendar.YEAR) &&
            today.get(Calendar.MONTH) == lastdate.get(Calendar.MONTH) &&
            today.get(Calendar.DATE) == lastdate.get(Calendar.DATE)) {
          return;
        }
      } catch (ParseException e) {
        lastdate.setTime(today.getTime());
        lastdate.add(Calendar.DATE, -1);
      }
    } else {
      lastdate.setTime(today.getTime());
      lastdate.add(Calendar.DATE, -1);
    }

    // システム日付 ＋  メール通知猶予日数
    beforelastdate.setTime(lastdate.getTime());
    beforelastdate.add(Calendar.DATE, beforedaycount);

    // バッチ最終処理日 ＋  メール通知猶予日数
    beforetoday.setTime(today.getTime());
    beforetoday.add(Calendar.DATE, beforedaycount);
    
    try {
      this.setResult(Daemon.RESULT_PROCESSING);
      logger.info("DailyBatchDocumentDaemon Start");
      // 管理者の一覧を取得
      List userlist = getUserList();

      // 全フォルダの取得
      folder_hierarchy_list = DocumentUtils.getFolderList();

      // 公開終了日（事前）メール処理
      createMessage(kikanOpen, timingBefore, userlist);

      // 公開終了日（当日）メール処理
      createMessage(kikanOpen, timingJust, userlist);

      // 保存終了日（事前）メール処理
      createMessage(kikanSave, timingBefore, userlist);

      // 保存終了日（当日）メール処理
      createMessage(kikanSave, timingJust, userlist);

      // バッチ最終処理日を設定
      ALDateTimeField date = new ALDateTimeField("yyyy/MM/dd");
      date.setValue(today.getTime());
      //JetspeedResources.setProperty("dailyBatchDocument.lastdate", date.toString());      
      setDailyBatchDocumentProperty("dailyBatchDocument.lastdate", date.toString());
      
      logger.info("DailyBatchDocumentDaemon End");
      this.setResult(Daemon.RESULT_SUCCESS);
    } catch (Exception e) {
      this.setResult(Daemon.RESULT_FAILED);
      logger.error("DailyBatchDocumentDaemon", e);
    }
  }

  /**
   * properties ファイル書込み
   * @param key
   * @param value
   * @return
   */
  private boolean setDailyBatchDocumentProperty(String key, String value){
    try{
      Properties prop = new Properties();

      prop.setProperty(key, value);
      
      prop.store(new FileOutputStream(FILEPATH_PROPERTIES), "DailyBatchDocument properties");
           
      return true;
    } catch (Exception e) {
      logger.error("DailyBatchDocumentDaemon", e);
      return false;
    }  
  }
  
  /**
   * propertiesファイル読み込み
   * @param key
   * @return
   */
  private String getDailyBatchDocumentProperty(String key)
  {
    try{
      Properties prop = new Properties();
      
      // ファイルが存在しない場合はnullを返す。
      File file = new File(FILEPATH_PROPERTIES);
      if (!file.exists()){
        return null;
      }

      prop.load(new FileInputStream(FILEPATH_PROPERTIES));
      String value = prop.getProperty(key);
         
      return value;
    } catch (Exception e) {
      logger.error("DailyBatchDocumentDaemon", e);
      return null;
    }  
    
  }
  /**
   * メッセージ作成
   */
  private void createMessage(int kikan, int timing, List userlist) {

    Calendar dateFrom = Calendar.getInstance();
    Calendar dateTo = Calendar.getInstance();
    Calendar xday = Calendar.getInstance();

    // 検索に使用する日付をセット
    if (timing == timingBefore) {
      dateFrom.setTime(beforelastdate.getTime());
      dateTo.setTime(beforetoday.getTime());
    } else {
      dateFrom.setTime(lastdate.getTime());
      dateTo.setTime(today.getTime());
    }
    
    xday.setTime(dateFrom.getTime());
    xday.add(Calendar.DATE, 1);
    
    // 1日につき1件メッセージを作成
    while(!xday.after(dateTo))
    {
      StringBuffer subject = new StringBuffer();
      StringBuffer message = new StringBuffer();
      
      // メール通知対象ドキュメントの検索
      List<ImwTDocumentFile> documentList = getDocumentFileList(kikan, xday);
      
      if (documentList != null) {
        if (documentList.size() > 0) {
          // サブジェクト
          subject.append(getSubject(kikan, timing));
          // 本文（ヘッダー）
          message.append(getMessageHeader(kikan, documentList.size(), xday));
          // 本文（明細）
          for (int i = 0; i < documentList.size(); i++) {
            ImwTDocumentFile imwTDocumentFile = documentList.get(i);
            message.append(imwTDocumentFile.getFileTitle()
              + "\t"
              + DocumentUtils.getFolderPosition(folder_hierarchy_list
                  ,imwTDocumentFile.getFolderId().intValue()).replaceAll("<b> &gt; </b>", " > ")
              + lineSeparator);
          }

          // 伝言メモへ送信
          sendMessage(userlist, subject.toString(), message.toString());
          logger.info(subject.toString() + " 対象ファイル：" + documentList.size());
        }
      }
      xday.add(Calendar.DATE, 1);
    }
  }

  /**
   * サブジェクトの取得
   */
  
  private String getSubject(int kikan, int timing) {
    StringBuffer subject = new StringBuffer();
    subject.append("[文書管理]");
    if (kikan == kikanOpen) {
      subject.append("公開期間終了");
    } else {
      subject.append("保存期間終了");
    }
    if (timing == timingBefore) {
      subject.append(beforedaycount + "日前の文書があります");
    } else {
      subject.append("の文書があります");
    }
    return subject.toString();
  }

  /**
   * 本文（ヘッダー）の取得
   */
  private String getMessageHeader(int kikan, int dataCount, Calendar xday) {
    StringBuffer message = new StringBuffer();
    String kikanName;
    if (kikan == kikanOpen) {
      kikanName = "公開";
    } else {
      kikanName = "保存";
    }
    message.append("下記の文書は");
    message.append(getDateString(xday));
    message.append("が" + kikanName + "終了日です。");
    
    message.append(lineSeparator);
    if (kikan == kikanOpen) {
      message.append("公開終了日を過ぎると管理者以外は参照できなくなります。" + lineSeparator);
    }
    message.append("本メールは文書管理システム管理者に対し自動送信されています。" + lineSeparator);
    message.append(lineSeparator);
    message.append("全" + dataCount + "件" + lineSeparator);
    return message.toString();
  }  

  /**
   * 日付（yyyy年M月d日）の取得
   */
  private String getDateString(Calendar calendar) {
    return calendar.get(Calendar.YEAR) + "年"
            + (calendar.get(Calendar.MONTH) + 1) + "月"
            + calendar.get(Calendar.DATE) + "日";
  }
  
  /**
   * 管理者の取得（管理者＝フォルダの削除権限を持ったユーザ）
   */
  private List getUserList() {
    try {
      StringBuffer statement = new StringBuffer();
      statement.append(" SELECT");
      statement.append("     TURBINE_USER.USER_ID");
      statement.append(" FROM EIP_T_ACL_ROLE");
      statement.append("     INNER JOIN EIP_T_ACL_USER_ROLE_MAP ON");
      statement.append("             EIP_T_ACL_ROLE.ROLE_ID = EIP_T_ACL_USER_ROLE_MAP.ROLE_ID");
      statement.append("     INNER JOIN TURBINE_USER ON");
      statement.append("             EIP_T_ACL_USER_ROLE_MAP.USER_ID = TURBINE_USER.USER_ID");
      statement.append(" WHERE");
      statement.append("         FEATURE_ID      = 212");   // フォルダ
      statement.append("     AND (ACL_TYPE & 16) != 0");    // 削除権限
      // Adminもリストに追加する。
      statement.append(" UNION");
      statement.append(" SELECT");
      statement.append("     1 AS USER_ID");
      statement.append(" ORDER BY");
      statement.append("      USER_ID");
      String query = statement.toString();
    
      DataContext dataContext = DatabaseOrmService.getInstance().getDataContext();
      SQLTemplate rawSelect = new SQLTemplate(TurbineUser.class, query, true);
      rawSelect.setFetchingDataRows(true);
      List list = dataContext.performQuery(rawSelect);
      return list;

    } catch (Exception ex) {
      logger.error("Exception", ex);
      return null;
    }
  }

  /**
   * 通知が必要なドキュメントの取得
   */
  private List<ImwTDocumentFile> getDocumentFileList(int kikan, Calendar xday) {
    try {
      ALDateTimeField date = new ALDateTimeField("yyyy-MM-dd");
      date.setValue(xday.getTime());
  
      String selectItem;
      if (kikan == kikanOpen) {
        selectItem = ImwTDocumentFile.OPEN_END_DATE_PROPERTY;
      } else {
        selectItem = ImwTDocumentFile.SAVE_END_DATE_PROPERTY;
      }
      
      DataContext dataContext = DatabaseOrmService.getInstance().getDataContext();
      SelectQuery query = new SelectQuery(ImwTDocumentFile.class);
      Expression exp1 = ExpressionFactory.matchExp(selectItem, date.toString());
      query.setQualifier(exp1);
      Expression exp2 = ExpressionFactory.noMatchExp(ImwTDocumentFile.STATUS_PROPERTY, "2");
      query.andQualifier(exp2);
      List files = dataContext.performQuery(query);
      
      if (files == null || files.size() == 0) {
        // 指定した条件のレコードが見つからない場合
        logger.debug("[Document File] Not found ID...");
        return null;
      }
      List<ImwTDocumentFile> filesList = new ArrayList<ImwTDocumentFile>();
      for (int i = 0; i < files.size(); i++) {
        ImwTDocumentFile record = (ImwTDocumentFile) files.get(i);
        filesList.add(record);
      }
      
      return filesList;

    } catch (Exception ex) {
      logger.error("Exception", ex);
      return null;
    }
  }

  /**
   * メッセージ送信処理
   */
  private void sendMessage(List memberList, String subject, String message) {
    try {
      if (memberList == null) {
        return;
      }
      
      dataContext = DatabaseOrmService.getInstance().getDataContext();
  
      // 伝言メモ
      EipTNote eipTNote = (EipTNote) dataContext
          .createAndRegisterNewObject(EipTNote.class);
      eipTNote.setOwnerId("1");
      eipTNote.setClientName("文書管理");
      eipTNote.setCompanyName("");
      eipTNote.setTelephone("");
      eipTNote.setEmailAddress("");
      eipTNote.setAddDestType("0");
      eipTNote.setSubjectType("0");
      eipTNote.setCustomSubject(subject);
      eipTNote.setMessage(message);
      eipTNote.setAcceptDate(today.getTime());
      eipTNote.setCreateDate(today.getTime());
      eipTNote.setUpdateDate(today.getTime());
  
      // 伝言メモマップ
      DataRow dataRow;
      for (int i = 0; i < memberList.size(); i++) {
        dataRow = (DataRow)memberList.get(i);
        String userId = ALEipUtils.getObjFromDataRow(dataRow,"USER_ID").toString();
        
        EipTNoteMap eipTNoteMap = (EipTNoteMap) dataContext
          .createAndRegisterNewObject(EipTNoteMap.class);
        eipTNoteMap.setEipTNote(eipTNote);
        eipTNoteMap.setUserId(userId);
        eipTNoteMap.setDelFlg("F");
        eipTNoteMap.setNoteStat(NoteUtils.NOTE_STAT_NEW); // 新着
      }
  
      dataContext.commitChanges();
  
      /* 送信先に新着ポートレット登録（Admin以外） */
      for (int i = 0; i < memberList.size(); i++) {
        dataRow = (DataRow)memberList.get(i);
        String userId = ALEipUtils.getObjFromDataRow(dataRow,"USER_ID").toString();
        if (!userId.equals("1")) {
          WhatsNewUtils.insertWhatsNew(dataContext,
              WhatsNewUtils.WHATS_NEW_TYPE_NOTE, eipTNote.getNoteId().intValue(),
              Integer.valueOf(userId));
        }
      }
    } catch (Exception ex) {
      logger.error("Exception", ex);
    }
  }
  
  /* *** Daemon interface *** */

  /**
   * Init this Daemon from the DaemonFactory
   */
  public void init(DaemonConfig config, DaemonEntry entry) {
    this.config = config;
    this.entry = entry;
    dataContext = DatabaseOrmService.getInstance().getDataContext();
    enable_run = false;
  }

  /**
   */
  public DaemonConfig getDaemonConfig() {
    return this.config;
  }

  /**
   */
  public DaemonEntry getDaemonEntry() {
    return this.entry;
  }

  /**
   * Return the status for this Daemon
   *
   * @see Daemon#STATUS_NOT_PROCESSED
   * @see Daemon#STATUS_PROCESSED
   * @see Daemon#STATUS_PROCESSING
   */
  public int getStatus() {
    return this.status;
  }

  /**
   * Set the status for this Daemon
   *
   * @see #STATUS_NOT_PROCESSED
   * @see #STATUS_PROCESSED
   * @see #STATUS_PROCESSING
   */
  public void setStatus(int status) {
    this.status = status;
  }

  /**
   * @see Daemon#getResult()
   */
  public int getResult() {
    return this.result;
  }

  /**
   * @see Daemon#setResult(int result)
   */
  public void setResult(int result) {
    this.result = result;
  }

  /**
   * @see Daemon#getMessage()
   */
  public String getMessage() {
    return null;
  }

}
