/*
 * Aipo is a groupware program developed by Aimluck,Inc.
 * Copyright (C) 2004-2011 Aimluck,Inc.
 * http://www.aipo.com
 *
 * Copyright(C) 2012 avanza Co.,Ltd. All rights reserved.
 * http://www.avnz.co.jp/
 *
 * 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.aimluck.eip.schedule;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.jar.Attributes;

import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.ExpressionFactory;
import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
import org.apache.jetspeed.services.logging.JetspeedLogger;
import org.apache.turbine.util.RunData;
import org.apache.velocity.context.Context;

import com.aimluck.commons.field.ALDateTimeField;
import com.aimluck.eip.cayenne.om.portlet.EipTSchedule;
import com.aimluck.eip.cayenne.om.portlet.EipTScheduleMap;
import com.aimluck.eip.common.ALAbstractSelectData;
import com.aimluck.eip.common.ALBaseUser;
import com.aimluck.eip.common.ALDBErrorException;
import com.aimluck.eip.common.ALPageNotFoundException;
import com.aimluck.eip.modules.actions.common.ALAction;
import com.aimluck.eip.orm.Database;
import com.aimluck.eip.orm.query.ResultList;
import com.aimluck.eip.orm.query.SelectQuery;
import com.aimluck.eip.schedule.util.SchedulePrintUtils;
import com.aimluck.eip.schedule.util.ScheduleUtils;
import com.aimluck.eip.util.ALEipUtils;

/**
 * スケジュール1日表示の検索結果を管理するクラスです。
 * 
 */
public class SchedulePrintOnedaySelectData extends ALAbstractSelectData<EipTScheduleMap, EipTScheduleMap> {

  /** <code>logger</code> logger */
  private static final JetspeedLogger logger = JetspeedLogFactoryService.getLogger(SchedulePrintOnedaySelectData.class.getName());

  /** <code>TARGET_USER_ID</code> ユーザによる表示切り替え用変数の識別子 */
  private static final String TARGET_USER_ID = "target_user_id";

  /** <code>viewDate</code> 表示する日付 */
  private ALDateTimeField viewDate;

  /** デフォルト表示開始時刻 */
  private static final int DEFAULT_START_VIEW_HOUR = 7;

  /** デフォルト表示終了時刻 */
  private static final int DEFAULT_END_VIEW_HOUR = 18;

  /** 期間スケジュールの最大件数 */
  private static final int MAX_TERM_SCHEDULE_COUNT = 5;

  /** 表示スケジュールの最大件数 */
  private static final int MAX_OUT_SCHEDULE_COUNT = 5;

  /** スケジュールの最低表示時間（分） */
  private static final int MIN_SCHEDULE_MINUTES = 15;

  /** <code>termList</code> 期間スケジュールリスト */
  private List<ScheduleResultData> termResultDataList;

  /** <code>outList</code> 時間外スケジュールリスト */
  private ScheduleDayContainer outCon;

  /** <code>scheduleAcl</code> 表示可否 */
  private String scheduleAcl;

  /** <code>userid</code> ログインユーザーID */
  private String userid;

  /** <code>printDate</code> 印刷日時 */
  private ALDateTimeField printDate;

  /** <code>target_user_alias_name</code> 表示対象のユーザ 表示名 */
  private String targetUserAliasName;

  /** <code>target_user_id</code> 表示対象のユーザ ID */
  private String targetUserId;

  /** 表示できない期間スケジュールがあるかどうかのフラグ */
  private boolean cannotTermDisplayFlag;

  /** 表示できない時間外スケジュールがあるかどうかのフラグ */
  private boolean cannotOutDisplayFlag;

  /** １時間の高さ(px指定) */
  private static final int ONE_HOUR_HEIGHT = 70;

  /** １日の横幅(px指定) */
  private static final int ONE_DAY_WIDTH = 685;

  /** <code>dayCon</code> １日スケジュールコンテナ */
  private ScheduleDayContainer dayCon;

  /** <code>duplicateList</code> 重複スケジュールリスト */
  private List<List<ScheduleResultData>> duplicateList;

