package org.bodington.server;

import java.text.DateFormat;
import java.text.ParseException;
import java.util.Date;

import org.apache.log4j.Logger;
import org.bodington.database.PrimaryKey;
import org.bodington.server.events.EasyEditEvent;
import org.bodington.server.events.Event;
import org.bodington.server.realm.Permission;
import org.bodington.server.realm.User;
import org.bodington.server.resources.Resource;
import org.bodington.text.Metadatum;

public class EasyEditSessionImpl extends BuildingSessionImpl implements EasyEditorSession {

	public static final String METADATUM_LOCK = "lock";
	
	private static Logger log = Logger.getLogger(EasyEditSessionImpl.class);
	
	/* (non-Javadoc)
	 * @see org.bodington.server.EasyEditorSession#lock()
	 */
	public boolean lock() throws BuildingServerException {
        if (!getResource().checkPermission(Permission.EDIT))
            throw new PermissionDeniedException(Permission.EDIT);
		Lock lock = getLock();
		User user = getUser();
		if (lock == null || !lock.isValid()) {
			lock = new Lock(user);
		} else if ( !lock.user.equals(user) ) {
			return false;
		}
		lock.update();
		Metadatum data = getMetadatum();
		if (data == null) {
			data = new Metadatum(getResourceId(), METADATUM_LOCK, null);
		}
		data.setValue(lock.toString());
		data.save();	
		return true;
	}
	
	/* (non-Javadoc)
	 * @see org.bodington.server.EasyEditorSession#breakLock()
	 */
	public boolean breakLock() throws BuildingServerException {
        if (!getResource().checkPermission(Permission.EDIT))
            throw new PermissionDeniedException(Permission.EDIT);
		try {
			Metadatum data = getMetadatum();
			if( data != null ) { 
				data.delete();
				return true;
			}
		} catch (BuildingServerException bse) {
			log.error("Failed to break lock.", bse);
		}
		return false;
	}
	
	/* (non-Javadoc)
	 * @see org.bodington.server.EasyEditorSession#getExpire()
	 */
	public Date getExpire() {
		Lock lock = getLock();
		return (lock == null || !lock.isValid())?null:lock.released;
	}
	
	/* (non-Javadoc)
	 * @see org.bodington.server.EasyEditorSession#getLocker()
	 */
	public User getLocker() {
		Lock lock = getLock();
		return (lock == null || !lock.isValid())?null:lock.user;
	}
    
    public boolean update(String text) throws BuildingServerException {
        if (lock()) {
            Resource resource = getResource();
            resource.setIntroduction(text);
            resource.save();
            
            Event event = new EasyEditEvent(resource, getUser(), EasyEditEvent.EDIT);
            event.save();
            breakLock();
            return true;
        }
        return false;
    }
	
	private Lock getLock() {
		try {
			Metadatum data = getMetadatum();
			if (data != null && data.getValue().length() > 0) {
				Lock lock = new Lock(data.getValue());
				return lock;
			}
		} catch (BuildingServerException e) {
			log.error("Failed to load lock data for: "+ getResourceId(), e);
		} catch (IllegalArgumentException iae) {
			log.error("Failed to parse data for :" + getResourceId());
		}
		return null;
	}

	private Metadatum getMetadatum() throws BuildingServerException {
		return Metadatum.findMetadatumByIdAndName(getResourceId(), METADATUM_LOCK);
	}

	class Lock {
		private static final int LOCK_TIME = 1000*60*15;
		Date created;
		Date released;
		User user;
		
		Lock(String data) throws IllegalArgumentException {
			String[] parts = data.split(";");
			try {
			created = DateFormat.getInstance().parse(parts[0]);
			released = DateFormat.getInstance().parse(parts[1]);
			user = User.findUser(new PrimaryKey(Integer.parseInt(parts[2])));
			} catch (ParseException pe) {
				throw new IllegalArgumentException("Failed to parse date.");
			} catch (NumberFormatException nfe) {
				throw new IllegalArgumentException("Failed to parse user id.");
			} catch (BuildingServerException bse) {
				throw new IllegalArgumentException("Failed to load user id.");
			}
		}
		
		Lock (User user) {
			this.user = user;
			this.created = new Date();
		}
		
		boolean isValid() {
			return new Date().before(released);
		}
		
		void update() {
			released = new Date(System.currentTimeMillis() + LOCK_TIME);
		}
				
		public String toString() {
			return
				DateFormat.getInstance().format(created) + ";" +
				DateFormat.getInstance().format(released) + ";" +
				user.getPrimaryKey();
		}
		
		
	}


}
