/*
 * This software is distributed under following license based on modified BSD
 * style license.
 * ----------------------------------------------------------------------
 * 
 * Copyright 2009 The Nimbus2 Project. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer. 
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE NIMBUS PROJECT ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
 * NO EVENT SHALL THE NIMBUS PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * The views and conclusions contained in the software and documentation are
 * those of the authors and should not be interpreted as representing official
 * policies, either expressed or implied, of the Nimbus2 Project.
 */
package jp.ossc.nimbus.service.scheduler;

import java.util.*;
import java.text.SimpleDateFormat;

/**
 * デフォルトスケジュールマスタ。<p>
 *
 * @author M.Takata
 */
public class DefaultScheduleMaster
 implements ScheduleMaster, java.io.Serializable{
    
    private static final long serialVersionUID = -4016419359156060172L;
    
    protected String id;
    
    protected String taskName;
    
    protected String scheduleType;
    
    protected Object input;
    
    protected Date startTime;
    
    protected Date endTime;
    
    protected long repeatInterval;
    
    protected long retryInterval;
    
    protected Date retryEndTime;
    
    protected long maxDelayTime;
    
    protected boolean isEnabled = true;
    
    protected String[] depends;
    
    protected String executorKey;
    
    protected String executorType;
    
    /**
     * 空のインスタンスを生成する。<p>
     */
    public DefaultScheduleMaster(){
    }
    
    /**
     * インスタンスを生成する。<p>
     *
     * @param id スケジュールマスタID
     * @param taskName タスク名
     * @param scheduleType タスク種別
     * @param input 入力データ
     * @param startTime 開始時刻
     * @param isEnabled 有効/無効フラグ
     * @param depends 依存するスケジュールマスタIDの配列
     * @param executorKey ScheduleExecutorを特定するキー
     * @param executorType ScheduleExecutorの種類
     */
    public DefaultScheduleMaster(
        String id,
        String taskName,
        String scheduleType,
        Object input,
        Date startTime,
        boolean isEnabled,
        String[] depends,
        String executorKey,
        String executorType
    ){
        this(
            id,
            taskName,
            scheduleType,
            input,
            startTime,
            null,
            0l,
            0l,
            null,
            0l,
            isEnabled,
            depends,
            executorKey,
            executorType
        );
    }
    
    /**
     * インスタンスを生成する。<p>
     *
     * @param id スケジュールマスタID
     * @param taskName タスク名
     * @param scheduleType タスク種別
     * @param input 入力データ
     * @param startTime 開始時刻
     * @param retryInterval リトライ実行間隔[ms]
     * @param retryEndTime リトライ終了時刻
     * @param maxDelayTime 最大遅延時間[ms]
     * @param isEnabled 有効/無効フラグ
     * @param depends 依存するスケジュールマスタIDの配列
     * @param executorKey ScheduleExecutorを特定するキー
     * @param executorType ScheduleExecutorの種類
     */
    public DefaultScheduleMaster(
        String id,
        String taskName,
        String scheduleType,
        Object input,
        Date startTime,
        long retryInterval,
        Date retryEndTime,
        long maxDelayTime,
        boolean isEnabled,
        String[] depends,
        String executorKey,
        String executorType
    ){
        this(
            id,
            taskName,
            scheduleType,
            input,
            startTime,
            null,
            0l,
            retryInterval,
            retryEndTime,
            maxDelayTime,
            isEnabled,
            depends,
            executorKey,
            executorType
        );
    }
    
    /**
     * インスタンスを生成する。<p>
     *
     * @param id スケジュールマスタID
     * @param taskName タスク名
     * @param scheduleType タスク種別
     * @param input 入力データ
     * @param startTime 開始時刻
     * @param endTime 終了時刻
     * @param repeatInterval 繰り返し実行間隔[ms]
     * @param retryInterval リトライ実行間隔[ms]
     * @param retryEndTime リトライ終了時刻
     * @param maxDelayTime 最大遅延時間[ms]
     * @param isEnabled 有効/無効フラグ
     * @param depends 依存するスケジュールマスタIDの配列
     * @param executorKey ScheduleExecutorを特定するキー
     * @param executorType ScheduleExecutorの種類
     */
    public DefaultScheduleMaster(
        String id,
        String taskName,
        String scheduleType,
        Object input,
        Date startTime,
        Date endTime,
        long repeatInterval,
        long retryInterval,
        Date retryEndTime,
        long maxDelayTime,
        boolean isEnabled,
        String[] depends,
        String executorKey,
        String executorType
    ){
        setId(id);
        setTaskName(taskName);
        setScheduleType(scheduleType);
        setInput(input);
        setStartTime(startTime);
        setEndTime(endTime);
        setRepeatInterval(repeatInterval);
        setRetryInterval(retryInterval);
        setRetryEndTime(retryEndTime);
        setMaxDelayTime(maxDelayTime);
        setEnabled(isEnabled);
        setDepends(depends);
        setExecutorKey(executorKey);
        setExecutorType(executorType);
    }
    
    // ScheduleMasterのJavaDoc
    public String getId(){
        return id;
    }
    
    /**
     * スケジュールマスタのIDを設定する。<p>
     *
     * @param id スケジュールマスタのID
     */
    public void setId(String id){
        this.id = id;
    }
    
    // ScheduleMasterのJavaDoc
    public String getTaskName(){
        return taskName;
    }
    
    /**
     * スケジュールされたタスク名を設定する。<p>
     *
     * @param name タスク名
     */
    public void setTaskName(String name){
        taskName = name;
    }
    
    // ScheduleMasterのJavaDoc
    public String getScheduleType(){
        return scheduleType;
    }
    
    /**
     * スケジュールされたスケジュール種別を設定する。<p>
     *
     * @param type スケジュール種別
     */
    public void setScheduleType(String type){
        scheduleType = type;
    }
    
    // ScheduleMasterのJavaDoc
    public Object getInput(){
        return input;
    }
    
    /**
     * スケジュールの入力データを設定する。<p>
     *
     * @param data 入力データ
     */
    public void setInput(Object data){
        input = data;
    }
    
    // ScheduleMasterのJavaDoc
    public Date getStartTime(){
        return startTime;
    }
    
    /**
     * スケジュール開始時刻を設定する。<p>
     *
     * @param time 開始時刻
     */
    public void setStartTime(Date time){
        startTime = time;
    }
    
    // ScheduleMasterのJavaDoc
    public Date getEndTime(){
        return endTime;
    }
    
    /**
     * スケジュール終了時刻を設定する。<p>
     *
     * @param time 終了時刻
     */
    public void setEndTime(Date time){
        endTime = time;
    }
    
    // ScheduleMasterのJavaDoc
    public long getRepeatInterval(){
        return repeatInterval;
    }
    
    /**
     * スケジュール繰り返し実行間隔[ms]を設定する。<p>
     *
     * @param interval 繰り返し実行間隔
     */
    public void setRepeatInterval(long interval){
        repeatInterval = interval;
    }
    
    // ScheduleMasterのJavaDoc
    public long getRetryInterval(){
        return retryInterval;
    }
    
    /**
     * スケジュールリトライ実行間隔[ms]を設定する。<p>
     *
     * @param interval リトライ実行間隔
     */
    public void setRetryInterval(long interval){
        retryInterval = interval;
    }
    
    // ScheduleMasterのJavaDoc
    public Date getRetryEndTime(){
        return retryEndTime;
    }
    
    /**
     * スケジュールリトライ終了時刻を設定する。<p>
     *
     * @param time リトライ終了時刻
     */
    public void setRetryEndTime(Date time){
        retryEndTime = time;
    }
    
    // ScheduleMasterのJavaDoc
    public long getMaxDelayTime(){
        return maxDelayTime;
    }
    
    /**
     * スケジュールの最大遅延時間[ms]を設定する。<p>
     *
     * @param time スケジュール最大遅延時間
     */
    public void setMaxDelayTime(long time){
        maxDelayTime = time;
    }
    
    // ScheduleMasterのJavaDoc
    public boolean isEnabled(){
        return isEnabled;
    }
    
    // ScheduleMasterのJavaDoc
    public void setEnabled(boolean isEnabled){
        this.isEnabled = isEnabled;
    }
    
    // ScheduleMasterのJavaDoc
    public String[] getDepends(){
        return depends;
    }
    
    /**
     * 依存するスケジュールマスタIDを設定する。<p>
     *
     * @param ids スケジュールマスタIDの配列
     */
    public void setDepends(String[] ids){
        depends = ids;
    }
    
    // ScheduleMasterのJavaDoc
    public void setExecutorKey(String key){
        executorKey = key;
    }
    
    // ScheduleMasterのJavaDoc
    public String getExecutorKey(){
        return executorKey;
    }
    
    // ScheduleMasterのJavaDoc
    public void setExecutorType(String type){
        executorType = type;
    }
    
    // ScheduleMasterのJavaDoc
    public String getExecutorType(){
        return executorType;
    }
    
    // ScheduleMasterのJavaDoc
    public void applyDate(Date date){
        final Calendar cal = Calendar.getInstance();
        if(startTime != null){
            startTime = applyDateToTime(date, startTime, cal);
        }
        if(endTime != null){
            endTime = applyDateToTime(date, endTime, cal);
        }
        if(retryEndTime != null){
            retryEndTime = applyDateToTime(date, retryEndTime, cal);
        }
    }
    
    /**
     * timeにdateの日付を適用する。<p>
     *
     * @param date 日付
     * @param time 時刻
     * @param work 作業用カレンダー
     * @return dateの日付とtimeの時刻を持ったDate
     */
    protected Date applyDateToTime(Date date, Date time, Calendar work){
        work.clear();
        work.setTime(date);
        final int year = work.get(Calendar.YEAR);
        final int day = work.get(Calendar.DAY_OF_YEAR);
        work.clear();
        work.setTime(time);
        work.set(Calendar.YEAR, year);
        work.set(
            Calendar.DAY_OF_YEAR,
            day + (work.get(Calendar.DAY_OF_YEAR) - 1)
        );
        return work.getTime();
    }
    
    /**
     * ハッシュ値を取得する。<p>
     *
     * @return ハッシュ値
     */
    public int hashCode(){
        return id == null ? 0 : id.hashCode();
    }
    
    /**
     * 指定されたオブジェクトがこのインスタンスと等しいかどうかを判定する。<p>
     *
     * @param obj 比較対象のオブジェクト
     * @return 指定されたオブジェクトがこのインスタンスと等しい場合true
     */
    public boolean equals(Object obj){
        if(obj == null){
            return false;
        }
        if(obj == this){
            return true;
        }
        if(!(obj instanceof ScheduleMaster)){
            return false;
        }
        ScheduleMaster cmp = (ScheduleMaster)obj;
        return (id == null && cmp.getId() == null)
            || (id != null && id.equals(cmp.getId()));
    }
    
    // ComparableのJavaDoc
    public int compareTo(ScheduleMaster o){
        if(o == null){
            return -1;
        }
        if(o == this){
            return 0;
        }
        final ScheduleMaster cmp = o;
        if(startTime != null && cmp.getStartTime() == null){
            return -1;
        }
        if(startTime == null && cmp.getStartTime() != null){
            return 1;
        }
        if(startTime != null && cmp.getStartTime() != null){
            int result = startTime.compareTo(cmp.getStartTime());
            if(result != 0){
                return result;
            }
        }
        if(id == null && cmp.getId() == null){
            return 0;
        }
        if(id != null && cmp.getId() == null){
            return -1;
        }
        if(id == null && cmp.getId() != null){
            return 1;
        }
        return id.compareTo(cmp.getId());
    }
    
    /**
     * このインスタンスの文字列表現を取得する。<p>
     *
     * @return 文字列表現
     */
    public String toString(){
        final SimpleDateFormat format
            = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss SSS");
        final StringBuilder buf = new StringBuilder(super.toString());
        buf.append('{');
        buf.append("id=").append(id);
        buf.append(",taskName=").append(taskName);
        buf.append(",scheduleType=").append(scheduleType);
        buf.append(",input=").append(input);
        buf.append(",startTime=")
            .append(startTime == null ? null : format.format(startTime));
        buf.append(",endTime=")
            .append(endTime == null ? null : format.format(endTime));
        buf.append(",repeatInterval=").append(repeatInterval);
        buf.append(",retryInterval=").append(retryInterval);
        buf.append(",retryEndTime=")
            .append(retryEndTime == null ? null : format.format(retryEndTime));
        buf.append(",maxDelayTime=").append(maxDelayTime);
        buf.append(",isEnabled=").append(isEnabled);
        buf.append(",depends=");
        if(depends == null || depends.length == 0){
            buf.append((Object)null);
        }else{
            buf.append('[');
            for(int i = 0; i < depends.length; i++){
                buf.append(depends[i]);
                if(i != depends.length - 1){
                    buf.append(',');
                }
            }
            buf.append(']');
        }
        buf.append(",executorKey=").append(executorKey);
        buf.append(",executorType=").append(executorType);
        buf.append('}');
        return buf.toString();
    }
    
    public Object clone(){
        DefaultScheduleMaster master = null;
        try{
            master = (DefaultScheduleMaster)super.clone();
        }catch(CloneNotSupportedException e){
        }
        master.startTime = startTime == null ? null : (Date)startTime.clone();
        master.endTime = endTime == null ? null : (Date)endTime.clone();
        master.retryEndTime = retryEndTime == null ? null : (Date)retryEndTime.clone();
        return master;
    }
}