  /**
   * 
   * @param action
   * @param rundata
   * @param context
   * @throws ALPageNotFoundException
   * @throws ALDBErrorException
   */
  @Override
  public void init(ALAction action, RunData rundata, Context context) throws ALPageNotFoundException, ALDBErrorException {
    // 現在日時を取得
    Calendar now = Calendar.getInstance();

    // 印刷日時
    printDate = new ALDateTimeField("yyyy/MM/dd HH:mm");
    printDate.setValue(now.getTime());

    // リクエストパラメータから対象日を取得し、viewDate に値を設定する。
    viewDate = new ALDateTimeField("yyyy-MM-dd");
    viewDate.setNotNull(true);
    String tmpViewDate = rundata.getParameters().getString("view_start");
    if (tmpViewDate != null && tmpViewDate != "") {
      viewDate.setValue(tmpViewDate);
    } else {
      // リクエストパラメータが無効の場合、「システム日付」を印刷対象日とする
      Calendar sysdate = Calendar.getInstance();
      sysdate.set(Calendar.HOUR_OF_DAY, 0);
      sysdate.set(Calendar.MINUTE, 0);
      sysdate.set(Calendar.SECOND, 0);
      sysdate.set(Calendar.MILLISECOND, 0);
      viewDate.setValue(sysdate.getTime());
    }

    // 期間スケジュールリストの初期化
    termResultDataList = new ArrayList<ScheduleResultData>();

    // １日スケジュール時間外コンテナの初期化
    try {
      outCon = new ScheduleDayContainer();
      outCon.initField();
      outCon.setDate(viewDate.getValue());
    } catch (Exception e) {
      logger.error("Exception", e);
    }

    // １日スケジュール時間内コンテナの初期化
    try {
      dayCon = new ScheduleDayContainer();
      dayCon.initField();
      dayCon.setDate(viewDate.getValue());
    } catch (Exception e) {
      logger.error("Exception", e);
    }

    // リクエストパラメータから印刷対象ユーザーIDを取得
    if (rundata.getParameters().containsKey("TARGET_USER_ID")) {
      targetUserId = rundata.getParameters().getString(TARGET_USER_ID);
    } else {
      // リクエストパラメータが無効の場合、ログインユーザーを印刷対象ユーザーとする
      targetUserId = Integer.toString(ALEipUtils.getUserId(rundata));
    }
    ALBaseUser targetUser = ALEipUtils.getBaseUser(Integer.parseInt(targetUserId));
    // ユーザー表示名を取得
    targetUserAliasName = targetUser.getDisplayName();

    // スーパークラスのメソッドを呼び出す。
    super.init(action, rundata, context);

    // ログインユーザーのIDを取得
    userid = Integer.toString(ALEipUtils.getUserId(rundata));

    // 表示できない期間スケジュールがあるかどうかのフラグと、
    // 表示できない時間外スケジュールがあるかどうかのフラグをfalseで初期化する。
    cannotTermDisplayFlag = false;
    cannotOutDisplayFlag = false;

    // 重複スケジュールリストを初期化
    duplicateList = new ArrayList<List<ScheduleResultData>>();
    duplicateList.add(new ArrayList<ScheduleResultData>());
  }

  /**
   * 一覧表示します。
   * 
   * @param action
   * @param rundata
   * @param context
   * @return TRUE 成功 FASLE 失敗
   */
  @Override
  public boolean doViewList(ALAction action, RunData rundata, Context context) {
    boolean result = super.doViewList(action, rundata, context);

    // 重複リストを作成
    for (ScheduleResultData rd : dayCon.getScheduleListDesc()) {
      createDuplicateList(rd);
    }
    // 重複の列数を設定
    SchedulePrintUtils.setScheduleColumnCount(duplicateList, DEFAULT_END_VIEW_HOUR, MIN_SCHEDULE_MINUTES);
    return result;
  }

