package com.simpleteam.api;

import java.io.*;
import java.net.*;

import com.simpleteam.*;
import com.simpleteam.log.*;
import com.simpleteam.tag.*;

import com.simpleteam.connection.*;
import com.simpleteam.adapter.stream.*;

import com.simpleteam.packet.*;
import com.simpleteam.packet.smpp.*;

import com.simpleteam.transactor.authenticate.*;
import com.simpleteam.connection.protocol.smpp.*;

/**
 * <p>Title: SimpleTeam</p>
 * <p>Description: SMPP API</p>
 * <p>Copyright: All rights reserved by www.simpleteam.com. Copyright (c) 2000 ~ 2009</p>
 * <p>Company: www.simpleteam.com</p>
 * @author Forest Luo
 * @version 3.0
 */

public class SMPPAPI
{
    //Class Name
    private final static String CLASS_NAME                  = "SMPPAPI";

    /**
     * Whether authenticated.
     */
    private boolean authenticated;
    /**
     * Simple sequence.
     */
    private SimpleSequence sequence;

    /**
     * Connection.
     */
    private SMPPConnection connection;
    /**
     * Authenticate.
     */
    private SMPPAuthenticate authenticate;

    /**
     * Default construction.
     *
     * @param systemType System type.
     * @param serviceCode Service code.
     * @param account Account of user.
     * @param password Password of user.
     * @return
     *     <p>No results returned.</p>
     */
    public SMPPAPI(String systemType,String serviceCode,
                   String account,String password)
    {
        //Create sequence.
        sequence = new SimpleSequence();
        //Create connection.
        connection = new SMPPConnection();

        //Create node.
        TagNode node = new TagNode("authenticate");
        //Add node.
        node.addNode("account",account);
        //Add node.
        node.addNode("password",password);
        //Add node.
        node.addNode("system_type",systemType);
        //Add node.
        node.addNode("service_code",serviceCode);
        //Create authenticate.
        authenticate = new SMPPAuthenticate(node);
    }

    /**
     * Clear.
     *
     * @param None No parameters needed.
     * @return
     *     <p>No results returned.</p>
     */
    public void finalize()
    {
        //Clear sequence.
        sequence = null;
        //Clear connection.
        connection = null;
        //Clear authenticate.
        authenticate = null;
    }

    /**
     * Get next sequence.
     *
     * @param None No parameters needed.
     * @return
     *     <p>Next sequence.</p>
     */
    public int nextSequence()
    {
        //Return result.
        return sequence != null ? sequence.nextInteger() : 0;
    }

    /**
     * Whether there is available.
     *
     * @param None No parameters needed.
     * @return
     *     <p>Return true, if packet available.</p>
     */
    public synchronized boolean available()
        throws IOException
    {
        try
        {
            //Return result.
            return connection != null ? connection.available() : false;
        }
        catch(BufferException e)
        {
            throw new IOException(e.getMessage());
        }
        catch(ConnectionException e)
        {
            throw new IOException(e.getMessage());
        }
    }

    /**
     * Read packet.
     *
     * @param None No parameters needed.
     * @return
     *     <p>Read packet.</p>
     */
    public synchronized SMPPPacket readPacket()
        throws IOException
    {
        try
        {
            //Return result.
            return connection != null ? (SMPPPacket)connection.readPacket() : null;
        }
        catch(BufferException e)
        {
            throw new IOException(e.getMessage());
        }
        catch(ConnectionException e)
        {
            throw new IOException(e.getMessage());
        }
    }

    /**
     * Write packet.
     *
     * @param packet Packet for writing.
     * @return
     *     <p>No results returned.</p>
     */
    public synchronized void writePacket(SMPPPacket packet)
        throws IOException
    {
        try
        {
            //Return result.
            if(connection != null) connection.writePacket(packet);
        }
        catch(BufferException e)
        {
            throw new IOException(e.getMessage());
        }
        catch(ConnectionException e)
        {
            throw new IOException(e.getMessage());
        }
    }

