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

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

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

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

    /**
     * Node id.
     */
    private int node_id;
    /**
     * Connection.
     */
    private SGIPConnection connection;
    /**
     * Authenticate.
     */
    private SGIPAuthenticate authenticate;

    /**
     * Default construction.
     *
     * @param areaCode Area code.
     * @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 SGIPAPI(String areaCode,String enterpriseCode,String serviceCode,
                   String account,String password)
    {
        //Create sequence.
        sequence = new SimpleSequence();
        //Create connection.
        connection = new SGIPConnection();

        //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 SGIPAuthenticate(node);

        //Chcek result.
        if(areaCode == null || areaCode.length() != 4)
        {
            //Set node_id.
            node_id = (int)(300000L + Long.parseLong(enterpriseCode));
        }
        else
        {
            //Set node_id.
            node_id = (int)(3000000000L + Long.parseLong(areaCode) * 100000 + Long.parseLong(enterpriseCode));
        }
    }

    /**
     * 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 SGIPPacket readPacket()
        throws IOException
    {
        try
        {
            //Return result.
            return connection != null ? (SGIPPacket)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(SGIPPacket 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 bind.
        SGIPBind bind = new SGIPBind();
        //Set sequence.
        bind.sequence = 1;
        //Set node_id.
        bind.node_id = node_id;
        //Set timestamp.
        bind.timestamp = IntegerTimestamp.getTimestamp(System.currentTimeMillis());
        //Set user.
        bind.login_name = authenticate.getAccount();
        //Set password.
        bind.login_password = authenticate.getPassword();
        //Set login type.
        bind.login_type = 1;
        //Check validation.
        if(!bind.isValid())
        {
            //Log event.
            if(LogRequests.isRequested(EventID.SESSION | EventID.EXCEPTION))
            SimpleLog.log(CLASS_NAME,"login").
                message("invalid SGIP connect !").end();
            return false;
        }

        try
        {
            //Write packet.
            connection.writePacket(bind);

            //Read packet.
            SGIPPacket sgip = (SGIPPacket)connection.readPacket();
            //Check result.
            if(sgip == 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(sgip.command != SGIPCommandID.BIND_RESPONSE)
            {
                //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(bind.node_id != sgip.node_id || bind.timestamp != sgip.timestamp || bind.sequence != sgip.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 SGIP bind response.
            SGIPGeneralResponse response = (SGIPGeneralResponse)sgip;
            //Check status.
            if(response.result != 0)
            {
                //Log event.
                if(LogRequests.isRequested(EventID.SESSION | EventID.EXCEPTION))
                SimpleLog.log(CLASS_NAME,"login").begin().
                    append(SGIPResult.toString(response.result)).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.SGIP_PACKET | EventID.EXCEPTION))
            SimpleLog.log(CLASS_NAME,"connect").exception(e,false).end();
        }
        //Return false.
        return false;
    }

	/**
	 * Accept.
	 *
	 * @param localPort Local port.
	 * @return
	 *     <p>Return true, if successfully done.</p>
	 */
	public boolean accept(int localPort)
	{
		try
		{
			//Socket.
			ServerSocket socket = new ServerSocket(localPort);
			//Accept socket.
			Socket accepted = socket.accept();
			//Close socket.
			socket.close();

			//Create socket adapter.
			SocketAdapter adapter = new SocketAdapter(accepted);
			//Set adapter timeout.
			adapter.setTimeout(5 * TimeConstant.SECOND);
			//Open.
			connection.open(adapter);
			//Return accept.
			return accept();
		}
		catch(Exception e)
		{
			//Log event.
			if(LogRequests.isRequested(EventID.SGIP_PACKET | EventID.EXCEPTION))
			SimpleLog.log(CLASS_NAME,"accept").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.
        SGIPUnbind unbind = new SGIPUnbind();
        //Set node_id.
        unbind.node_id = node_id;
        //Set timestamp.
        unbind.timestamp = IntegerTimestamp.getTimestamp(SimpleTime.currentTimeMillis());
        //Set sequence.
        unbind.sequence = nextSequence();

        try
        {

            //Write packet.
            connection.writePacket(unbind);

            //Read packet.
            SGIPPacket sgip = (SGIPPacket)connection.readPacket();
            //Check result.
            if(sgip == 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(sgip.command == SGIPCommandID.UNBIND)
            {
                //Create unbind.
                SGIPUnbindResponse response = new SGIPUnbindResponse(sgip.sequence);
                //Set node_id.
                unbind.node_id = sgip.node_id;
                //Set timestamp.
                unbind.timestamp = sgip.timestamp;
                //Write response.
                connection.writePacket(response);
                //Log event.
                if(LogRequests.isRequested(EventID.SESSION | EventID.INFORMATION))
                SimpleLog.log(CLASS_NAME,"logout").
                    message("unbind request was received !").end();
            }
            else if(sgip.command == SGIPCommandID.UNBIND_RESPONSE)
            {
                //Log event.
                if(LogRequests.isRequested(EventID.SESSION | EventID.INFORMATION))
                SimpleLog.log(CLASS_NAME,"logout").
                    message("unbind response was received !").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.SGIP_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.SGIP_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.SGIP_PACKET | EventID.EXCEPTION))
                SimpleLog.log(CLASS_NAME,"close").exception(e,false).end();
            }
            //Log event.
            if(LogRequests.isRequested(EventID.SGIP_PACKET | EventID.INFORMATION))
            SimpleLog.log(CLASS_NAME,"close").
                message("connection was closed !").end();
        }
        else
        {
            //Log event.
            if(LogRequests.isRequested(EventID.SGIP_PACKET | EventID.INFORMATION))
            SimpleLog.log(CLASS_NAME,"close").
                message("connection has already been closed !").end();
        }
    }

    /**
     * Accept 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 accpeted successfully.</p>
     */
    public boolean accept()
    {
        try
        {
            //Read packet.
            SGIPPacket sgip = (SGIPPacket)connection.readPacket();
            //Check result.
            if(sgip == null)
            {
                //Log event.
                if(LogRequests.isRequested(EventID.SESSION | EventID.EXCEPTION))
                SimpleLog.log(CLASS_NAME,"accept").
                    message("fail to read packet !").end();
                return false;
            }
            //Check command.
            if(sgip.command != SGIPCommandID.BIND)
            {
                //Check event.
                if(LogRequests.isRequested(EventID.SESSION | EventID.EXCEPTION))
                SimpleLog.log(CLASS_NAME,"accept").
                    message("invalid bind packet !").end();
                return false;
            }
            //Get bind packet.
            SGIPBind bind = (SGIPBind)sgip;
            //Get bind response packet.
            SGIPBindResponse response = new SGIPBindResponse(bind.sequence);
            //Set node_id.
            response.node_id = bind.node_id;
            //Set time_stamp.
            response.timestamp = bind.timestamp;
            //Write packet.
            connection.writePacket(response);
            //Set authenticated.
            authenticated = true;
            //Return true.
            return true;
        }
        catch(Exception e)
        {
            //Log event.
            if(LogRequests.isRequested(EventID.SESSION | EventID.EXCEPTION))
            SimpleLog.log(CLASS_NAME,"accept").exception(e,false).end();
        }
        //Return false.
        return false;
    }

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

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

                        //SP Number
                        submit.sp_number = serviceCode;
                        //Charge Number
                        submit.charge_number = null;
                        //User Count
                        submit.user_count = 1;
                        //User Number
                        submit.user_number = new String[]{"13001081371"};
                        //Corporation ID
                        submit.corporation_id = enterpriseCode;
                        //Service Type
                        submit.service_type = "HELP";
                        //Fee Type
                        submit.fee_type = 0;
                        //Fee Value
                        submit.fee_value = null;
                        //Given Value
                        submit.given_value = null;
                        //Agent Flag
                        submit.agent_flag = 0;
                        //Mo Relate to MT Flag
                        submit.mo_relate_to_mt_flag = 2;
                        //Expire Time
                        submit.expire_time = null;
                        //Schedule Time
                        submit.schedule_time = null;
                        //Report Flag
                        submit.report_flag = 2;
                        //TP_PID
                        submit.tp_pid = 0;
                        //TP_UDHI
                        submit.tp_udhi = 0;
                        //Message Coding
                        submit.message_coding = 0;
                        //Message Type
                        submit.message_type = 0;
                        //Message Content
                        submit.message_content = "Hello World !".getBytes("US-ASCII");
                        //Message Length
                        submit.message_length = submit.message_content.length;
                        //Reserved Field
                        submit.reserve = null;

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

                    //Check available.
                    if(sgipApi.available())
                    {
                        //Read packet.
                        SGIPPacket packet = sgipApi.readPacket();
                        //Check instance.
                        if(packet instanceof SGIPDeliver)
                        {
                            //Get deliver.
                            SGIPDeliver deliver = (SGIPDeliver)packet;
                            //Create deliver response.
                            SGIPDeliverResponse response = new SGIPDeliverResponse(packet.sequence);
                            //Set result.
                            response.result = 0;
                            //Write response.
                            sgipApi.writePacket(response);//Do response as soon as possible.

                            ///////////////////////////////////////////////////////////////////////////
                            //
                            // Do process of deliver.
                            //
                            // <p>You can add your own processing here !</p>
                            //
                            //Print.
                            System.out.println("SGIPAPI.main : normal/source(" + deliver.user_number + ")");
                            //
                            ///////////////////////////////////////////////////////////////////////////
                        }
                        else if(packet instanceof SGIPReport)
                        {
                            //Get report.
                            SGIPReport report = (SGIPReport)packet;
                            //Create report response.
                            SGIPReportResponse response = new SGIPReportResponse(packet.sequence);
                            //Set result.
                            response.result = 0;
                            //Write response.
                            sgipApi.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>
                            //
                            //Print.
                            System.out.println("SGIPAPI.main : report/status(" + report.error_code + ")");
                            //
                            ///////////////////////////////////////////////////////////////////////////
                        }
                        //Check command.
                        else if(packet instanceof SGIPSubmitResponse)
                        {
                            //Create submit response.
                            SGIPSubmitResponse response = (SGIPSubmitResponse)packet;
                            //Print.
                            System.out.println("SGIPAPI.main : submit/result(" + response.result + ")");

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

        //Uninitialize log.
        SimpleLog.uninitialize();

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