  /**
   * 一覧データを取得します。 <BR>
   * 
   * @param rundata
   *          JetSpeedランデータ
   * @param context
   *          JetSpeedコンテキスト
   * @return 結果リスト
   * @see com.aimluck.eip.common.ALAbstractListData#selectData(org.apache.turbine.util.RunData,
   *      org.apache.velocity.context.Context)
   * @throws ALPageNotFoundException
   *           , ALDBErrorException
   */
  @Override
  protected ResultList<EipTScheduleMap> selectList(RunData rundata, Context context) throws ALPageNotFoundException, ALDBErrorException {
    try {

      // ソート順が異なるため、期間スケジュールと通常スケジュールを別々に取得して合体する
      List<EipTScheduleMap> termList = getSelectQueryForTerm(rundata, context).fetchList();
      List<EipTScheduleMap> normalList = getSelectQueryForNormal(rundata, context).fetchList();
      termList.addAll(normalList);

      return new ResultList<EipTScheduleMap>(ScheduleUtils.sortByDummySchedule(termList));
    } catch (Exception e) {
      logger.error("１日スケジュール印刷用画面の表示に失敗しました[" + ALEipUtils.getBaseUser(Integer.parseInt(targetUserId)).getUserName() + "]", e);
      return null;
    }
  }

  /**
   * 検索条件を設定した SelectQuery を返します。
   * 
   * @param rundata
   * @param context
   * @return
   */
  protected SelectQuery<EipTScheduleMap> getSelectQueryForTerm(RunData rundata, Context context) {
    SelectQuery<EipTScheduleMap> query = Database.query(EipTScheduleMap.class);

    // 印刷対象ユーザ
    Expression exp1 = ExpressionFactory.matchExp(EipTScheduleMap.USER_ID_PROPERTY, Integer.valueOf(targetUserId));
    query.setQualifier(exp1);
    // ユーザのスケジュール
    Expression exp2 = ExpressionFactory.matchExp(EipTScheduleMap.TYPE_PROPERTY, ScheduleUtils.SCHEDULEMAP_TYPE_USER);
    query.andQualifier(exp2);
    // 期間スケジュール
    Expression exp3 =
      ExpressionFactory.matchExp(EipTScheduleMap.EIP_TSCHEDULE_PROPERTY + "." + EipTSchedule.REPEAT_PATTERN_PROPERTY, ScheduleConst.SCHEDULE_PATTERN_SPAN);
    query.andQualifier(exp3);

    // 日付を1日ずらす
    Calendar cal = Calendar.getInstance();
    cal.setTime(viewDate.getValue());
    cal.add(Calendar.DATE, 1);
    ALDateTimeField endDate = new ALDateTimeField();
    endDate.setValue(cal.getTime());

    // 終了日時
    Expression exp11 = ExpressionFactory.greaterOrEqualExp(EipTScheduleMap.EIP_TSCHEDULE_PROPERTY + "." + EipTSchedule.END_DATE_PROPERTY, viewDate.getValue());
    // 開始日時
    Expression exp12 = ExpressionFactory.lessOrEqualExp(EipTScheduleMap.EIP_TSCHEDULE_PROPERTY + "." + EipTSchedule.START_DATE_PROPERTY, endDate.getValue());
    query.andQualifier(exp11.andExp(exp12));
    // 開始日時 昇順＞終了日時 昇順＞スケジュールID(スケジュールマップ) 昇順でソート
    query.orderAscending(EipTScheduleMap.EIP_TSCHEDULE_PROPERTY + "." + EipTSchedule.START_DATE_PROPERTY);
    query.orderAscending(EipTScheduleMap.EIP_TSCHEDULE_PROPERTY + "." + EipTSchedule.END_DATE_PROPERTY);
    query.orderAscending(EipTScheduleMap.SCHEDULE_ID_PROPERTY);

    return query;
  }

