/**
 * Copyright (c) 2006, yher2.net
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 * 
 * * Redistributions of source code must retain the above copyright notice, 
 *   this list of conditions and the following disclaimer.
 * * 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.
 * * Neither the name of the nor the names of its contributors may be used to endorse or 
 *   promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER 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.
 */
package net.yher2.workstyle.manager;

import java.io.IOException;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.yher2.commons.lang.Pager;
import net.yher2.workstyle.FileData;
import net.yher2.workstyle.TaskQuery;
import net.yher2.workstyle.exception.NotFoundException;
import net.yher2.workstyle.torque.AttachedFilePeer;
import net.yher2.workstyle.torque.Status;
import net.yher2.workstyle.torque.StatusPeer;
import net.yher2.workstyle.torque.Tag;
import net.yher2.workstyle.torque.Task;
import net.yher2.workstyle.torque.TaskComment;
import net.yher2.workstyle.torque.TaskCommentPeer;
import net.yher2.workstyle.torque.TaskPeer;
import net.yher2.workstyle.torque.bean.TaskBean;
import net.yher2.workstyle.torque.bean.TaskCommentBean;

import org.apache.commons.lang.StringUtils;
import org.apache.torque.NoRowsException;
import org.apache.torque.TorqueException;
import org.apache.torque.util.Criteria;

public class TaskManager {
	Connection con = null;

	public TaskManager(Connection con) {
		this.con = con;
	}

	public List<TaskBean> search(TaskQuery query, Pager pager) throws TorqueException {
		Criteria criteria = new Criteria();
		setSearchCriteria(criteria, query);
		if (pager != null) {
			Criteria criteriaPager = new Criteria();
			setSearchCriteria(criteriaPager, query);
			pager.setRowCount(TaskPeer.doSelectCount(criteriaPager, con));
			
			criteria.setLimit(pager.getLimit());
			criteria.setOffset(pager.getOffset());
		}
		switch (query.getOrder()) {
		case TaskQuery.ORDER_BY_STATUS_ASC:
			criteria.addAscendingOrderByColumn(StatusPeer.SORT_ORDER);
			break;
		case TaskQuery.ORDER_BY_STATUS_DESC:
			criteria.addDescendingOrderByColumn(StatusPeer.SORT_ORDER);
			break;
		case TaskQuery.ORDER_BY_ID_ASC:
			criteria.addAscendingOrderByColumn(TaskPeer.TASK_ID);
			break;
		case TaskQuery.ORDER_BY_ID_DESC:
			criteria.addDescendingOrderByColumn(TaskPeer.TASK_ID);
			break;
		case TaskQuery.ORDER_BY_DATE_ASC:
			criteria.addAscendingOrderByColumn(TaskPeer.UPDATE_DATE);
			break;
		case TaskQuery.ORDER_BY_DATE_DESC:
			criteria.addDescendingOrderByColumn(TaskPeer.UPDATE_DATE);
			break;
		case TaskQuery.ORDER_BY_MAN_HOUR_ASC:
			criteria.addAscendingOrderByColumn(TaskPeer.ESTIMATED_MAN_HOUR);
			break;
		case TaskQuery.ORDER_BY_MAN_HOUR_DESC:
			criteria.addDescendingOrderByColumn(TaskPeer.ESTIMATED_MAN_HOUR);
			break;
		}
		criteria.addAscendingOrderByColumn(StatusPeer.SORT_ORDER);
		criteria.addAscendingOrderByColumn(TaskPeer.TASK_ID);
		criteria.addAscendingOrderByColumn(TaskPeer.UPDATE_DATE);
		criteria.addAscendingOrderByColumn(TaskPeer.ESTIMATED_MAN_HOUR);
		
		List<Task> taskList = TaskPeer.doSelectJoinStatus(criteria, con);
		
		List<TaskBean> beanList = new ArrayList<TaskBean>();
		for (Task task : taskList) {
			beanList.add(task.getBean());
		}
		return beanList;
	}

	public List<TaskBean> searchDetail(TaskQuery query, Pager pager) throws TorqueException {
		List<TaskBean> beanList = search(query, pager);
		if (beanList.size() == 0) return beanList;
		
		List<Integer> taskIdList = new ArrayList<Integer>();
		Map<Integer, TaskBean> taskMap = new HashMap<Integer, TaskBean>();
		for (TaskBean task : beanList) {
			taskIdList.add(task.getTaskId());
			taskMap.put(task.getTaskId(), task);
			if (task.getTaskCommentBeans() == null) {
				task.setTaskCommentBeans(new ArrayList<TaskCommentBean>());
			}
		}
		
		Criteria criteria = new Criteria();
		criteria.addIn(TaskCommentPeer.TASK_ID, taskIdList);
		criteria.addAscendingOrderByColumn(TaskCommentPeer.TASK_ID);
		criteria.addAscendingOrderByColumn(TaskCommentPeer.UPDATE_DATE);
		
		List<TaskComment> commentList = TaskCommentPeer.doSelect(criteria, con);
		for (TaskComment comment : commentList) {
			TaskCommentBean bean = comment.getBean();
			taskMap.get(bean.getTaskId()).getTaskCommentBeans().add(bean);
		}
		
		return beanList;
	}
	
