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

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
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.AppTransaction;
import jp.co.argo21.nautica.workflow.sample.model.Application;
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.util.Constants;
import jp.co.argo21.nautica.workflow.sample.util.ResourceManager;
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.StringUtil;
import jp.co.argo21.nautica.workflow.sample.util.WebServiceUtil;
import jp.co.argo21.nautica.workflow.soap.OrganizationManager;
import jp.co.argo21.nautica.workflow.soap.RoleManager;
import jp.co.argo21.nautica.workflow.soap.UserManager;
import jp.co.argo21.nautica.workflow.soap.WorkItemHandler;
import jp.co.argo21.nautica.workflow.soap.WorkflowEngineHandler;
import jp.co.argo21.nautica.workflow.soap.message.Filter;
import jp.co.argo21.nautica.workflow.soap.message.Organization;
import jp.co.argo21.nautica.workflow.soap.message.Process;
import jp.co.argo21.nautica.workflow.soap.message.Role;
import jp.co.argo21.nautica.workflow.soap.message.User;
import jp.co.argo21.nautica.workflow.soap.message.WorkItem;

/**
 * 申請書の一覧と詳細を表示する。
 * 
 * @author stokin
 *
 */
public class ShowApplication extends HttpServlet {

	/**
	 * 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);
		ResourceManager rm = ResourceManager.getInstance();
		User user = (User)session.getAttribute("user");
		String sessionID = (String)session.getAttribute("sessionID");

		ServletContext context = getServletContext();
		RequestDispatcher dispatcher = null;
		String path = req.getParameter("screen");

		try{
			if (path.equals("Menu") || path.equals("AppState")){
				String aim = req.getParameter("aim");

				List<Application> app = null;
				if (aim.equals("APPROVE")){
					//目的が「承認」のときは、承認待ち一覧を表示する
					app = getApprovalList(user, sessionID);		
				} else if (aim.equals("CONFIRM")){
					//目的が「同報確認」のときは、同報申請一覧を表示する
					app = getConfirmList(sessionID, user);
				} else if (aim.equals("WAIT")){
					//目的が「承認状況表示」のときは、承認状況一覧を表示する
					app = getAllList(user, sessionID);
				}

				//閲覧可能な申請がない場合はその旨表示する
				if(app.size() == 0){
					req.setAttribute("errMsg", rm.getValue("error.nolist"));
				} else {
					req.setAttribute("app", app);
				}
				
				session.setAttribute("aim", aim);
				dispatcher = context.getRequestDispatcher("/AppList.jsp");
			} else if (path.equals("AppList")){
				//AppListから来たときには申請書の詳細を表示する
				String appID = (String)req.getParameter("appID");
				OvertimeApp overtime = (OvertimeApp)DAOFactory.getApplicationDAO().getApplicationByID(appID);
				
				OrganizationManager orgManager = ServiceAccessor.getInstance().getOrganizationManager();
				List<Organization> orgList = orgManager.getOrganizationByUser(user);
				//ユーザはひとつの組織にのみ所属するため、0番目を決め打ち
				Organization org = orgList.get(0);
				
				req.setAttribute("overtime", overtime);
				req.setAttribute("org", org);
				
				String aim = (String)session.getAttribute("aim");
				if (aim.equals("APPROVE") || aim.equals("CONFIRM")){
					//目的が「承認」か「同報確認」のとき	
					dispatcher = context.getRequestDispatcher("/App.jsp");
				} else if(aim.equals("WAIT")){
					//目的が「承認状況表示」のとき
					AppTransaction[] appTransArray = getApprovalState(appID);
					req.setAttribute("appTran", appTransArray);
					dispatcher = context.getRequestDispatcher("/AppState.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
	 * @return appList 承認待ち申請書
	 * @throws Exception 承認待ち申請書の取得に失敗した場合
	 */
	private List<Application> getApprovalList(User user, String sessionID) throws Exception{
		List<AppTransaction> appTransList = new ArrayList<AppTransaction>();
		//ユーザがフローに参加している申請書を取得する
		AppTransactionDAO appTransDao = DAOFactory.getAppTransactionDAO();
		AppTransaction[] apptsByUser = appTransDao.getAppTransactionsByUserID(user.getId());
		appTransList.addAll(Arrays.asList(apptsByUser));
		
		//ユーザのロールがフローに含まれる申請の申請書番号を取得する
		//ログインユーザが人事部の一般社員の場合、承認可能
		RoleManager roleManager = ServiceAccessor.getInstance().getRoleManager();
		List<Role> roleList = roleManager.getAttachedRoles2(user);
		List<String> roleIDList = new ArrayList<String>();
		for (Role role : roleList){
			roleIDList.add(role.getId());
		}

		if (roleIDList.contains(Constants.JINJI_ROLE) && !roleIDList.contains(Constants.JINJI_BUCHO_ROLE) && !roleIDList.contains(Constants.JINJI_KACHO_ROLE)){
			AppTransaction[] apptsByRole = appTransDao.getAppTransactionsByRoleID(Constants.JINJI_ROLE);
			appTransList.addAll(Arrays.asList(apptsByRole));
		}
		
		//承認待ちの申請書の申請書番号を取得する
		List<String> appIDList = new ArrayList<String>();
		for (AppTransaction appTrans : appTransList){
			if (appTrans != null && appTrans.getAppStatus().equals(StateConstants.STATUS_TRANS_WAIT)){
				if (!appIDList.contains(appTrans.getAppID())){
					appIDList.add(appTrans.getAppID());
				}
			}
		}
		
		//状態が"APPROVAL_WAIT"である作業項目を取得する
		WorkItemHandler wiHandler = ServiceAccessor.getInstance().getWorkItemHandler();
		List<Filter> state = WebServiceUtil.createEqualAttributeFilter(Constants.STATE, StateConstants.APPROVAL_WAIT);
		List<WorkItem> items = wiHandler.getWorkItems(sessionID, state);
		
		//申請書番号を取得する
		List<String> wiAppIDList = new ArrayList<String>();
		for (WorkItem witem : items){
			String wid = witem.getID();
			String appID = wiHandler.getWorkItemAttributeValue(sessionID, wid, Constants.APPLICATION_NO).getValue();
			wiAppIDList.add(appID);
		}
		
		ApplicationDAO appDao = DAOFactory.getApplicationDAO();
		List<Application> appList = new ArrayList<Application>();
		//状態が"APPROVAL_WAIT"で、かつユーザがフローに参加している申請を取得する
		for (String appID : appIDList){
			if(wiAppIDList.contains(appID)){
				Application app = appDao.getApplicationByID(appID);
				appList.add(app);
			}
		}
		return appList;
	}

