package jp.co.argo21.nautica.workflow.sample.control;

import java.io.IOException;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import jp.co.argo21.nautica.workflow.sample.model.AppKind;
import jp.co.argo21.nautica.workflow.sample.model.AppTransaction;
import jp.co.argo21.nautica.workflow.sample.model.OvertimeApp;
import jp.co.argo21.nautica.workflow.sample.model.dao.AppTransactionDAO;
import jp.co.argo21.nautica.workflow.sample.model.dao.ApplicationDAO;
import jp.co.argo21.nautica.workflow.sample.model.dao.DAOFactory;
import jp.co.argo21.nautica.workflow.sample.model.dao.OvertimeAppDAO;
import jp.co.argo21.nautica.workflow.sample.util.Constants;
import jp.co.argo21.nautica.workflow.sample.util.HtmlUtil;
import jp.co.argo21.nautica.workflow.sample.util.ServiceAccessor;
import jp.co.argo21.nautica.workflow.sample.util.StateConstants;
import jp.co.argo21.nautica.workflow.sample.util.Validator;
import jp.co.argo21.nautica.workflow.sample.util.ValidatorException;
import jp.co.argo21.nautica.workflow.sample.util.WebServiceUtil;
import jp.co.argo21.nautica.workflow.soap.WorkItemHandler;
import jp.co.argo21.nautica.workflow.soap.message.Attribute;
import jp.co.argo21.nautica.workflow.soap.message.Filter;
import jp.co.argo21.nautica.workflow.soap.message.User;
import jp.co.argo21.nautica.workflow.soap.message.WorkItem;

/**
 * 申請書の申請を行う。
 * 
 * @author stokin
 *
 */
public class Apply extends HttpServlet{

	private static final int FIRST_APPROVER = 1;
	private static final int APPLICANT = 0;

	/**
	 * doPostに処理を委譲する。
	 * 
	 * @param req リクエスト
	 * @param res レスポンス
	 * @throws ServletException 発生しない
	 * @throws IOException forwardに失敗した場合
	 */
	public void doGet(HttpServletRequest req, HttpServletResponse res)
	throws ServletException, IOException {
		doPost(req, res);
	}