	private void setSearchCriteria(Criteria criteria, TaskQuery query) {
		criteria.addJoin(TaskPeer.STATUS_ID, StatusPeer.STATUS_ID);
		if (query.hasStatus()) {
			criteria.addIn(StatusPeer.STATUS_ID, query.getStatus());
		}
		if (query.hasKeyword()) {
			List<String> keywordList = query.getKeywordAsList(); 
			for (String keyword : keywordList) {
				keyword = "%" + keyword + "%";
				Criteria.Criterion	criterion = criteria.getNewCriterion(TaskPeer.CONTENTS, keyword, Criteria.LIKE);
				criterion.or(criteria.getNewCriterion(TaskPeer.TAG_LIST, keyword, Criteria.LIKE));

				Criteria.Criterion exist = criteria.getCriterion(TaskPeer.CONTENTS);
				if (exist == null) {
					criteria.add(criterion);
				} else {
					exist.and(criterion);
				}
			}
		}
		if (query.hasTag()) {
			for (String tag : query.getTag()) {
				tag = "%" + Tag.format(tag) + "%";
				Criteria.Criterion criterion = criteria.getNewCriterion(TaskPeer.TAG_LIST, tag, Criteria.LIKE);
				Criteria.Criterion exist = criteria.getCriterion(TaskPeer.TAG_LIST);
				if (exist == null) {
					criteria.add(criterion);
				} else {
					exist.and(criterion);
				}
			}
		}
		if (query.hasDateFrom()) {
			criteria.add(TaskPeer.UPDATE_DATE, query.getDateFrom(), Criteria.GREATER_EQUAL);
		}
	}
	
	public TaskBean add(TaskBean task, TaskCommentBean comment, FileData file) throws TorqueException, NotFoundException, IOException {
		TaskBean taskBean = add(task);
		if (comment != null) {
			comment.setTaskId(taskBean.getTaskId());
			new TaskCommentManager(con).add(comment);
		}
		if (file != null) new FileManager(con).add(taskBean.getTaskId(), file);
		return taskBean;
	}
	public TaskBean add(TaskBean task) throws NotFoundException, TorqueException {
		task.setStatusId(getValidStatusId(task.getStatusId()));
		
		Task dbTask = new Task();
		dbTask.setBean(task);
		dbTask.setUpdateDate(new Date());
		
		TaskPeer.doInsert(dbTask, con);
		
		new TagManager(con).add(task.getTagAsList());
		
		return dbTask.getBean();
	}

	public TaskBean update(TaskBean task, TaskCommentBean comment, FileData file) throws TorqueException, NotFoundException, IOException {
		TaskBean taskBean = null;

		taskBean = update(task);
		if (comment != null) {
			comment.setTaskId(taskBean.getTaskId());
			new TaskCommentManager(con).add(comment);
		}
		if (file != null) new FileManager(con).add(taskBean.getTaskId(), file);
			
		return taskBean;
	}
	public TaskBean update(TaskBean task) throws NotFoundException, TorqueException {
		int updateStatusId = task.getStatusId();
		task.setStatusId(getValidStatusId(task.getStatusId()));
		
		Task dbTask = getDBModel(task.getTaskId());
		if (updateStatusId != task.getStatusId()) {
			task.setStatusId(dbTask.getStatusId());
		}
		dbTask.setBean(task);
		dbTask.setUpdateDate(new Date());
		
		TaskPeer.doUpdate(dbTask, con);
		
		new TagManager(con).add(task.getTagAsList());
		
		return dbTask.getBean();
	}
	
	protected int getValidStatusId(int statusId) throws NotFoundException, TorqueException {
		try {
			StatusPeer.retrieveByPK(statusId, con);
			return statusId;
		} catch (NoRowsException e) {
			return Status.NORMAL;
		}
	}
	public TaskBean get(int taskId) throws TorqueException, NotFoundException {
		Task task = getDBModel(taskId);
		task.getStatus(con);
		task.getTaskComments(con);
		task.getAttachedFiles(con);
		return task.getBean();
	}
	protected Task getDBModel(int taskId) throws TorqueException, NotFoundException {
		try {
			return TaskPeer.retrieveByPK(taskId, con);
		} catch (NoRowsException e) {
			throw new NotFoundException("指定のタスク("+taskId+")");
		}
	}
	
	public void delete(int taskId) throws TorqueException, NotFoundException {
		Criteria criteria = new Criteria();
		criteria.add(AttachedFilePeer.TASK_ID, taskId);
		new FileManager(con).deleteAttachedFile(AttachedFilePeer.doSelect(criteria, con));
		
		criteria.clear();
		criteria.add(TaskCommentPeer.TASK_ID, taskId);
		TaskCommentPeer.doDelete(criteria, con);
		
		criteria.clear();
		criteria.add(TaskPeer.TASK_ID, taskId);
		TaskPeer.doDelete(criteria, con);
	}
	
	public void updateStatus(int taskId, int statusId) throws TorqueException, NotFoundException {
		int validStatusId = getValidStatusId(statusId);
		if (validStatusId == statusId) {
			Task task = getDBModel(taskId);
			task.setStatusId(statusId);
			task.setUpdateDate(new Date());
			TaskPeer.doUpdate(task, con);
		}
	}
	
	public void addTag(int taskId, String tag) throws TorqueException, NotFoundException {
		if (StringUtils.isBlank(tag)) return;
		Task task = getDBModel(taskId);
		task.setTagList(task.getTagList() + Tag.format(tag));
		task.setUpdateDate(new Date());
		TaskPeer.doUpdate(task, con);
		
		new TagManager(con).add(tag);
	}
	public void deleteTag(int taskId, String tag) throws TorqueException, NotFoundException {
		Task task = getDBModel(taskId);
		task.setTagList(task.getTagList().replaceAll(Tag.formatRegExp(tag), ""));
		task.setUpdateDate(new Date());
		TaskPeer.doUpdate(task, con);
	}
}