    /**
     * Login session.
     *
     * <p>It depends on session type and gateway type. It must be overrided.</p>
     *
     * @param None No parameters needed.
     * @return
     *     <p>Return true, if logined successfully.</p>
     */
    private boolean login()
    {
        //Bind.
        SMPPBind bind = new SMPPBindTransceiver(0);
        //Set sequence.
        bind.sequence = 1;
        //Set system_id.
        bind.system_id = authenticate.getAccount();
        //Set password.
        bind.password = authenticate.getPassword();
        //Set system_type.
        bind.system_type = authenticate.getType();
        //Set interface_version.
        bind.interface_version = authenticate.getVersion();
        //Check result.
        if(bind.esme != null)
        {
            //Set ton and npi.
            bind.esme.ton = 0;bind.esme.npi = 0;
            //Set address.
            bind.esme.address = authenticate.getAdderss();
        }
        //Check validation.
        if(!bind.isValid())
        {
            //Log event.
            if(LogRequests.isRequested(EventID.SESSION | EventID.EXCEPTION))
            SimpleLog.log(CLASS_NAME,"login").
                message("invalid SMPP bind !").end();
            return false;
        }

        try
        {
            //Write packet.
            connection.writePacket(bind);
            //Read packet.
            SMPPPacket smpp = (SMPPPacket)connection.readPacket();
            //Check result.
            if(smpp == null)
            {
                //Log event.
                if(LogRequests.isRequested(EventID.SESSION | EventID.EXCEPTION))
                SimpleLog.log(CLASS_NAME,"login").
                    message("fail to read packet !").end();
                return false;
            }
            //Check command
            if(smpp.command != (bind.command | SMPPCommandID.RESPONSE_MASK))
            {
                //Log event.
                if(LogRequests.isRequested(EventID.SESSION | EventID.EXCEPTION))
                SimpleLog.log(CLASS_NAME,"login").
                    message("invalid bind response packet !").end();
                return false;
            }
            //Check sequence.
            if(smpp.sequence != bind.sequence)
            {
                //Log event.
                if(LogRequests.isRequested(EventID.SESSION | EventID.EXCEPTION))
                SimpleLog.log(CLASS_NAME,"login").
                    message("sequence is not according to previous !").end();
                return false;
            }
            //Get bind response.
            SMPPBindResponse response = (SMPPBindResponse)smpp;
            //Check result.
            if(response.error != SMPPCommandStatus.ESME_ROK)
            {
                //Log event.
                if(LogRequests.isRequested(EventID.SESSION | EventID.EXCEPTION))
                SimpleLog.log(CLASS_NAME,"login").begin().
                    append(SMPPCommandStatus.toString(response.error)).append(" !").end();
                return false;
            }
            //Set authenticated.
            authenticated = true;
            //Return true.
            return true;
        }
        catch(Exception e)
        {
            //Log event.
            if(LogRequests.isRequested(EventID.SESSION | EventID.EXCEPTION))
            SimpleLog.log(CLASS_NAME,"login").exception(e,false).end();
        }
        //Return false.
        return false;
    }

    /**
     * Connect.
     *
     * @param remoteHost Address of remote host.
     * @param remotePort Port of remote host.
     * @return
     *     <p>Return true, if successfully done.</p>
     */
    public boolean connect(String remoteHost,int remotePort)
    {
        try
        {
            //Socket.
            Socket socket = new Socket(InetAddress.getByName(remoteHost),remotePort);
            //Create socket adapter.
            SocketAdapter adapter = new SocketAdapter(socket);
            //Set adapter timeout.
            adapter.setTimeout(5 * TimeConstant.SECOND);
            //Open.
            connection.open(adapter);
            //Return login.
            return login();
        }
        catch(Exception e)
        {
            //Log event.
            if(LogRequests.isRequested(EventID.SMPP_PACKET | EventID.EXCEPTION))
            SimpleLog.log(CLASS_NAME,"connect").exception(e,false).end();
        }
        //Return false.
        return false;
    }

