/*
 * Copyright 2007 Kickmogu Co. Ltd. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */



/*
 * 作成日: 2006/12/18
 *
 */
package com.kickmogu.rhythm.todo.ws;

import java.util.List;
import java.util.HashMap;
import java.util.Map;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Calendar;
import java.lang.reflect.InvocationTargetException;
import java.sql.*;
import java.text.SimpleDateFormat;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.log4j.Category;
import com.kickmogu.core.XmlUtils;
import com.kickmogu.rhythm.todo.*;
import com.kickmogu.rhythm.core.*;
import com.kickmogu.rhythm.core.dao.MemberDao;
import com.kickmogu.rhythm.todo.dao.TodoDao;
import com.kickmogu.rhythm.todo.dao.TodoWorkerDao;
import com.kickmogu.s2.DAOSequence;
import com.kickmogu.s2.S2Util;

public class TodoWs {
	private static Map<String, List<Todo>> holidayMaster = new HashMap<String, List<Todo>>();
	private static SimpleDateFormat monthDt = new SimpleDateFormat("yyyyMM");
	
	TodoDao tododao = (TodoDao) S2Util.getS2Container().getComponent(TodoDao.class);
	TodoWorkerDao workerdao = (TodoWorkerDao) S2Util.getS2Container().getComponent(TodoWorkerDao.class);
	MemberDao memdao = (MemberDao) S2Util.getS2Container().getComponent(MemberDao.class);

	static Category log = Category.getInstance(TodoWs.class.getName());

	public BaseResult addNode(Member loginMember, String name, int parentId) throws Exception {
		BaseResult result = new BaseResult();
		if (!loginMember.isLogined(result)) {
			return result;
		}
		TodoTree todotree = new TodoTree();
		todotree.setId(DAOSequence.getNextId(TodoTree.UNIQ_KEYNAME));
		todotree.setName(name);
		TodoTree parent = (TodoTree) RhythmTreeManager.getRoot(TodoTree.class).search(parentId);
		if (parent == null) {
			result.setStatusCode(100);
			return result;
		}
		todotree.addToParent(parent);
		RhythmTreeManager.write(TodoTree.class);
		result.setResult(todotree.getId());
		return result;
	}

	public BaseResult removeNode(Member loginMember, int targetId) throws Exception {
		BaseResult result = new BaseResult();
		if (!loginMember.isLogined(result)) {
			return result;
		}
		TodoTree todoTree = (TodoTree) RhythmTreeManager.getRoot(TodoTree.class).search(targetId);
		todoTree.remove();
		RhythmTreeManager.write(TodoTree.class);
		result.setResult(todoTree.getId());
		return result;
	}

	public BaseResult updateNode(Member loginMember, int targetId, String newName) throws Exception {
		BaseResult result = new BaseResult();
		if (!loginMember.isLogined(result)) {
			return result;
		}
		TodoTree todoTree = (TodoTree) RhythmTreeManager.getRoot(TodoTree.class).search(targetId);
		todoTree.setName(newName);
		RhythmTreeManager.write(TodoTree.class);
		result.setResult(todoTree.getId());
		return result;
	}
	
	public BaseResult post(Member loginMember, int categoryId, Todo todo) throws Exception {
		BaseResult result = new BaseResult();
		if (!loginMember.isLogined(result)) {
			return result;
		}
		todo.setId(DAOSequence.getNextId(Todo.UNIQ_KEYNAME));
		todo.setRegistrant(loginMember.getId());
		todo.setRootId(todo.getId());
		todo.setTodoTreeId(categoryId);
		todo.setRegistDate(new Date());
		tododao.insert(todo);
		deleteExtractLoopTodo(todo);
		doExtractLoopTodo(todo);
		result.setResult(todo);
		log.info("post todo:" + todo.getId() + "," + todo.getSubject());
		return result;
	}
	
