package org.maachang.session.client.local ;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

import org.maachang.session.client.PSession;
import org.maachang.session.client.net.protocol.ClientProtocol;
import org.maachang.session.engine.PersistenceDefine;
import org.maachang.session.engine.PersistenceEngine;
import org.maachang.util.ArrayBinary;
import org.maachang.util.SerializableUtil;

/**
 * クライアント永続化セッションローカル.
 * 
 * @version 2008/05/25
 * @author masahito suzuki
 * @since PersistenceSession 1.00
 */
class PSessionLocalImpl implements PSession {
    
    /**
     * セッションID.
     */
    private String sessionId = null ;
    
    /**
     * 永続化セッションエンジン.
     */
    private PersistenceEngine engine = null ;
    
    /**
     * コンストラクタ.
     */
    private PSessionLocalImpl() {
        
    }
    
    /**
     * コンストラクタ.
     * @param sessionId 対象のセッションIDを設定します.
     * @param engine 対象の永続化セッションエンジンを設定します.
     * @exception Exception 例外.
     */
    public PSessionLocalImpl( String sessionId,PersistenceEngine engine )
        throws Exception {
        if( sessionId == null ||
            ( sessionId = sessionId.trim() ).length() != PersistenceDefine.SESSION_KEY_LENGTH ||
            engine == null ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        this.sessionId = sessionId ;
        this.engine = engine ;
    }
    
    /**
     * 指定要素を追加.
     * @param key 対象のKey名を設定します.
     * @param value 対象の要素を設定します.
     * @exception Exception 例外.
     */
    public void put( String key,Object value ) throws Exception {
        if( key == null || ( key = key.trim() ).length() <= 0 ) {
            throw new IllegalArgumentException( "指定された要素キーは不正です" ) ;
        }
        if( value == null ) {
            throw new IllegalArgumentException( "指定された要素は不正です" ) ;
        }
        else if( value instanceof String && ( ( String )value ).length() <= 0 ) {
            throw new IllegalArgumentException( "指定された要素は不正です" ) ;
        }
        else if( value instanceof byte[] && ( ( byte[] )value ).length <= 0 ) {
            throw new IllegalArgumentException( "指定された要素は不正です" ) ;
        }
        else if( ( value instanceof Serializable ) == false ||
            value instanceof Map || value instanceof List ) {
            throw new IllegalArgumentException( "指定された要素はシリアライズ可能な形式ではありません" ) ;
        }
        ArrayBinary b = new ArrayBinary() ;
        byte[] valBin = null ;
        if( value instanceof String ) {
            valBin = ( ( String )value ).getBytes( "UTF8" ) ;
            b.write( new byte[]{ ( byte )ClientProtocol.VALUE_TYPE_STRING } ) ;
            b.write( valBin ) ;
        }
        else if( value instanceof byte[] ) {
            b.write( new byte[]{ ( byte )ClientProtocol.VALUE_TYPE_BINARY } ) ;
            b.write( ( byte[] )value ) ;
        }
        else {
            valBin = SerializableUtil.toBinary( ( Serializable )value ) ;
            b.write( new byte[]{ ( byte )ClientProtocol.VALUE_TYPE_OBJECT } ) ;
            b.write( valBin ) ;
        }
        engine.put( sessionId,key,b.getBinary() ) ;
    }
    
    /**
     * 指定要素を削除.
     * @param key 対象のKey名を設定します.
     * @exception Exception 例外.
     */
    public void remove( String key ) throws Exception {
        if( key == null || ( key = key.trim() ).length() <= 0 ) {
            throw new IllegalArgumentException( "指定された要素キーは不正です" ) ;
        }
        engine.remove( sessionId,key ) ;
    }
    
    /**
     * 指定要素を取得.
     * @param key 対象のKey名を設定します.
     * @return Object 対象の要素が返されます.
     * @exception Exception 例外.
     */
    public Object get( String key ) throws Exception {
        if( key == null || ( key = key.trim() ).length() <= 0 ) {
            throw new IllegalArgumentException( "指定された要素キーは不正です" ) ;
        }
        byte[] bin = engine.get( sessionId,key ) ;
        if( bin == null ) {
            return null ;
        }
        Object ret = null ;
        int type = ( int )( bin[ 0 ] & 0x000000ff ) ;
        int len = bin.length-1 ;
        byte[] b = new byte[ len ] ;
        System.arraycopy( bin,1,b,0,len ) ;
        bin = null ;
        if( type == ClientProtocol.VALUE_TYPE_STRING ) {
            ret = new String( b,"UTF8" ) ;
        }
        else if( type == ClientProtocol.VALUE_TYPE_BINARY ) {
            ret = b ;
        }
        else if( type == ClientProtocol.VALUE_TYPE_OBJECT ) {
            ret = SerializableUtil.toObject( b ) ;
        }
        return ret ;
    }
    
    /**
     * セッションIDを取得.
     * @return String セッションIDが返されます.
     * @exception Exception 例外.
     */
    public String getSessionId() throws Exception {
        return sessionId ;
    }
    
    /**
     * 現在のセッション更新時間を取得.
     * @return long セッション更新時間が返されます.
     * @exception Exception 例外.
     */
    public long getUpdateTime() throws Exception {
        return engine.getLastUpdateTime( sessionId ) ;
    }
    
}