  /**
   * 検索条件を設定した SelectQuery を返します。
   * 
   * @param rundata
   * @param context
   * @return
   */
  protected SelectQuery<EipTScheduleMap> getSelectQueryForNormal(RunData rundata, Context context) {
    SelectQuery<EipTScheduleMap> query = Database.query(EipTScheduleMap.class);

    // 印刷対象ユーザ
    Expression exp1 = ExpressionFactory.matchExp(EipTScheduleMap.USER_ID_PROPERTY, Integer.valueOf(targetUserId));
    query.setQualifier(exp1);
    // ユーザのスケジュール
    Expression exp2 = ExpressionFactory.matchExp(EipTScheduleMap.TYPE_PROPERTY, ScheduleUtils.SCHEDULEMAP_TYPE_USER);
    query.andQualifier(exp2);
    // 期間スケジュール以外
    Expression exp3 =
      ExpressionFactory.noMatchExp(EipTScheduleMap.EIP_TSCHEDULE_PROPERTY + "." + EipTSchedule.REPEAT_PATTERN_PROPERTY, ScheduleConst.SCHEDULE_PATTERN_SPAN);
    query.andQualifier(exp3);

    // 日付を1日ずらす
    Calendar cal = Calendar.getInstance();
    cal.setTime(viewDate.getValue());
    cal.add(Calendar.DATE, 1);
    ALDateTimeField endDate = new ALDateTimeField();
    endDate.setValue(cal.getTime());

    // 終了日時
    Expression exp11 = ExpressionFactory.greaterOrEqualExp(EipTScheduleMap.EIP_TSCHEDULE_PROPERTY + "." + EipTSchedule.END_DATE_PROPERTY, viewDate.getValue());
    // 開始日時
    Expression exp12 = ExpressionFactory.lessOrEqualExp(EipTScheduleMap.EIP_TSCHEDULE_PROPERTY + "." + EipTSchedule.START_DATE_PROPERTY, endDate.getValue());
    query.andQualifier(exp11.andExp(exp12));
    // 開始日時 昇順＞終了日時 降順＞スケジュールID(スケジュールマップ) 昇順でソート
    query.orderAscending(EipTScheduleMap.EIP_TSCHEDULE_PROPERTY + "." + EipTSchedule.START_DATE_PROPERTY);
    query.orderDesending(EipTScheduleMap.EIP_TSCHEDULE_PROPERTY + "." + EipTSchedule.END_DATE_PROPERTY);
    query.orderAscending(EipTScheduleMap.SCHEDULE_ID_PROPERTY);

    return query;
  }

  /**
   * 
   * @param rundata
   * @param context
   * @return
   */
  @Override
  protected EipTScheduleMap selectDetail(RunData rundata, Context context) {
    // このメソッドは利用されません。
    return null;
  }