	public BaseResult reply(Member loginMember, int parentId, Todo todo) throws Exception {
		BaseResult result = new BaseResult();
		if (!loginMember.isLogined(result)) {
			return result;
		}
		Todo parentTodo = tododao.getTodo(loginMember.getId(), parentId);
		int newSeq = getChildMaxSeq(parentTodo.getRootId(), parentTodo.getSeq(), parentTodo.getDepth()) + 1;
		updateThreadSeq(parentTodo.getRootId(), newSeq);
		todo.setId(DAOSequence.getNextId(Todo.UNIQ_KEYNAME));
		todo.setRegistrant(loginMember.getId());
		todo.setRootId(parentTodo.getRootId());
		todo.setSeq(newSeq);
		todo.setDepth(parentTodo.getDepth() + 1);
		todo.setTodoTreeId(parentTodo.getTodoTreeId());
		todo.setRegistDate(new Date());
		tododao.insert(todo);
		deleteExtractLoopTodo(todo);
		doExtractLoopTodo(todo);
		result.setResult(todo);
		log.info("reply todo:" + todo.getId() + "," + parentId + "," + todo.getSubject());
		return result;
	}
	
	public BaseResult addWorker(Member loginMember, int todoId, int memberId, int kind) throws Exception {
		BaseResult result = new BaseResult();
		if (!loginMember.isLogined(result)) {
			return result;
		}
		TodoWorker worker = new TodoWorker();
		worker.setId(DAOSequence.getNextId(TodoWorker.UNIQ_KEYNAME));
		worker.setTodoId(todoId);
		worker.setMemberId(memberId);
		worker.setKind(kind);
		workerdao.insert(worker);
		result.setResult(worker);
		return result;
	}
	
	public BaseResult removeWorker(Member loginMember, int workerId) throws Exception {
		BaseResult result = new BaseResult();
		if (!loginMember.isLogined(result)) {
			return result;
		}
		TodoWorker worker = workerdao.getTodoWorker(workerId);
		workerdao.delete(worker);
		result.setResult(worker);
		return result;
	}
	
	public BaseResult setWorkerKind(Member loginMember, int workerId, int newKind) throws Exception {
		BaseResult result = new BaseResult();
		if (!loginMember.isLogined(result)) {
			return result;
		}
		TodoWorker worker = workerdao.getTodoWorker(workerId);
		worker.setKind(newKind);
		workerdao.update(worker);
		result.setResult(worker);
		return result;
	}
	
	public BaseResult listRootTodos(Member loginMember, int categoryId) {
		BaseResult result = new BaseResult();
		if (!loginMember.isLogined(result)) {
			return result;
		}
		result.setResult(tododao.listRootTodos(loginMember.getId(), categoryId));
		return result;
	}
	
	public BaseResult listCategoryAllTodos(Member loginMember, int categoryId) {
		BaseResult result = new BaseResult();
		if (!loginMember.isLogined(result)) {
			return result;
		}
		Todo[] rootTodos = tododao.listRootTodos(loginMember.getId(), categoryId);
		StringBuffer tags = new StringBuffer();
		for (int i = 0; i < rootTodos.length; i++) {
			tags.append(listTodoThreads(loginMember, rootTodos[i].getId()).getResult() + "\n");
		}
		result.setResult(tags.toString());
		return result;
	}
	
	public BaseResult getTodo(Member loginMember, int todoId) {
		BaseResult result = new BaseResult();
		if (!loginMember.isLogined(result)) {
			return result;
		}
		result.setResult(tododao.getTodo(loginMember.getId(), todoId));
		return result;
	}
	
	private int getChildMaxSeq(int rootId, int seq, int depth) throws Exception {
		int maxSeq = seq;
		Connection conn = S2Util.getConnection();
		PreparedStatement ps = conn.prepareStatement("select seq, depth from todo where deleted = 0 and rootId = ? and seq > ? order by seq");
		try {
			ps.setInt(1, rootId);
			ps.setInt(2, seq);
			ResultSet rs = ps.executeQuery();
			while (rs.next()) {
				if (depth >= rs.getInt("depth")) {
					break;
				}
				maxSeq = rs.getInt("seq");
			}
		} finally {
			ps.close();
			conn.close();
		}
		return maxSeq;
	}
	
	private void updateThreadSeq(int rootId, int seq) throws Exception {
		Connection conn = S2Util.getConnection();
		PreparedStatement ps = conn.prepareStatement("update todo set seq = seq + 1 where deleted = 0 and rootId = ? and seq >= ?");
		try {
			ps.setInt(1, rootId);
			ps.setInt(2, seq);
			ps.execute();
		} finally {
			ps.close();
			conn.close();
		}
	}
	