	/**
	 * 入力内容の確認を行い、申請書を申請する。
	 * 
	 * @param req リクエスト
	 * @param res レスポンス
	 * @throws ServletException 発生しない
	 * @throws IOException forwardに失敗した場合
	 */
	public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException{

		HttpSession session = req.getSession(true);
		req.setCharacterEncoding("UTF-8");
		User user = (User)session.getAttribute("user");
		String appKindID = (String)req.getParameter("appKindID");
		String sessionID = (String)session.getAttribute("sessionID");
		String path = req.getParameter("screen");
		String pressedButton = req.getParameter("pressedButton");
		String appID = req.getParameter("appID");

		ServletContext context = getServletContext();
		RequestDispatcher dispatcher = null;
		try{
			if(path.equals("ApplyInput")){
				//ApplyInputから来たとき
				if(pressedButton.equals("確認")){
					//「確認」ボタンが押されたときは確認処理を行う
										
					//入力内容をセットする
					OvertimeApp rawContent = toOvertimeApp(req);

					//入力内容をサニタイズする
					OvertimeApp content = HtmlUtil.sanitize(rawContent);
					
					//入力エラーをチェックする
					try {
						Validator.validate(content);
					} catch (ValidatorException e) {
						//入力にエラーがあれば、メッセージをセットし入力画面に戻す
						List<String> errMsgs = e.getErrList();
						req.setAttribute("errMsg", errMsgs);
						req.setAttribute("overtimeApp", content);
						dispatcher = context.getRequestDispatcher("/ApplyInput.jsp");
						dispatcher.forward(req, res);
						return;
					}

					//入力内容を申請テーブルと時間外労働申請テーブルに登録する
					ApplicationDAO appDao = DAOFactory.getApplicationDAO();
					appDao.updateApplication(content);
					OvertimeAppDAO overtimeDao = (OvertimeAppDAO)DAOFactory.getApplicationDAOFromAppKindID(appKindID);
					overtimeDao.updateApplication(content);
					
					req.setAttribute("overtime", content);
					session.setAttribute("applyDay", content.getDate());
					dispatcher = context.getRequestDispatcher("/ConfirmApply.jsp");

				} else if(pressedButton.equals("キャンセル")){
					//「キャンセル」ボタンが押されたときはキャンセル処理を行う
					cancel(sessionID, user, appID);
					dispatcher = context.getRequestDispatcher("/Menu.jsp");
				}

			}else if(path.equals("ConfirmApply")){
				//ConfirmApplyから来たとき

				if(pressedButton.equals("申請")){
					//「申請」ボタンが押されたときは申請処理を行う
					Timestamp applyDay = (Timestamp)session.getAttribute("applyDay");
					apply(user, sessionID, appID, applyDay);
					req.setAttribute("appID", appID);
					dispatcher = context.getRequestDispatcher("/CompleteApply.jsp");

				} else if(pressedButton.equals("訂正")){
					//「訂正」ボタンが押されたときは訂正処理を行う
					OvertimeApp overtime = (OvertimeApp)DAOFactory.getApplicationDAO().getApplicationByID(appID);
					req.setAttribute("overtimeApp", overtime);
					dispatcher = context.getRequestDispatcher("/ApplyInput.jsp");
				} else if (pressedButton.equals("キャンセル")){
					//「キャンセル」ボタンが押されたときはキャンセル処理を行う
					cancel(sessionID, user, appID);
					dispatcher = context.getRequestDispatcher("/Menu.jsp");
				}
			}

		} catch (Exception e) {
			// 致命的エラー
			session.invalidate();			
			req.setAttribute("errMsg", e.toString());
			req.setAttribute("err", e.getStackTrace());
			dispatcher = context.getRequestDispatcher("/FatalError.jsp");
		}
		dispatcher.forward(req, res);
	}

	/**
	 * 申請書を申請する。。
	 * 
	 * @param user ユーザ
	 * @param sessionID セッションID
	 * @param appID 申請書番号
	 * @param applyday 申請日時
	 * @throws Exception 申請処理に失敗した場合
	 */
	private void apply(User user, String sessionID, String appID, Timestamp applyday) throws Exception{
		//申請トランザクションテーブルの[承認ブロック番号]に応じて[状態]を"申請"と"待ち"に変更する
		AppTransactionDAO appTransDao = DAOFactory.getAppTransactionDAO();
		AppTransaction[] appTransArray = appTransDao.getAppTransactionsByAppID(appID);
		for (AppTransaction appTrans : appTransArray) {
			int approveAccountID = appTrans.getApproveAccountID();
			if (approveAccountID == APPLICANT) {
				//申請者の[状態]を"待ち"に、[申請日]を申請日に変更する
				appTrans.setAppStatus(StateConstants.STATUS_TRANS_APPLY);
				appTrans.setAppUserID(user.getId());
				appTrans.setDate(applyday);
				appTransDao.updateAppTransaction(appTrans);
			} else if (approveAccountID == FIRST_APPROVER) {
				//最初の承認者の[状態]を"待ち"に変更する
				appTrans.setAppStatus(StateConstants.STATUS_TRANS_WAIT);
				appTransDao.updateStatus(appTrans);
			} 
		}

		//申請書番号で作業項目を取得する
		WorkItemHandler wiHandler = ServiceAccessor.getInstance().getWorkItemHandler();
		List<Filter> appIDFilter = WebServiceUtil.createEqualAttributeFilter(Constants.APPLICATION_NO, appID);
		List<WorkItem> items = wiHandler.getWorkItems(sessionID, appIDFilter);
		// 作業項目は1つだけ取得できるため、0番目を決め打ち
		String wid = items.get(0).getID();

		//プロセス変数[申請日]、[状態]を変更する
		WebServiceUtil.assignWorkItemAttribute(sessionID, wid, Constants.APPLY_DATE, applyday.toString());
		WebServiceUtil.assignWorkItemAttribute(sessionID, wid, Constants.STATE, StateConstants.APPROVAL_WAIT);

		//作業項目の完了を通知する
		wiHandler.completeWorkItem(sessionID, wid);
	}