	/**
	 * 同報確認待ち申請書を返す。
	 * 
	 * @param sessionID セッションID
	 * @param user ユーザ
	 * @return appList 同報確認待ち申請書
	 * @throws Exception 同報確認待ち申請書の取得に失敗した場合
	 */
	private List<Application> getConfirmList(String sessionID, User user) throws Exception{
		//状態が"CONFIRM_WAIT"である作業項目を取得する
		WorkItemHandler wiHandler = ServiceAccessor.getInstance().getWorkItemHandler();
		List<Filter> state = WebServiceUtil.createEqualAttributeFilter(Constants.STATE, StateConstants.CONFIRM_WAIT);
		List<WorkItem> items = wiHandler.getWorkItems(sessionID, state);
		
		//作業項目の申請書番号を取得し、そこから申請書を取得する。
		List<Application> appList = new ArrayList<Application>();
		for (WorkItem item : items) {
			String wid = item.getID();
			String appID = wiHandler.getWorkItemAttributeValue(sessionID, wid, Constants.APPLICATION_NO).getValue();
			Application app = DAOFactory.getApplicationDAO().getApplicationByID(appID);
			appList.add(app);
		}
		return appList;
	}

	/**
	 * 承認状況を確認できる申請書を返す。
	 * 
	 * @param user ユーザ
	 * @param sessionID セッションID
	 * @return appList 承認状況を確認できる申請書
	 * @throws Exception 承認状況を確認できる申請書の取得に失敗した場合
	 */
	private List<Application> getAllList(User user, String sessionID) throws Exception{
		List<String> appIDList = new ArrayList<String>();
		// ユーザがフローに参加している申請の申請書番号を取得する
		AppTransaction[] apptsByUser = DAOFactory.getAppTransactionDAO().getAppTransactionsByUserID(user.getId());
		addAppID(appIDList, apptsByUser);

		// ユーザのロールがフローに含まれる申請の申請書番号を取得する
		RoleManager roleManager = ServiceAccessor.getInstance().getRoleManager();
		List<Role> roleList = roleManager.getAttachedRoles2(user);
		AppTransactionDAO appTransDao = DAOFactory.getAppTransactionDAO();
		for (Role role : roleList) {
			AppTransaction[] apptsByRole = appTransDao.getAppTransactionsByRoleID(role.getId());
			addAppID(appIDList, apptsByRole);
		}

		// プロセスが実行中である申請を取得する
		WorkflowEngineHandler wfEngineHandler = ServiceAccessor.getInstance().getWfEngineHandler();
		List<Filter> stateFilter = WebServiceUtil.createEqualStateFilter("ProcessState", "open.running");
		List<Process> processes = wfEngineHandler.getProcesses(sessionID, stateFilter);
		
		//申請書番号を取得する
		List<String> processAppID = new ArrayList<String>();
		for (Process process : processes){
			processAppID.add(process.getName());
		}

		ApplicationDAO appDao = DAOFactory.getApplicationDAO();
		List<Application> appList = new ArrayList<Application>();
		//プロセスが実行中で、かつ、ユーザがフローに含まれる申請を取得する。
		for(String appID : appIDList){
			if (processAppID.contains(appID)) {
				Application app = appDao.getApplicationByID(appID);
				appList.add(app);
			}
		}

		return appList;
	}