  /**
   * スケジュールの検索結果を取得する。
   * 
   * @param record
   * @return
   * @throws ALPageNotFoundException
   * @throws ALDBErrorException
   */
  @Override
  protected Object getResultData(EipTScheduleMap record) throws ALPageNotFoundException, ALDBErrorException {
    ScheduleResultData rd = new ScheduleResultData();
    rd.initField();
    try {
      EipTSchedule schedule = record.getEipTSchedule();
      // スケジュールが棄却されている場合は表示しない
      if ("R".equals(record.getStatus())) {
        return rd;
      }

      SelectQuery<EipTScheduleMap> mapquery = Database.query(EipTScheduleMap.class);
      Expression mapexp1 = ExpressionFactory.matchExp(EipTScheduleMap.SCHEDULE_ID_PROPERTY, schedule.getScheduleId());
      mapquery.setQualifier(mapexp1);
      Expression mapexp2 = ExpressionFactory.matchExp(EipTScheduleMap.USER_ID_PROPERTY, userid);
      mapquery.andQualifier(mapexp2);

      List<EipTScheduleMap> schedulemaps = mapquery.fetchList();
      boolean is_member = (schedulemaps != null && schedulemaps.size() > 0) ? true : false;

      // <閲覧予定ユーザー>のログインユーザーに対する<表示可否>を取得する。
      scheduleAcl = ScheduleUtils.getScheduleAcl(userid, targetUserId);

      // 完全に非公開(コンテナに格納しません）
      if (ScheduleConst.SCHEDULE_ACL_PUBLIC.equals(scheduleAcl) && !"D".equals(record.getStatus()) && "P".equals(schedule.getPublicFlag()) && !is_member) {
        return rd;
      }

      // 秘書設定元ユーザーが作成者で完全に非公開(コンテナに格納しません）
      if (ScheduleConst.SCHEDULE_ACL_SECRETARY.equals(scheduleAcl)
        && !"D".equals(record.getStatus())
        && "P".equals(schedule.getPublicFlag())
        && (schedule.getCreateUserId().intValue() == record.getUserId().intValue())
        && !is_member) {
        return rd;
      }

      // 公開フラグ[非公開]、かつ、予定権限[非公開]、または、[秘書]、かつ、作成者が対象ユーザーである場合、かつ、メンバーでない
      if ("C".equals(schedule.getPublicFlag())
        && (ScheduleConst.SCHEDULE_ACL_PUBLIC.equals(scheduleAcl) || (ScheduleConst.SCHEDULE_ACL_SECRETARY.equals(scheduleAcl) && schedule
          .getCreateUserId()
          .intValue() == record.getUserId().intValue()))
        && !is_member) {
        // 予定名
        rd.setName("非公開");
        // 場所
        rd.setPlace("");
        // 状態
        rd.setStatus("");
        // 重要フラグ
        rd.setPriority("");
        // 仮予定フラグ
        rd.setTemporaryFlag("");
      } else {
        // 予定名
        rd.setName(schedule.getName());
        // 場所
        rd.setPlace(schedule.getPlace());
        // 状態
        rd.setStatus(record.getStatus());
        // 重要フラグ
        rd.setPriority(record.getPriority());
        // 仮予定フラグ
        rd.setTemporaryFlag(schedule.getTemporaryFlag());
      }
      // ID
      rd.setScheduleId(schedule.getScheduleId().intValue());
      // 親スケジュール ID
      rd.setParentId(schedule.getParentId().intValue());
      // 開始日時
      rd.setStartDate(schedule.getStartDate());
      // 終了日時
      rd.setEndDate(schedule.getEndDate());
      // 公開するかどうか
      rd.setPublic("O".equals(schedule.getPublicFlag()));
      // ダミーか
      rd.setDummy("D".equals(record.getStatus()));
      // 繰り返しパターン
      rd.setPattern(schedule.getRepeatPattern());

      // 表示対象日の23：59
      Calendar calBorderEnd = Calendar.getInstance();
      calBorderEnd.setTime(viewDate.getValue());
      calBorderEnd.set(Calendar.HOUR_OF_DAY, 23);
      calBorderEnd.set(Calendar.MINUTE, 59);
      calBorderEnd.set(Calendar.SECOND, 0);
      calBorderEnd.set(Calendar.MILLISECOND, 0);

      // 翌日の0:00から始まる予定は除外
      if (calBorderEnd.getTime().before(rd.getStartDate().getValue())) {
        return null;
      }

      // 期間スケジュールの場合
      if (rd.getPattern().equals("S")) {
        // 表示最大件数を超えている場合、期間スケジュールは格納せず、表示不可フラグを立てる。
        if (termResultDataList.size() >= MAX_TERM_SCHEDULE_COUNT) {
          cannotTermDisplayFlag = true;
        } else {
          // 期間スケジュール を格納
          termResultDataList.add(rd);
        }
        return rd;
      }

      // 繰り返しスケジュール
      if (!rd.getPattern().equals("N") && !rd.getPattern().equals("Z")) {
        // 繰り返し日程の範囲内かどうか判定
        if (ScheduleUtils.isView(viewDate, rd.getPattern(), rd.getStartDate().getValue(), rd.getEndDate().getValue())) {
          // 開始日時を再セット
          Calendar startDate = Calendar.getInstance();
          startDate.setTime(viewDate.getValue());
          startDate.set(Calendar.HOUR_OF_DAY, Integer.parseInt(rd.getStartDate().getHour()));
          startDate.set(Calendar.MINUTE, Integer.parseInt(rd.getStartDate().getMinute()));
          startDate.set(Calendar.SECOND, 0);
          startDate.set(Calendar.MILLISECOND, 0);

          // 終了日時を再セット
          Calendar endDate = Calendar.getInstance();
          endDate.setTime(viewDate.getValue());
          endDate.set(Calendar.HOUR_OF_DAY, Integer.parseInt(rd.getEndDate().getHour()));
          endDate.set(Calendar.MINUTE, Integer.parseInt(rd.getEndDate().getMinute()));
          endDate.set(Calendar.SECOND, 0);
          endDate.set(Calendar.MILLISECOND, 0);

          rd.setStartDate(startDate.getTime());
          rd.setEndDate(endDate.getTime());
          // 繰り返しあり
          rd.setRepeat(true);
        } else {
          // 対象外ならコンテナに格納しない
          return rd;
        }
      }

      // 日またぎスケジュールの場合
      if (rd.getPattern().equals("Z")) {
        // 表示日当日のスケジュールデータに変換する。
        SchedulePrintUtils.setDayCrossoverDate2DayScheduleDate(rd, viewDate.getValue());
      }

      // 通常スケジュール（日またぎ、繰り返しは変換済み）の場合

      // スケジュールが時間外かどうかを判定
      if (!rd.isDummy() && SchedulePrintUtils.isOutSchedule(rd, DEFAULT_START_VIEW_HOUR, DEFAULT_END_VIEW_HOUR)) {
        if (!SchedulePrintUtils.isDeletedRepeatSchedule(dayCon, rd)) {
          // 表示最大件数を超えている場合、時間外スケジュールは格納せず、表示不可フラグを立てる。
          if (outCon.getScheduleListDesc().size() >= MAX_OUT_SCHEDULE_COUNT) {
            cannotOutDisplayFlag = true;
          } else {
            // 時間外スケジュール を格納
            outCon.addResultData(rd);
          }
        }
        return rd;
      }
      // スケジュールをコンテナに格納
      dayCon.addResultData(rd);

    } catch (Exception e) {
      logger.error("Exception", e);

      return null;
    }

    return rd;
  }