    /**
     * Logout session.
     *
     * <p>It depends on session type and gateway type. It must be overrided.</p>
     *
     * @param None No parameters needed.
     * @return
     *     <p>Return true, if logouted successfully.</p>
     */
    private boolean logout()
    {
        //Create unbind.
        SMPPUnbind unbind = new SMPPUnbind();
        //Set sequence.
        unbind.sequence = nextSequence();

        try
        {
            //Write packet.
            connection.writePacket(unbind);
            //Read packet.
            SMPPPacket smpp = (SMPPPacket)connection.readPacket();
            //Check result.
            if(smpp == null)
            {
                //Log event.
                if(LogRequests.isRequested(EventID.SESSION | EventID.EXCEPTION))
                SimpleLog.log(CLASS_NAME,"logout").
                    message("fail to read packet !").end();
                return false;
            }
            //Check command.
            if(smpp.command == SMPPCommandID.UNBIND)
            {
                //Log event.
                if(LogRequests.isRequested(EventID.SESSION | EventID.INFORMATION))
                SimpleLog.log(CLASS_NAME,"logout").
                    message("unbind request was received !").end();

                //Create response.
                SMPPUnbindResponse response = new SMPPUnbindResponse(smpp.sequence);
                //Set status.
                response.error = SMPPCommandStatus.ESME_ROK;
                //Write response.
                connection.writePacket(response);
            }
            else if(smpp.command == SMPPCommandID.UNBIND_RESPONSE)
            {
                //Log event.
                if(LogRequests.isRequested(EventID.SESSION | EventID.INFORMATION))
                SimpleLog.log(CLASS_NAME,"logout").
                    message("unbind response was received !").end();

                //Check result.
                if(smpp.error != SMPPCommandStatus.ESME_ROK)
                {
                    //Log event.
                    if(LogRequests.isRequested(EventID.SESSION | EventID.WARNING))
                    SimpleLog.log(CLASS_NAME,"logout").begin().
                        append(SMPPCommandStatus.toString(smpp.error)).append(" !").end();
                }
            }
            else
            {
                //Log event.
                if(LogRequests.isRequested(EventID.SESSION | EventID.EXCEPTION))
                SimpleLog.log(CLASS_NAME,"logout").
                    message("invalid unbind response packet !").end();
                return false;
            }
            //Set authenticated.
            authenticated = false;
            //Return true.
            return true;
        }
        catch(Exception e)
        {
            //Log event.
            if(LogRequests.isRequested(EventID.SESSION | EventID.EXCEPTION))
            SimpleLog.log(CLASS_NAME,"logout").exception(e,false).end();
        }
        //Return false.
        return false;
    }

    /**
     * Close.
     *
     * @param None No parameters needed.
     * @return
     *     <p>No results returned.</p>
     */
    public void close()
    {
        //Check status.
        if(!connection.isClosed())
        {
            //Check terminated.
            if(!connection.isTerminated())
            {
                //Check authenticated.
                if(!authenticated)
                {
                    /*
                    //Log event.
                    if(LogRequests.isRequested(EventID.SMPP_PACKET | EventID.INFORMATION))
                    SimpleLog.log(CLASS_NAME,"close").
                        message("session is not authenticated !").end();
                    */
                }
                //Logout session.
                else if(!logout())
                {
                    /*
                    //Log event.
                    if(LogRequests.isRequested(EventID.SMPP_PACKET | EventID.INFORMATION))
                    SimpleLog.log(CLASS_NAME,"close").
                        message("fail to logout !").end();
                    */
                }
            }

            try
            {
                //Close connection.
                connection.close();
            }
            catch(Exception e)
            {
                //Log event.
                if(LogRequests.isRequested(EventID.SMPP_PACKET | EventID.EXCEPTION))
                SimpleLog.log(CLASS_NAME,"close").exception(e,false).end();
            }
            //Log event.
            if(LogRequests.isRequested(EventID.SMPP_PACKET | EventID.INFORMATION))
            SimpleLog.log(CLASS_NAME,"close").
                message("connection was closed !").end();
        }
        else
        {
            //Log event.
            if(LogRequests.isRequested(EventID.SMPP_PACKET | EventID.INFORMATION))
            SimpleLog.log(CLASS_NAME,"close").
                message("connection has already been closed !").end();
        }
    }

