package com.sap.dbtech.rte.comm;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Properties;
import java.util.Vector;

import com.sap.dbtech.util.MessageKey;
import com.sap.dbtech.util.MessageTranslator;

/*

 @author D031096

 ========== 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


 */
public class NiCommunication extends BasicSocketComm {
    private final static String NiHandle_Class_C     = "com.sap.ni.NiHandle";
    private final static String NiRoute_Class_C      = "com.sap.ni.NiRoute";
    private final static String NiRouteEntry_Class_C = "com.sap.ni.NiRouteEntry";
    private final static String Ni_Class_C           = "com.sap.ni.NI";
    
    private static Class niRoute; 
    private static Class niHandle; 
    private static Class niRouteEntry; 
    private static Class ni; 

    {
        try {
            niRoute       = Class.forName(NiRoute_Class_C);
            niHandle      = Class.forName(NiHandle_Class_C);
            niRouteEntry  = Class.forName(NiRouteEntry_Class_C);
            ni            = Class.forName(Ni_Class_C);
        } catch (ClassNotFoundException ex) {
            throw new RTEException(
                    MessageTranslator.translate(MessageKey.ERROR_LOAD_NILIBRARY, ex.toString()),
                    -709);
        }
    }

    public final static JdbcCommFactory factory = new JdbcCommFactory() {

        public JdbcCommunication open(String host, String dbname,
                Properties properties) throws RTEException {
            NiCommunication sc = new NiCommunication(host, properties);
            sc.connectDB(dbname);
            return sc;

        }

        public JdbcCommunication xopen(String host, String db, String dbroot,
                String pgm, Properties properties) throws RTEException {
            NiCommunication sc = new NiCommunication(host, properties);
            sc.connectAdmin(db, dbroot, pgm);
            return sc;
        }

    };

    /**
     * @param hostPort
     * @param properties
     * @throws RTEException
     */
    
    public NiCommunication(String hostPort, Properties properties)
            throws RTEException {
        super(hostPort, properties);
        try {
            Constructor cst = NiCommunication.niRoute.getConstructor(new Class[]{String.class});
            Object niR = cst.newInstance(new Object[]{hostPort}); 
    	    /*patch port for last entry*/
            Vector routeEntries = (Vector)NiCommunication.niRoute.getField("route_entries").get(niR);  
            Object lastEntry = routeEntries.lastElement();
            String  curPort =  (String) lastEntry.getClass().getMethod("getPort",new Class[]{}).invoke(lastEntry,new Object[]{});
    	    if (curPort==null || curPort.equals("") || curPort.equals("3299")){
    	      this.host += "/S/"+this.getDefaultPort();  
    	    }
        } catch (Exception ioexc) {
            throw new RTEException(
                    MessageTranslator
                            .translate(
                                    MessageKey.ERROR_HOST_NICONNECT,
                                    this.host,
                                    ioexc.toString(),
                                    new Integer(
                                            RteC.CommunicationErrorCodeMap_C[RteC.SQLSTART_REQUIRED_C])),
                    RteC.CommunicationErrorCodeMap_C[RteC.SQLSTART_REQUIRED_C]);
        }
        this.openSocket();
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.sap.dbtech.rte.comm.BasicSocketComm#getNewCommunication()
     */
    protected BasicSocketComm getNewCommunication() throws RTEException {
        return new SocketComm(this.host, null);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.sap.dbtech.rte.comm.BasicSocketComm#openSocket()
     */
    protected void openSocket() throws RTEException {
        try {
            Object talkmode = NiCommunication.ni.getField("NI_TALK_NATIVE").get(null);
            Constructor cst = NiCommunication.niHandle.getConstructor(new Class[]{String.class, Integer.TYPE, Byte.TYPE});
            Object niHdl = cst.newInstance(new Object[]{this.host, new Integer(0), talkmode});
            this.socket = (java.net.Socket) NiCommunication.niHandle.getMethod("getSocket",new Class[]{}).invoke(niHdl,new Object[]{});  

            //            try {
            //                this.socket.setSoTimeout(this.socketTimeOut);
            //                this.socket.setTcpNoDelay(true);
            //                //this.socket.setReceiveBufferSize(36864);
            //                this.socket.setSendBufferSize(36864);
            //            } catch (SocketException socketEx) {
            //                // ignore, as it is harmless
            //            }
            this.instream = this.socket.getInputStream();
            this.outstream = this.socket.getOutputStream();
        } catch (UnknownHostException uhexc) {
            throw new RTEException(
                    MessageTranslator
                            .translate(
                                    MessageKey.ERROR_HOST_NICONNECT,
                                    this.host,
                                    uhexc.toString(),
                                    new Integer(
                                            RteC.CommunicationErrorCodeMap_C[RteC.SQLSERVER_OR_DB_UNKNOWN_C])),
                    RteC.CommunicationErrorCodeMap_C[RteC.SQLSERVER_OR_DB_UNKNOWN_C]);
        } catch (IOException ioexc) {
            throw new RTEException(
                    MessageTranslator
                            .translate(
                                    MessageKey.ERROR_HOST_NICONNECT,
                                    this.host,
                                    ioexc.toString(),
                                    new Integer(
                                            RteC.CommunicationErrorCodeMap_C[RteC.SQLSTART_REQUIRED_C])),
                    RteC.CommunicationErrorCodeMap_C[RteC.SQLSTART_REQUIRED_C]);

        } catch (Exception ioexc) {
            throw new RTEException(
                    MessageTranslator
                            .translate(
                                    MessageKey.ERROR_HOST_NICONNECT,
                                    this.host,
                                    ioexc.toString(),
                                    new Integer(
                                            RteC.CommunicationErrorCodeMap_C[RteC.SQLSTART_REQUIRED_C])),
                    RteC.CommunicationErrorCodeMap_C[RteC.SQLSTART_REQUIRED_C]);

        }
        try {
            // !!! DO NOT LINGER !!!
            // Modern OS linger in the background, if necessary.
            // Lingering causes the whole Java application to linger for that
            // time, if the
            // x_server hasn't read all data. And in case he hasn't our last
            // command was
            // a COMMIT/ROLLBACK WORK RELEASE, so no harm is done to the
            // database. The
            // only thing could be some nasty entry in some x_server log ...
            this.socket.setSoLinger(true, 15);
        } catch (SocketException soExc) {
            // ignore
        }

    }

    /*
     * (non-Javadoc)
     * 
     * @see com.sap.dbtech.rte.comm.BasicSocketComm#getDefaultPort()
     */
    protected int getDefaultPort() {
        return RteC.defaultNIPort_C;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.sap.dbtech.rte.comm.BasicSocketComm#socketMustClosedAfterInfoRequest()
     */
    protected boolean socketMustClosedAfterInfoRequest() {
        return false;
    }

}