	/**
	 * 承認状況を返す。
	 * 
	 * @param appID 申請書番号
	 * @return appTransArray 承認状況を示す申請トランザクションオブジェクト
	 * @throws Exception 承認状況の取得に失敗した場合
	 */
	private AppTransaction[] getApprovalState(String appID) throws Exception{
	
			AppTransactionDAO appTransDao = DAOFactory.getAppTransactionDAO();
			AppTransaction[] appTransArray = appTransDao.getAppTransactionsByAppID(appID);
	
			//申請トランザクションテーブルのAppUserIDを氏名に変更する
			UserManager userManager = ServiceAccessor.getInstance().getUserManager();
			for (AppTransaction appTrans : appTransArray) {
				String userID = appTrans.getAppUserID();
				if (userID != null && !userID.equals("")) {
					String userName = StringUtil.toFullName(userManager.getUser(userID));
					appTrans.setAppUserID(userName);
				} else {
					String roleID = appTrans.getAppRoleID();
					if ((Constants.JINJI_ROLE).equals(roleID)) {
						appTrans.setAppUserID(Constants.JINJI_DEPT);
					}                 
				}
			}		
			return appTransArray;
	}

	/**
	 * リストに含まれていない申請書番号を追加して返す。
	 * 
	 * @param list　申請書番号のリスト
	 * @param appts　申請トランザクション
	 * @throws Exception　申請トランザクションテーブルの検索に失敗した場合
	 */
	private void addAppID(List list, AppTransaction[] appts) throws Exception {
		for (int i = 0; i < appts.length; i++) {
			String id = appts[i].getAppID();
			AppTransactionDAO appTransDao = DAOFactory.getAppTransactionDAO();
			AppTransaction appTrans = appTransDao.getAppTransactionByAppIDBlockID(id, 0);            
			if (!list.contains(id) && (StateConstants.STATUS_TRANS_APPLY).equals(appTrans.getAppStatus())) {
				list.add(id);
			}
		}
	}
}