  /**
   * 未使用
   * 
   * @param record
   * @return
   */
  @Override
  protected Object getResultDataDetail(EipTScheduleMap record) {
    return null;
  }

  /**
   * 未使用
   * 
   * @return
   */
  @Override
  protected Attributes getColumnMap() {
    return null;
  }

  /**
   * 期間スケジュールリストを取得します。
   * 
   * @return 期間スケジュールリスト
   */
  public List<ScheduleResultData> getTermResultDataList() {
    return termResultDataList;
  }

  /**
   * 時間外スケジュールコンテナを取得します。
   * 
   * @return 時間外スケジュールコンテナ
   */
  public ScheduleDayContainer getOutContainer() {
    return outCon;
  }

  /**
   * 日ごとの表示できない期間スケジュールがあるかどうかのフラグを取得します。<br>
   * 
   * @return true：期間スケジュール表示可能、false：期間スケジュール表示不可
   */
  public boolean getCannotTermDisplayFlag() {
    return cannotTermDisplayFlag;
  }

  /**
   * 日ごとの表示できない時間外スケジュールがあるかどうかのフラグを取得します。<br>
   * 
   * @return true：時間外スケジュール表示可能、false：時間外スケジュール表示不可
   */
  public boolean getCannotOutDisplayFlag() {
    return cannotOutDisplayFlag;
  }

  /**
   * １日スケジュールコンテナを取得します。
   * 
   * @return 日コンテナ
   */
  public ScheduleDayContainer getContainer() {
    return dayCon;
  }

  /**
   * 印刷日時を取得します。
   * 
   * @return 印刷日時
   */
  public ALDateTimeField getPrintDate() {
    return printDate;
  }

  /**
   * 表示開始時刻を取得します。
   * 
   * @return 表示開始時刻
   */
  public int getStartViewHour() {
    return DEFAULT_START_VIEW_HOUR;
  }

  /**
   * 表示終了時刻を取得します。
   * 
   * @return 表示終了時刻
   */
  public int getEndViewHour() {
    return DEFAULT_END_VIEW_HOUR;
  }

  /**
   * 印刷対象の日付を取得します。
   * 
   * @return 印刷対象日付
   */
  public ALDateTimeField getViewDate() {
    return viewDate;
  }

  /**
   * １時間の高さを取得します。
   * 
   * @return １時間のスケジュール枠の高さ
   */
  public int getOneHourHeight() {
    return ONE_HOUR_HEIGHT;
  }

  /**
   * １日の横幅を取得します。
   * 
   * @return １日のスケジュール枠の横幅
   */
  public int getOneDayWidth() {
    return ONE_DAY_WIDTH;
  }

  /**
   * 期間スケジュール最大表示可能件数を取得します。
   * 
   * @return 期間スケジュール最大表示可能件数
   */
  public int getMaxTermScheduleCount() {
    return MAX_TERM_SCHEDULE_COUNT;
  }

  /**
   * 時間外スケジュール最大表示可能件数を取得します。
   * 
   * @return 時間外スケジュール最大表示可能件数
   */
  public int getMaxOutScheduleCount() {
    return MAX_OUT_SCHEDULE_COUNT;
  }

