//
// $Id: BodyProvider.java,v 1.6 2003/09/11 03:20:06 mdb Exp $

package com.threerings.crowd.server;

import com.threerings.presents.data.ClientObject;
import com.threerings.presents.server.InvocationException;
import com.threerings.presents.server.InvocationManager;
import com.threerings.presents.server.InvocationProvider;

import com.threerings.crowd.Log;
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.data.OccupantInfo;
import com.threerings.crowd.data.PlaceObject;

/**
 * Provides the server-side side of the body-related invocation services.
 */
public class BodyProvider
    implements InvocationProvider
{
    /** Used by {@link #updateOccupantInfo}. */
    public static interface OccupantInfoOp
    {
        /**
         * Updates the supplied occupant info record, returning true if
         * changes were made and thus the object should be published anew
         * to the place object, false if no publish should be done.
         */
        public boolean update (OccupantInfo oinfo);
    }

    /**
     * Constructs and initializes a body provider instance which will be
     * used to handle all body-related invocation service requests.
     */
    public static void init (InvocationManager invmgr)
    {
        // register a provider instance
        invmgr.registerDispatcher(new BodyDispatcher(new BodyProvider()), true);
    }

    /**
     * Handles a request to set the idle state of a client to the
     * specified value.
     */
    public void setIdle (ClientObject caller, boolean idle)
        throws InvocationException
    {
        BodyObject bobj = (BodyObject)caller;

        // determine the body's proposed new status
        byte nstatus = (idle) ? OccupantInfo.IDLE : OccupantInfo.ACTIVE;

        // report NOOP attempts
        if (bobj.status == nstatus) {
            throw new InvocationException(
                (idle) ? "m.already_idle" : "m.already_active");
        }

        // update their status!
        Log.debug("Setting user idle state [user=" + bobj.username +
                  ", status=" + nstatus + "].");
        updateOccupantStatus(bobj, bobj.location, nstatus);
    }

    /**
     * Locates the specified body's occupant info in the specified
     * location, applies the supplied occuapnt info operation to it and
     * then broadcasts the updated info (assuming the occop returned true
     * indicating that an update was made).
     */
    public static void updateOccupantInfo (BodyObject body, int locationId,
                                           OccupantInfoOp occop)
    {
        PlaceManager pmgr = CrowdServer.plreg.getPlaceManager(locationId);
        if (pmgr == null) {
            return;
        }

        OccupantInfo info = pmgr.getOccupantInfo(body.getOid());
        if (info != null && occop.update(info)) {
            pmgr.updateOccupantInfo(info);
        }
    }

    /**
     * Updates the connection status for the given body object's occupant
     * info in the specified location.
     */
    public static void updateOccupantStatus (
        BodyObject body, int locationId, final byte status)
    {
        // no need to NOOP
        if (body.status != status) {
            // update the status in their body object
            body.setStatus(status);
            body.statusTime = System.currentTimeMillis();
        }

        updateOccupantInfo(body, locationId, new OccupantInfoOp() {
            public boolean update (OccupantInfo info) {
                if (info.status != status) {
                    info.status = status;
                    return true;
                } else {
                    return false;
                }
            }
        });
    }
}