    /**
     * Main.
     *
     * <p>It is better to sperate submit and deliver processing,
     * although this example give a mixed processing.</p>
     *
     * @param args Arguments.
     * @return
     *     <p>No results returned.</p>
     */
    public static void main(String[] args)
    {
        //Account.
        String account = "333";
        //Password.
        String password = "0555";
        //Service code.
        String serviceCode = "9999";
        //Enterprise code.
        String systemType = "logica";

        //Get current directory.
        String userDirectory = System.getProperty("user.dir");
        //Create work directory.
        File workDirectory = new File(userDirectory);
        //Create directory.
        File logDirectory = new File(userDirectory,"logs");
        //Check result.
        if(!logDirectory.exists() && !logDirectory.mkdir())
        {
            System.out.println("SMPPAPI.main : fail to create log directory !");
            return;
        }
        //Log mode.
        int logMode = LogMode.LOG_ALL;
        //Check args.
        if(args.length > 1)
        {
            //Check value.
            logMode = Decimal.parseInteger(args[1]);
            //Print.
            System.out.println("SMPPAPI.main : log mode(0x" + Hex.toString(logMode) + ") !");
        }
        //Initialize log.
        if(!SimpleLog.initialize(logDirectory,logMode))
        {
            System.out.println("SMPPAPI.main : fail to initialize log !");
            return;
        }

        try
        {
            //Create API.
            SMPPAPI smppApi = new SMPPAPI(systemType,serviceCode,account,password);
            //Connect.
            if(smppApi.connect("127.0.0.1",7890))
            {
                //Print.
                System.out.println("SMPPAPI.main : connection was built !");

                //Count.
                int totalCount = 1,sendCount = 0,reportCount = 0;
                //Do while.
                while(reportCount < totalCount)
                {
                    //Check send count.
                    if(sendCount < totalCount)
                    {
                        //Create submit.
                        SMPPSubmitSM submit = new SMPPSubmitSM(smppApi.nextSequence());

                        //Service Type
                        submit.service_type = "HELP";
                        //Protocol ID
                        submit.protocol_id = 0;
                        //Message Mode
                        submit.registered_delivery = 1;
                        //Validity Period
                        submit.validity_period = null;
                        //Schedule
                        submit.schedule_delivery_time = null;
                        //Source Address
                        submit.source = new SMPPSMEAddress(serviceCode);
                        //Destination address
                        submit.destination = new SMPPSMEAddress("13501026991");
                        //Data Coding
                        submit.data_coding = 0;
                        //Message Content
                        submit.short_message = "Hello World !".getBytes("US-ASCII");
                        //Message Length
                        submit.sm_length = submit.short_message.length;

                        //Write packet.
                        smppApi.writePacket(submit);
                        //Add send count and clear flag.
                        sendCount ++;
                    }

                    //Check available.
                    if(smppApi.available())
                    {
                        //Read packet.
                        SMPPPacket packet = smppApi.readPacket();
                        //Check instance.
                        if(packet instanceof SMPPDeliverSM)
                        {
                            //Get deliver.
                            SMPPDeliverSM deliver = (SMPPDeliverSM)packet;
                            //Create deliver response.
                            SMPPDeliverSMResponse response = new SMPPDeliverSMResponse(packet.sequence);
                            //Write response.
                            smppApi.writePacket(response);//Do response as soon as possible.

                            //Add report count.
                            reportCount ++;
                            ///////////////////////////////////////////////////////////////////////////
                            //
                            // Do process of deliver.
                            //
                            // <p>You can add your own processing here !</p>
                            //
                            //Check message_mode.
                            if((deliver.registered_delivery & 0x0f) == 1)
                            {
                                //Print.
                                System.out.println("SMPPAPI.main : report/status(" + deliver.stat + ")");
                            }
                            else
                            {
                                //Print.
                                System.out.println("SMPPAPI.main : normal/source(" + (deliver.source != null ? deliver.source.address : null) + ")");
                            }
                            //
                            ///////////////////////////////////////////////////////////////////////////
                        }
                        //Check instance.
                        else if(packet instanceof SMPPEnquireLink)
                        {
                            //Create activetest response.
                            SMPPEnquireLinkResponse response = new SMPPEnquireLinkResponse(packet.sequence);
                            //Write response.
                            smppApi.writePacket(response);
                        }
                        //Check command.
                        else if(packet instanceof SMPPSubmitSMResponse)
                        {
                            //Create submit response.
                            SMPPSubmitSMResponse response = (SMPPSubmitSMResponse)packet;
                            //Print.
                            System.out.println("SMPPAPI.main : submit/result(" + response.message_id + ")");

                            ///////////////////////////////////////////////////////////////////////////
                            //
                            // Do process of submit response.
                            //
                            // <p>You can add your own processing here !</p>
                            //
                            //
                            ///////////////////////////////////////////////////////////////////////////
                        }
                    }
                }
            }
            else
            {
                //Print.
                System.out.println("SMPPAPI.main : fail to connect !");
            }
            //Close.
            smppApi.close();
            //Print.
            System.out.println("SMPPAPI.main : connection was closed !");
        }
        catch(Exception e)
        {
            //Print.
            System.out.println("SMPPAPI.main : " + e.getMessage());
            System.out.println("SMPPAPI.main : unexpected exit !");
        }

        //Uninitialize log.
        SimpleLog.uninitialize();

        //Exit
        System.exit(0);
    }
}

