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.mpcp.*;

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

/**
 * <p>Title: SimpleTeam</p>
 * <p>Description: MPCP 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 MPCPAPI
{
    //Class Name
    private final static String CLASS_NAME                  = "MPCPAPI";

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

    /**
     * Connection.
     */
    private MPCPConnection connection;
    /**
     * Authenticate.
     */
    private MPCPAuthenticate authenticate;

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

        //Create node.
        TagNode node = new TagNode("authenticate");
        //Add node.
        node.addNode("account",account);
        //Add node.
        node.addNode("password",password);
        //Add node.
        node.addNode("service_code",serviceCode);
        //Add node.
        node.addNode("enterprise_code",enterpriseCode);
        //Create authenticate.
        authenticate = new MPCPAuthenticate(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 MPCPPacket readPacket()
        throws IOException
    {
        try
        {
            //Return result.
            return connection != null ? (MPCPPacket)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(MPCPPacket 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()
    {
        //Create connect.
        MPCPLogin connect = new MPCPLogin();
        //Set sequence.
        connect.sequence = 1;
        //Set timestamp.
        connect.timestamp = 0x3b47fcbd;
        //Set version.
        connect.version = 0x12;
        //Set source address.
        connect.source_addr = authenticate.getEnterpriseCode();
        //Get authenticate.
        connect.authenticator_sp = authenticate.getAuthenticate(connect.timestamp);
        //'02' present transceiver.
        connect.bind_type = MPCPBindType.BIND_TRANSCEIVER;
        //Check validation.
        if(!connect.isValid())
        {
            //Log event.
            if(LogRequests.isRequested(EventID.SESSION | EventID.EXCEPTION))
            SimpleLog.log(CLASS_NAME,"login").
                message("invalid MPCP connect !").end();
            return false;
        }

        try
        {
            //Write packet.
            connection.writePacket(connect);
            //Read packet.
            MPCPPacket smias = (MPCPPacket)connection.readPacket();
            //Check result.
            if(smias == 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(smias.command != MPCPCommandID.LOGIN_RESPONSE)
            {
                //Log event.
                if(LogRequests.isRequested(EventID.SESSION | EventID.EXCEPTION))
                SimpleLog.log(CLASS_NAME,"login").
                    message("invalid connect response packet !").end();
                return false;
            }
            //Check sequence.
            if(smias.sequence != connect.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 connect response.
            MPCPLoginResponse response = (MPCPLoginResponse)smias;
            //Check result.
            if(response.error != 0)
            {
                //Log event.
                if(LogRequests.isRequested(EventID.SESSION | EventID.EXCEPTION))
                SimpleLog.log(CLASS_NAME,"login").begin().
                    append(MPCPCommandStatus.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.MPCP_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 terminate.
        MPCPLogout terminate = new MPCPLogout();
        //Set sequence.
        terminate.sequence = nextSequence();

        try
        {
            //Write packet.
            connection.writePacket(terminate);
            //Read packet.
            MPCPPacket smias = (MPCPPacket)connection.readPacket();
            //Check result.
            if(smias == 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(smias.command == MPCPCommandID.LOGOUT)
            {
                //Log event.
                if(LogRequests.isRequested(EventID.SESSION | EventID.INFORMATION))
                SimpleLog.log(CLASS_NAME,"logout").
                    message("terminate request was received !").end();

                //Create response.
                MPCPLogoutResponse response = new MPCPLogoutResponse(smias.sequence);
                //Set command status.
                response.error = MPCPCommandStatus.CMPPE_RSP_SUCCESS;
                //Write response.
                connection.writePacket(response);
            }
            else if(smias.command == MPCPCommandID.LOGOUT_RESPONSE)
            {
                //Log event.
                if(LogRequests.isRequested(EventID.SESSION | EventID.INFORMATION))
                SimpleLog.log(CLASS_NAME,"logout").
                    message("terminate response was received !").end();

                //Check result.
                if(smias.error != MPCPCommandStatus.CMPPE_RSP_SUCCESS)
                {
                    //Log event.
                    if(LogRequests.isRequested(EventID.SESSION | EventID.WARNING))
                    SimpleLog.log(CLASS_NAME,"logout").begin().
                        append(MPCPCommandStatus.toString(smias.error)).append(" !").end();
                }
            }
            else
            {
                //Log event.
                if(LogRequests.isRequested(EventID.SESSION | EventID.EXCEPTION))
                SimpleLog.log(CLASS_NAME,"logout").
                    message("invalid terminate 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.MPCP_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.MPCP_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.MPCP_PACKET | EventID.EXCEPTION))
                SimpleLog.log(CLASS_NAME,"close").exception(e,false).end();
            }
            //Log event.
            if(LogRequests.isRequested(EventID.MPCP_PACKET | EventID.INFORMATION))
            SimpleLog.log(CLASS_NAME,"close").
                message("connection was closed !").end();
        }
        else
        {
            //Log event.
            if(LogRequests.isRequested(EventID.MPCP_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 enterpriseCode = "044022";

        //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("MPCPAPI.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("MPCPAPI.main : log mode(0x" + Hex.toString(logMode) + ") !");
        }
        //Initialize log.
        if(!SimpleLog.initialize(logDirectory,logMode))
        {
            System.out.println("MPCPAPI.main : fail to initialize log !");
            return;
        }

        try
        {
            //Create API.
            MPCPAPI mpcpApi = new MPCPAPI(enterpriseCode,serviceCode,account,password);
            //Connect.
            if(mpcpApi.connect("127.0.0.1",7890))
            {
                //Print.
                System.out.println("MPCPAPI.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.
                        MPCPSubmit submit = new MPCPSubmit(mpcpApi.nextSequence());

                        //Service Provider ID
                        submit.sp_id = enterpriseCode;
                        //Service Type
                        submit.service_type = "HELP";
                        //Fee Type
                        submit.fee_type = 1;
                        //Information Fee
                        submit.info_fee = 0;
                        //Protocol ID
                        submit.protocol_id = 0;
                        //Message Mode
                        submit.message_mode = 1;
                        //Validity Period
                        submit.validity_period = null;
                        //Schedule
                        submit.schedule = null;
                        //Fee User Type
                        submit.fee_user_type = 1;
                        //Fee User
                        submit.fee_user = null;
                        //Source Address
                        submit.source_address = serviceCode;
                        //Destination address
                        submit.destination_address = "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.
                        mpcpApi.writePacket(submit);
                        //Add send count and clear flag.
                        sendCount ++;
                    }

                    //Check available.
                    if(mpcpApi.available())
                    {
                        //Read packet.
                        MPCPPacket packet = mpcpApi.readPacket();
                        //Check instance.
                        if(packet instanceof MPCPDeliver)
                        {
                            //Get deliver.
                            MPCPDeliver deliver = (MPCPDeliver)packet;
                            //Create deliver response.
                            MPCPDeliverResponse response = new MPCPDeliverResponse(packet.sequence);
                            //Write response.
                            mpcpApi.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.message_mode & 0x0f) == 1)
                            {
                                //Print.
                                System.out.println("MPCPAPI.main : report/status(" + deliver.status + ")");
                            }
                            else
                            {
                                //Print.
                                System.out.println("MPCPAPI.main : normal/source(" + deliver.source_address + ")");
                            }
                            //
                            ///////////////////////////////////////////////////////////////////////////
                        }
                        //Check instance.
                        else if(packet instanceof MPCPActiveTest)
                        {
                            //Create activetest response.
                            MPCPActiveTestResponse response = new MPCPActiveTestResponse(packet.sequence);
                            //Write response.
                            mpcpApi.writePacket(response);
                        }
                        //Check command.
                        else if(packet instanceof MPCPSubmitResponse)
                        {
                            //Create submit response.
                            MPCPSubmitResponse response = (MPCPSubmitResponse)packet;
                            //Print.
                            System.out.println("MPCPAPI.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("MPCPAPI.main : fail to connect !");
            }
            //Close.
            mpcpApi.close();
            //Print.
            System.out.println("MPCPAPI.main : connection was closed !");
        }
        catch(Exception e)
        {
            //Print.
            System.out.println("MPCPAPI.main : " + e.getMessage());
            System.out.println("MPCPAPI.main : unexpected exit !");
        }

        //Uninitialize log.
        SimpleLog.uninitialize();

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

