/* Copyright 2008 we-lab-doc! (http://www.we-lab-doc.com/)
 *
 * 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.
 */
package com.we_lab_doc.spacecard.model

import com.we_lab_doc.spacecard.model.abstracts.*
import com.we_lab_doc.spacecard.ac.AuthUser
import com.we_lab_doc.spacecard.utils.*

/**
 * @author Shigeru GOUGI
 * @since 0.1
 *        <p/>
 *        Created: February 24, 2008
 */ 
final class User extends Person  implements AuthUser, SpaceCardDomainModel {
	
	boolean remove() {
		for( dataTrays in ModelUtils.cloneCollection(dataTrays)) {
	        if(! dataTrays.remove()) return false			
		}
		for ( cardHolder in ModelUtils.cloneCollection(cardHolders)){
			if(! cardHolder.remove()) return false
		}
		for( role in  roles) {
			role.removeFromUsers(this)
		}
		return ModelUtils.delete(this)
	}
	////
	boolean isSupervisor() {
		def roles = Role.select("join r.users as u where r.organization.isSupervisorCommunity=true and u=:user", [user:this])
		if(roles.size() == 0) return false
		return true
	}
	boolean isAdmin(Organization org) {
		assert org != null
		def roles = Role.select("join r.users as u where r.authority=:authority and r.organization=:organization and u=:user", [authority:AuthorityType.ADMIN, organization:organization, user:this])
		if(roles.size() == 0) return false
		return true
	}
	boolean isUser(Organization org) {
		assert org != null
		def roles = Role.select("join r.users as u where r.authority=:authority and r.organization=:organization and u=:user", [authority:AuthorityType.USER, organization:organization, user:this])
		if(roles.size() == 0) return false
		return true
	}
	///
	User deactivate(){
		// TODO: inactiveに出来る条件を明確にする必要がある;RuntimeExceptionを投げるようになる可能性がある。
		this.active = false
		return this
	}
	User activate(){
		this.active = true
		return this
	}
	///
	Collection getAdministeringOrganizations(boolean includesInactive, Map paginateParams) {
		return _getOrganizations(includesInactive, null, paginateParams)
	}	
	Collection getBelongingOrganizations(boolean includesInactive, boolean includesAsAdmin, paginateParams) {
		return _getOrganizations(includesInactive, includesAsAdmin ? Boolean.TRUE : Boolean.FALSE, paginateParams )
	}
	int countOfAdministeringOrganizations(boolean includesInactive) {
		return _countOfOrganizations(includesInactive, null)
	}
	int countOfBelongingOrganizations(boolean includesInactive, boolean includesAsAdmin) {
		return _countOfOrganizations(includesInactive, includesAsAdmin)
	}
	////
	Collection getAssignedWorkCards(Map paginateParams) {
		Map sParams = PaginateUtils.makeSelectParams(paginateParams, [user:this])
		return WorkCard.select("join w.assigns as a where a.user=:user ${sParams.orderByClause}", sParams.args)
		//memo: 削除されているWorkCardは含めない
	}
	int countOfAssignedWorkCards() {
		Integer[] counts = SpaceCardModel.executeQuery(
				"select count(w) from WorkCard as w join w.assigns as a where a.user=:user", [user:this]
			)
		return counts[0].intValue()		
	}
	////
	Collection getAssigns(Map paginateParams) {
		Map sParams = PaginateUtils.makeSelectParams(paginateParams, [user:this])
		return Assign.select("where a.user=:user ${sParams.orderByClause}", sParams.args)
	}
	int countOfAssigns() {
		Integer[] counts = SpaceCardModel.executeQuery(
				"select count(a) from Assign as a where a.user=:user", [user:this]
			)
		return counts[0].intValue()		
	}
	/////
	CardHolder getCardHolder(String name) {
		assert name != null
		def cardHolders = UserCardHolder.select("where name=:name and user=:user", [name:name, user:this])
		if(cardHolders.size() == 0) return null
		return cardHolders[0]
	}
	CardHolder createCardHolder(String name) {
		assert name != null
		return UserCardHolder.create([name:name, user:this])
	}
	/////////////////////////////////////////////
	static User getActiveUserByAccount(account) {
		assert account != null
		return User.findByAccountAndActive(account, true)
	}
	static User getUserByAccount(account) {
		assert account != null
		return User.findByAccount(account)
	}
	////
	static User createSupervisor(Role role) {
		assert role != null
		def spaceCardModelProperties = SpaceCardModel.props
		def user = new User (
				account:spaceCardModelProperties.supervisorAccount,
				lastName:spaceCardModelProperties.defaultSupervisorLastName,
				firstName:spaceCardModelProperties.defaultSupervisorFirstName,
				lastNameKana:spaceCardModelProperties.defaultSupervisorLastNameKana,
				firstNameKana:spaceCardModelProperties.defaultSupervisorFirstNameKana,				
				password:User.encodePassword(spaceCardModelProperties.defaultSupervisorPassword),
				email:spaceCardModelProperties.defaultSupervisorEmail
				)
		role.addToUsers(user)
		return user
	}
	static User create(String account, String lastName, String firstName, 
			String lastNameKana, String firstNameKana,	
			String password, String email, String description
			) {
		return create(account, lastName, firstName, 
				lastNameKana, firstNameKana,	
				password, email, description, null)
	}
	static User create( String account, String lastName, String firstName, 
						String lastNameKana, String firstNameKana,	
						String password, String email, String description, Role role
			) {
		return create([
		   				account:account,
						lastName:lastName,
						firstName:firstName,
						lastNameKana:lastNameKana,
						firstNameKana:firstNameKana,						
						password:password,
						email:email,
						description:description		                   
						], role)
	}
	static User create(params, Role role) {
		def user = new User (params)
		role?.addToUsers(user)
		return user
	}
	static User create(params) {
		return create(params, null)
	}	
	static String encodePassword(String password) {
		return StringUtils.toMD5(password)
	}
	/////////////////////////////////////////////
	private int _countOfOrganizations(boolean includesInactive, Boolean includesAsAdmin) {
		def qs = _getOrganizationsQS(includesInactive, includesAsAdmin)
		Integer[] counts = SpaceCardModel.executeQuery(
				"select count(o) from Organization as o ${qs}", [user:this]
			)
		return counts[0].intValue()		
	}
	private Collection _getOrganizations(boolean includesInactive, Boolean includesAsAdmin, paginateParams ) {
		def qs = _getOrganizationsQS(includesInactive, includesAsAdmin)
		Map sParams = PaginateUtils.makeSelectParams(paginateParams, [user:this])
		return Organization.selectDistinct(
			"${qs} ${sParams.orderByClause}",
			sParams.args)
	}
	private String _getOrganizationsQS(boolean includesInactive, Boolean includesAsAdmin) {
		def includesInactiveStatement = ""
		if(!includesInactive) {
			includesInactiveStatement = "and o.active=true"
		}
		def	includesAsAdminStatement = ""
		if(includesAsAdmin != null) {
			if(!includesAsAdmin.booleanValue()) 
				includesAsAdminStatement = "and r.authority!='${AuthorityType.ADMIN}'"
		} else {
			includesAsAdminStatement = "and r.authority='${AuthorityType.ADMIN}'"
		}
		return "join o.roles as r join r.users as u where u=:user ${includesInactiveStatement} ${includesAsAdminStatement}"
	}	
	/////////////////////////////////////////////
	def addToRoles(Role obj) {
		return CollectionBugEvasion.addToForBidirectionalManyToMany(
					this, Set.class, obj, Role.class, "roles", "users"  
				)	
	}
	def removeFromRoles(Role obj) {
		return CollectionBugEvasion.removeFromForBidirectionalManyToMany(
					this, obj, "roles", "users"  
				)		
	}
	def addToDataTrays(UserDataTray obj) {
		return CollectionBugEvasion.addToForBidirectionalManyToOne(
					this, Set.class, obj, UserDataTray.class, "dataTrays", "user"  
				)	
	}
	def removeFromDataTrays(UserDataTray obj) {
		return CollectionBugEvasion.removeFromForBidirectionalManyToOne(
					this, obj, "dataTrays", "user"  
				)		
	}
	def addToCardHolders(UserCardHolder obj) {
		return CollectionBugEvasion.addToForBidirectionalManyToOne(
					this, Set.class, obj, UserCardHolder.class, "cardHolders", "user"  
				)	
	}
	def removeFromCardHolders(UserCardHolder obj) {
		return CollectionBugEvasion.removeFromForBidirectionalManyToOne(
					this, obj, "cardHolders", "user"  
				)		
	}
	def addToWorkspaces(UserWorkspace obj) {
		return CollectionBugEvasion.addToForBidirectionalManyToOne(
					this, Set.class, obj, UserWorkspace.class, "workspaces", "user"  
				)	
	}
	def removeFromWorkspaces(UserWorkspace obj) {
		return CollectionBugEvasion.removeFromForBidirectionalManyToOne(
					this, obj, "workspaces", "user"
				)		
	}	
	/////////////////////////////////////////////
	def beforeInsert = {
		ModelUtils.insertLog(this)
		createdDate = new Date()
	}
	def beforeUpdate = {
		ModelUtils.updateLog(this)
	}
	def beforeDelete = {
		ModelUtils.deleteLog(this)
	}	
	/////////////////////////////////////////////
/*	memo:
 * 
 *	static mapping     = {
 *		table "`user`" // for postgresql
 *	}
 *	上の技は、ManyToManyだと使えないようす。エラーが出ます。
 *  Batch entry 0 insert into role_`user` (users_id, roles_id) values (4, 5) was aborted.  Call getNextException to see the cause.	
 *
 *  テーブル名を変えても不可解な現象発生（Person参照）
 */
 	static transients  = ['assignedWorkCards',
 	                      'assignsByUser',
 	                      'supervisor',
 	                     ]
	static belongsTo   = [Role]
	static hasMany     = [
	                      roles:Role, 
	                      dataTrays:UserDataTray,
	                      cardHolders:UserCardHolder,
	                      workspaces:UserWorkspace,	                      
	                      ]
	static constraints = {
		account(blank:false,unique:true)
		lastName(blank:false)
		firstName(blank:false)
		lastNameKana(blank:false)
		firstNameKana(blank:false)		
		password(blank:false)
		active()
		email(blank:false,unique:true)
		description(nullable:true)
		createdDate(nullable:true)
		roles()
	}
	/////////////////////////////////////////////
	String account
	String lastName
	String firstName
	String lastNameKana
	String firstNameKana	
	String password
	boolean active = true
	String email
	String description
	Date createdDate
}