/*

    @author DanielD

    ========== licence begin GPL
    Copyright (C) 2002-2003 SAP AG

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    ========== licence end


*/

package com.sap.dbtech.procserver;

import com.sap.dbtech.util.*;
import com.sap.dbtech.jdbc.packet.*;
import com.sap.dbtech.vsp00.*;
import com.sap.dbtech.vsp001.*;

/**
 *
 */
public class ProcServerSession
    extends Thread
{
    private com.sap.dbtech.rte.comm.NativeComm comm;
    private ProcServerConnection conn;
    private CallableFactory      callFactory;
    /**
     * creates a new ProcServerSession
     */
    public 
    ProcServerSession (String sessionId)
    throws ProcServerSession.Exception
    {
        ParsedSessionId parsedId = new ParsedSessionId (sessionId);
        try {
            this.comm = new com.sap.dbtech.rte.comm.NativeComm ("", sessionId);
        }
        catch (com.sap.dbtech.rte.comm.RTEException rteExc) {
            throw new ProcServerSession.Exception (rteExc.getMessage ());
        }
        try {
            this.conn = new ProcServerConnection (this.comm);
        }
        catch (java.sql.SQLException sqlExc) {
            throw new ProcServerSession.Exception (sqlExc.getMessage ());
        }
        this.callFactory = new SQLJCallable.SQLJCallableFactory ();
    }
    /**
     *
     */
    public void 
    run ()
    {
        try {
            /*
             * send initial package to trigger server process
             */
            StructuredMem requestPacket = this.comm.getRequestPacket ();
            requestPacket.putString("", 0);
            this.comm.request (requestPacket, 0);
            /*
             * loop until 'end' request
             */
            while (true) {
                ProcReplyPacket procReply;
                StructuredBytes [] rawPacket = new StructuredBytes [1];
                /*
                 * recv next request
                 */
                StructuredMem request = this.comm.receive ();
                /*
                 * check for empty packet (signalling end of loop)
                 */
                if (request.size () == 0) {
                    break;
                }
                /*
                 * swap memory if necessary
                 */
                boolean needsSwapping = false;
                if (request.getInt1 (Packet.MessSwap_O) == Vsp00Const.fullSwap_C) {
                    StructuredBytes assertBytes = (StructuredBytes) request;
                    request = new FullswapMem (assertBytes.bytes ());
                    needsSwapping = true;
                }
                /*
                 * 
                 */
                try {
                    ReplyPacket procCall = new ReplyPacket (request);
                    /*
                     * find callable
                     */
                    String [] idTuple = this.parseProcidPart (procCall);
                    String idString = idTuple [0];
                    String date = idTuple [1];
                    Callable call = this.callFactory.create (idString, date);
                    /*
                     * execute proc
                     */
                    ProcParameterList parameterList = new ProcParameterList (procCall);
                    call.call (parameterList);
                    /*
                     * build reply packet with output parameters
                     */
                    procReply = this.initReply (needsSwapping, rawPacket);
                    if (parameterList.hasOutputParameter ()) {
                        DataPart dataPart = procReply.newDataPart (PartKind.Data_C);
                        parameterList.copyToOutputPacket (dataPart);
                        dataPart.close ();
                    }
                }
                catch (Stop stop) {
                    Log.log ("STOP: [" + stop.getErrorCode () + "] " + stop.getMessage ());
                    /*
                     * build reply packet with stop information
                     */
                    procReply = this.initReply (needsSwapping, rawPacket);
                    procReply.setStopInfo (stop.getErrorCode (), stop.getMessage ());
                }
                catch (Throwable callExc) {
                    Log.traceException (callExc);
                    /*
                     * build reply packet with exception information
                     */
                    procReply = this.initReply (needsSwapping, rawPacket);
                    procReply.setExceptionInfo (callExc);
                }
                procReply.closePacket ();
                procReply.traceOn (System.out);
                this.comm.request (procReply, procReply.length ());
            }
            
        }
        catch (Throwable exc) {
            Log.traceException (exc);
        }
        
    }
    /**
     * 
     * @param procCall
     * 
     * @return tuple (idString, dateString)
     * 
     * @exception PartNotFound
     */
    protected String [] 
    parseProcidPart (
        ReplyPacket procCall)
    throws PartNotFound
    {
        String [] result = new String [2];
        procCall.findPart (PartKind.Procid_C);
        int readPos = procCall.getPartDataPos ();
        for (int i = 0; i < 2; ++i) {
            int len = procCall.getUInt1 (readPos) * 256 
                    + procCall.getUInt1 (readPos + 1);
            String value = procCall.getString (readPos + 2, len);
            result [i] = value;
            readPos += 2 + len;
        }
        return result;
    }
    /**
     *
     */
    protected ProcReplyPacket 
    initReply (
        boolean needsSwapping,
        StructuredBytes [] rawPacket)
    {
        StructuredMem requestPacket = this.comm.getRequestPacket ();
        rawPacket [0] = (StructuredBytes) requestPacket;
        if (needsSwapping) {
            StructuredBytes requestBytes = (StructuredBytes) requestPacket;
            requestPacket = new FullswapMem (requestBytes.bytes ());
        }
        ProcReplyPacket result = new ProcReplyPacket (requestPacket);
        return result;
    }
    /**
     *
     */
    public class Exception
        extends Throwable
    {
        /**
         * creates a new Exception
         */
        public 
        Exception (
            String msg)
        {
            super (msg);
        }
    }
    /**
     *
     */
    private static class ParsedSessionId
    {
        public int port;
        public int packetSize;
        /**
         * creates a new ParsedSessionId
         */
        public 
        ParsedSessionId (
            String idString)
        {
            // $:<port>:<packetSize>
            int colonPos = idString.indexOf (':', 2);
            this.port = Integer.parseInt (idString.substring (2, colonPos));
            this.packetSize = Integer.parseInt (idString.substring (colonPos + 1));
        }
    }
}