  /**
   * 印刷対象ユーザ ID を取得する．
   * 
   * @return 印刷対象ユーザーのユーザーID
   */
  public String getTargetUserId() {
    return targetUserId;
  }

  /**
   * 印刷画面に表示するユーザ の表示名
   * 
   * @return 印刷画面に表示するユーザ の表示名
   */
  public String getTargetUserAliasName() {
    return targetUserAliasName;
  }

  /**
   * 予定の縦幅 を取得する． 時間外にまたぐ場合、時間を調整する。
   * 
   * @param rd
   *          予定データ
   * 
   * @return 縦幅
   */
  public String getScheduleHeight(ScheduleResultData rd) {
    // Utilに定義した週、日共通メソッドから取得
    return SchedulePrintUtils.getScheduleHeight(rd, DEFAULT_START_VIEW_HOUR, DEFAULT_END_VIEW_HOUR, ONE_HOUR_HEIGHT, MIN_SCHEDULE_MINUTES);
  }

  /**
   * 予定の表示開始時刻からの高さ を取得する．
   * 
   * @param rd
   *          予定データ
   * 
   * @return 高さ
   */
  public String getScheduleTop(ScheduleResultData rd) {
    // Utilに定義した週、日共通メソッドから取得
    return SchedulePrintUtils.getScheduleTop(rd, DEFAULT_START_VIEW_HOUR, DEFAULT_END_VIEW_HOUR, ONE_HOUR_HEIGHT, MIN_SCHEDULE_MINUTES);
  }

  /**
   * 予定の左端からの描画開始位置 を取得する．
   * 
   * @param rd
   *          予定データ
   * 
   * @return 開始位置
   */
  public String getScheduleLeft(ScheduleResultData rd) {
    // Utilに定義した週、日共通メソッドから取得
    return SchedulePrintUtils.getScheduleLeft(rd, ONE_DAY_WIDTH);
  }

  /**
   * 予定の描画幅 を取得する．
   * 
   * @param rd
   *          予定データ
   * 
   * @return 横幅
   */
  public String getScheduleWidth(ScheduleResultData rd) {
    // Utilに定義した週、日共通メソッドから取得
    return SchedulePrintUtils.getScheduleWidth(rd, ONE_DAY_WIDTH);
  }

  /**
   * 重複予定のリスト を作成する．
   * 
   * @param rd
   *          予定データ
   */
  public void createDuplicateList(ScheduleResultData rd) {

    // リストを順に検索し、重複していたらリストに追加する。
    for (int listIndex = 0; listIndex < duplicateList.size(); listIndex++) {

      // リスト内の列リストからスケジュールリストを取得
      List<ScheduleResultData> columnList = duplicateList.get(listIndex);

      // 列リストが空の場合は、スケジュールを追加して終了。
      if (columnList.size() == 0) {
        rd.setSortNo(listIndex);
        rd.setColumnCount(listIndex + 1);
        columnList.add(rd);
        return;
      }

      // 列リストの最後のスケジュールを取得
      ScheduleResultData rd2 = columnList.get(columnList.size() - 1);

      Date startDate1 = rd.getStartDate().getValue();
      Date startDate2 = rd2.getStartDate().getValue();
      Date endDate1 = rd.getEndDate().getValue();
      Date endDate2 = rd2.getEndDate().getValue();
      // 重複しているかどうか判定
      if (SchedulePrintUtils.isDuplicateSchedule(startDate1, endDate1, startDate2, endDate2, DEFAULT_END_VIEW_HOUR, MIN_SCHEDULE_MINUTES)) {
        // 重複している場合は、次の列リストへ

        // 列リストがない場合は、追加して終了。
        if (listIndex == duplicateList.size() - 1) {
          duplicateList.add(new ArrayList<ScheduleResultData>());
          rd.setSortNo(listIndex + 1);
          rd.setColumnCount(listIndex + 2);
          duplicateList.get(listIndex + 1).add(rd);
          return;
        }
      } else {
        // 重複していない場合は、リストの最後に追加して終わり。
        rd.setSortNo(listIndex);
        rd.setColumnCount(listIndex + 1);
        columnList.add(rd);
        return;
      }
    }
  }
}