	private String toTodoXmlTag(Todo todo) {
		try {
			return "<todo " + XmlUtils.bean2attributes(todo, new String[] {"registDate"})
				+ " registDate=\"" + todo.getRegistDate().getTime() + "\""
				+ ">";
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 特定のThreadの親子関係を含めたXML形式文字列を返す
	 * @param loginMember
	 * @param rootId
	 * @return
	 */
	public BaseResult listTodoThreads(Member loginMember, int rootId) {
		BaseResult result = new BaseResult();
		if (!loginMember.isLogined(result)) {
			return result;
		}
		Todo[] threads = tododao.listTodoThreads(loginMember.getId(), rootId);
		StringBuffer out = new StringBuffer();
		int beforeDepth = -1;
		for (int i = 0; i < threads.length; i++) {
			if (beforeDepth < threads[i].getDepth()) {
				for (int j = 0; j < threads[i].getDepth() - beforeDepth; j++) {
					out.append(toTodoXmlTag(threads[i]) + "\n");
				}
			} else {
				for (int j = 0; j < beforeDepth - threads[i].getDepth() + 1; j++) {
					out.append("</todo>\n");
				}
				out.append(toTodoXmlTag(threads[i]) + "\n");
			}
			beforeDepth = threads[i].getDepth();
		}
		for (int j = 0; j < beforeDepth + 1; j++) {
			out.append("</todo>\n");
		}
		result.setResult(out.toString());
		return result;
	}
	
	/**
	 * 特定のThreadの親子関係を含めたXML形式文字列を返す
	 * @param loginMember
	 * @param rootId
	 * @return
	 */
	public BaseResult listTodoFlat(Member loginMember, int categoryId) {
		BaseResult result = new BaseResult();
		if (!loginMember.isLogined(result)) {
			return result;
		}
		Todo[] rootTodos = tododao.listRootTodos(loginMember.getId(), categoryId);
		List<Todo> todos = new ArrayList<Todo>();
		for (int i = 0; i < rootTodos.length; i++) {
			todos.addAll(Arrays.asList(tododao.listTodoFlat(loginMember.getId(), rootTodos[i].getId())));
		}

		result.setResult(todos);
		return result;
	}
	
	public BaseResult listWorkers(Member loginMember, int todoid) {
		BaseResult result = new BaseResult();
		if (!loginMember.isLogined(result)) {
			return result;
		}
		result.setResult(workerdao.listTodoWorkers(todoid));
		return result;
	}
	
	public BaseResult remove(Member loginMember, int targetId) {
		BaseResult result = new BaseResult();
		if (!loginMember.isLogined(result)) {
			return result;
		}
		Todo targetTodo = tododao.getTodo(loginMember.getId(), targetId);
		List<Todo> threads = Arrays.asList(tododao.listTodoThreads(loginMember.getId(), targetTodo.getRootId()));
		int idx = threads.indexOf(targetTodo);
		if (idx == -1) {
			log.info("remove todo not found:" + targetTodo.getId() + "," + threads.size());
			result.setStatusCode(100);
			return result;
		}
		for (int i = idx + 1; i < threads.size(); i++) {
			if (targetTodo.getDepth() >= threads.get(i).getDepth()) {
				break;
			}
			threads.get(i).setDeleted(1);
			tododao.update(threads.get(i));
		}
		targetTodo.setDeleted(1);
		tododao.update(targetTodo);
		return result;
	}

	public BaseResult update(Member loginMember, Todo todo) throws Exception {
		BaseResult result = new BaseResult();
		if (!loginMember.isLogined(result)) {
			return result;
		}
		tododao.update(todo);
		deleteExtractLoopTodo(todo);
		doExtractLoopTodo(todo);
		return result;
	}
	
	private void deleteExtractLoopTodo(Todo todo) throws Exception {
		Connection conn = S2Util.getConnection();
		PreparedStatement ps = conn.prepareStatement("delete from todo where parentId = ?");
		try {
			ps.setInt(1, todo.getId());
			ps.execute();
		} finally {
			ps.close();
			conn.close();
		}
	}
	
	private void doExtractLoopTodo(Todo todo) throws IllegalAccessException, InvocationTargetException {
		if (todo.getLoopUnit() == Todo.LOOP_UNIT_NONE) {
			return;
		}
		Calendar loopDate = Calendar.getInstance();
		loopDate.setTime(todo.getLoopTarget());
		for (int i = 0; i < todo.getLoopCount(); i++) {
			Todo loopTodo = new Todo();
			BeanUtils.copyProperties(loopTodo, todo);
			loopTodo.setId(DAOSequence.getNextId(Todo.UNIQ_KEYNAME));
			loopTodo.setParentId(todo.getId());
			switch (todo.getLoopUnit()) {
			case Todo.LOOP_UNIT_DAY:
				loopDate.add(Calendar.DATE, todo.getLoopInterval());
				break;
			case Todo.LOOP_UNIT_WEEK:
				loopDate.add(Calendar.DATE, todo.getLoopInterval() * 7);
				break;
			case Todo.LOOP_UNIT_MONTH:
				loopDate.add(Calendar.MONTH, todo.getLoopInterval());
				break;
			case Todo.LOOP_UNIT_YEAR:
				loopDate.add(Calendar.YEAR, todo.getLoopInterval());
				break;
			}
			loopTodo.setPlanFromDate(loopDate.getTime());
			loopTodo.setPlanToDate(loopDate.getTime());
			tododao.insert(loopTodo);
		}
	}

	public BaseResult listSchedules(Member loginMember, Date fromDate, Date toDate) throws Exception {
		BaseResult result = new BaseResult();
		if (!loginMember.isLogined(result)) {
			return result;
		}
		List<PermissionTarget> ptTree = loginMember.getAssignTarget(Todo.PERM_VIEW);
		List<Integer> idcond = new ArrayList<Integer>();
		for (int i = 0; ptTree != null && i < ptTree.size(); i++) {
			List<RhythmTree> ptAll = ptTree.get(i).getAll();
			for (int j = 0; j < ptAll.size(); j++) {
				idcond.add(ptAll.get(j).getId());
			}
		}
		if (idcond.size() == 0) {
			result.setResult(new Todo[] {});
			return result;
		}
		int[] intIds = new int[idcond.size()];
		for (int i = 0; i < idcond.size(); i++) {
			intIds[i] = idcond.get(i);
		}

		List<Integer> memidcond = new ArrayList<Integer>();
		List<PermissionTarget> roles = loginMember.getAssignTarget(Todo.CALENDAR_VIEW);
		memidcond.add(loginMember.getId());
		for (int i = 0; i < roles.size(); i++) {
			Member[] members = memdao.listMembers(roles.get(i).getId());
			for (int j = 0; j < members.length; j++) {
				memidcond.add(members[j].getId());
			}
		}
		int[] memIds = new int[memidcond.size()];
		for (int i = 0; i < memidcond.size(); i++) {
			memIds[i] = memidcond.get(i);
		}
		// 休日マスタの読み込み
		if (holidayMaster.size() == 0) {
			readHolidays();
		}
		List<Todo> holiArray = holidayMaster.get(monthDt.format(fromDate));
		holiArray = new ArrayList<Todo>(holiArray != null ? holiArray : new ArrayList<Todo>());
		holiArray.addAll(Arrays.asList(tododao.listSchedules(loginMember.getId(), memIds, intIds, loginMember.getId(), fromDate, toDate, fromDate, toDate)));
		result.setResult(holiArray);
		return result;
	}
	
	private void readHolidays() {
		Todo[] holidays = tododao.listHolidays();
		for (int i = 0; i < holidays.length; i++) {
			String yymm = monthDt.format(holidays[i].getPlanFromDate());
			List<Todo> holiArray = holidayMaster.get(yymm);
			if (holiArray == null) {
				holiArray = new ArrayList<Todo>();
				holidayMaster.put(yymm, holiArray);
			}
			holiArray.add(holidays[i]);
		}
	}

	public void wsdldummy(Todo todo, TodoWorker worker) {
		
	}
}
