// ========================================================================
// $Id: LocalState.java,v 1.4 2004/05/09 20:30:47 gregwilkins Exp $
// Copyright 2002-2004 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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 org.mortbay.j2ee.session;

//----------------------------------------

import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import org.jboss.logging.Logger;

//----------------------------------------

/**
 * Hold the state of an HttpSession
 * 
 * @author <a href="mailto:jules@mortbay.com">Jules Gosnell</a>
 * @version 1.0
 */
public class LocalState implements State, java.io.Serializable
{
    protected static final Logger _log = Logger.getLogger(LocalState.class);

    protected String _id;

    protected int _maxInactiveInterval;

    protected int _actualMaxInactiveInterval;

    protected long _creationTime;

    protected long _lastAccessedTime;

    protected Map _attributes; // allocated lazily

    public LocalState(String id, long creationTime, int maxInactiveInterval,
            int actualMaxInactiveInterval)
    {
        _id = id;
        _creationTime = creationTime;
        _lastAccessedTime = _creationTime;
        _maxInactiveInterval = maxInactiveInterval;
        _actualMaxInactiveInterval = actualMaxInactiveInterval;
    }

    public LocalState(String id, int maxInactiveInterval,
            int actualMaxInactiveInterval)
    {
        _id = id;
        _creationTime = System.currentTimeMillis();
        _lastAccessedTime = _creationTime;
        _maxInactiveInterval = maxInactiveInterval;
        _actualMaxInactiveInterval = actualMaxInactiveInterval;
    }

    protected LocalState()
    {
        // for deserialisation only
    }

    public String getId()
    {
        return _id;
    }

    public long getCreationTime()
    {
        return _creationTime;
    }

    public int getActualMaxInactiveInterval()
    {
        return _actualMaxInactiveInterval;
    }

    public long getLastAccessedTime()
    {
        return _lastAccessedTime;
    }

    public void setLastAccessedTime(long time)
    {
        _lastAccessedTime = time;
    }

    public int getMaxInactiveInterval()
    {
        return _maxInactiveInterval;
    }

    public void setMaxInactiveInterval(int interval)
    {
        _log.info("_maxInactiveInterval=" + interval);
        _maxInactiveInterval = interval;
    }

    // allocate attribute map lazily. This is more complex, but JSPs
    // seem to force allocation of sessions and then never put anything
    // in them! - so it is a worthwhile saving in speed and footprint...

    protected static Map _emptyMap = Collections.EMPTY_MAP;

    protected static Enumeration _emptyEnumeration = Collections
            .enumeration(Collections.EMPTY_LIST);

    protected static String[] _emptyStringArray = new String[0]; // could
                                                                    // this be
                                                                    // changed
                                                                    // by user ?

    protected void ensureAttributes()
    {
        if (_attributes == null) _attributes = new HashMap();
    }

    public Object getAttribute(String name)
    {
        // _log.info("getAttribute("+name+")");
        return _attributes == null ? null : _attributes.get(name);
    }

    public Map getAttributes()
    {
        return (_attributes == null || _attributes.size() == 0) ? _emptyMap : Collections
                .unmodifiableMap(_attributes);
    }

    public Enumeration getAttributeNameEnumeration()
    {
        return (_attributes == null || _attributes.size() == 0) ? _emptyEnumeration : Collections
                .enumeration(_attributes.keySet());
    }

    public String[] getAttributeNameStringArray()
    {
        return (_attributes == null || _attributes.size() == 0) ? _emptyStringArray : (String[]) _attributes
                .keySet().toArray(new String[_attributes.size()]);
    }

    public Object setAttribute(String name, Object value, boolean returnValue)
    {
        // we can be sure that name is non-null, because this will have
        // been checked in our adaptor...

        // _log.info("setAttribute("+name+", "+value+", "+returnValue+")");

        ensureAttributes();
        Object tmp = _attributes.put(name, value);
        return returnValue ? tmp : null;
    }

    public void setAttributes(Map attributes)
    {
        if (_attributes != null) _attributes.clear();

        if (attributes.size() > 0)
        {
            ensureAttributes();
            _attributes.putAll(attributes);
        }
    }

    public Object removeAttribute(String name, boolean returnValue)
    {
        if (_attributes == null)
            return null;
        else
        {
            Object tmp = _attributes.remove(name);
            return returnValue ? tmp : null;
        }
    }

    protected long remainingTime()
    {
        long maxInactiveInterval = _maxInactiveInterval < 1 ? _actualMaxInactiveInterval : _maxInactiveInterval;
        return (_lastAccessedTime + (maxInactiveInterval * 1000L))
                - System.currentTimeMillis();
    }

    public boolean isValid(int extraTime)
    {
        long remainingTime = remainingTime();
        long etime = (extraTime * 1000L);
        boolean valid = remainingTime + etime > 0;
        return valid;
    }

    public boolean isValid()
    {
        return isValid(0);
    }
}