	/**
	 * 申請の申請をキャンセルする。
	 * 
	 * @param sessionID セッションID
	 * @param user ユーザ
	 * @param appID 申請書番号
	 * @throws Exception 申請キャンセルに失敗した場合
	 */
	private void cancel(String sessionID, User user, String appID) throws Exception{
		// 申請トランザクションテーブルと申請テーブルと時間外労働申請テーブルの該当レコードを削除
		ApplicationDAO appDao = DAOFactory.getApplicationDAO();
		appDao.remove(appID);
		AppTransactionDAO appTransDao = DAOFactory.getAppTransactionDAO();
		appTransDao.remove(appID);

		//申請書番号で作業項目を取得する
		WorkItemHandler wiHandler = ServiceAccessor.getInstance().getWorkItemHandler();
		List<Filter> appIDFilter = WebServiceUtil.createEqualAttributeFilter(Constants.APPLICATION_NO, appID);
		List<WorkItem> items = wiHandler.getWorkItems(sessionID, appIDFilter);
		// 作業項目は1つだけ取得できるため、0番目を決め打ち
		String wid = items.get(0).getID();

		//申請書番号に空文字を設定
		WebServiceUtil.assignWorkItemAttribute(sessionID, wid, Constants.APPLICATION_NO, "");

		//作業項目の完了を通知する
		wiHandler.completeWorkItem(sessionID, wid);
	}
	
	/**
	 * 入力内容をOvertimeAppオブジェクトにつめる。
	 * 
	 * @param req Httpリクエスト
	 * @return 入力内容
	 * @throws SQLException 申請種別の取得に失敗した場合
	 */
	private OvertimeApp toOvertimeApp(HttpServletRequest req) throws SQLException{
		String appKindID = req.getParameter("appKindID");
		String appID = req.getParameter("appID");
		
		Calendar workCalendar = Calendar.getInstance();
		workCalendar.set(Integer.parseInt(req.getParameter("workdayYear")), Integer.parseInt(req.getParameter("workdayMonth"))-1, Integer.parseInt(req.getParameter("workdayDay")));
		Timestamp workday = new Timestamp(workCalendar.getTimeInMillis());
		int startHour = Integer.parseInt(req.getParameter("startHour"));
		int startMin = Integer.parseInt(req.getParameter("startMin"));
		int endHour = Integer.parseInt(req.getParameter("endHour"));
		int endMin = Integer.parseInt(req.getParameter("endMin"));
		float overtimeHours = Float.parseFloat(req.getParameter("overtimeHours"));
		boolean isComp = Boolean.parseBoolean(req.getParameter("compDay"));
		Calendar compCalendar  = Calendar.getInstance();
		compCalendar.set(Integer.parseInt(req.getParameter("compDayYear")), Integer.parseInt(req.getParameter("compDayMonth"))-1, Integer.parseInt(req.getParameter("compDayDay")));
		Timestamp compDay = new Timestamp(compCalendar.getTimeInMillis());
		Timestamp applyday = new Timestamp(System.currentTimeMillis());
		AppKind appKind = DAOFactory.getAppKindDAO().getAppKindFromID(appKindID);

		OvertimeApp preContent = new OvertimeApp();
		preContent.setId(appID);
		preContent.setWorkday(workday);
		preContent.setStartHour(startHour);
		preContent.setStartMin(startMin);
		preContent.setEndHour(endHour);
		preContent.setEndMin(endMin);
		preContent.setOvertimeHours(overtimeHours);
		preContent.setReason(req.getParameter("reason"));
		preContent.setComp(isComp);
		preContent.setCompDay(compDay);
		preContent.setDate(applyday);
		preContent.setRemark(req.getParameter("remarks"));
		preContent.setKind(appKind);		
		
		return preContent;
	}
}