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.cmpp.*;

import com.simpleteam.transactor.authenticate.*;
import com.simpleteam.connection.protocol.cmpp.*;

/**
 * <p>Title: SimpleTeam</p>
 * <p>Description: CMPP3 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 CMPP3API
{
	//Class Name
	private final static String CLASS_NAME                  = "CMPP3API";

	/**
	 * Whether authenticated.
	 */
	private boolean authenticated;
	/**
	 * Simple sequence.
	 */
	private SimpleSequence sequence;

	/**
	 * Connection.
	 */
	private CMPP3Connection connection;
	/**
	 * Authenticate.
	 */
	private CMPPAuthenticate 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 CMPP3API(String enterpriseCode,String serviceCode,
				   String account,String password)
	{
		//Create sequence.
		sequence = new SimpleSequence();
		//Create connection.
		connection = new CMPP3Connection();

		//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 CMPPAuthenticate(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 CMPPPacket readPacket()
		throws IOException
	{
		try
		{
			//Return result.
			return connection != null ? (CMPPPacket)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(CMPPPacket 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 timestamp.
		IntegerTimestamp timestamp = new IntegerTimestamp();
		//Create connect.
		CMPPConnect connect = new CMPPConnect();
        //Set version.
        connect.version = 0x30;
        //Set timestamp.
        connect.timestamp = timestamp.getTimestamp();
        //Set source address.
        connect.source_addr = authenticate.getAccount();
        //Get authenticate.
        connect.authenticator_sp = authenticate.getAuthenticate(timestamp);
		//Set sequence.
		connect.sequence = nextSequence();
		//Check validation.
		if(!connect.isValid())
		{
			//Log event.
			if(LogRequests.isRequested(EventID.CMPP_PACKET | EventID.EXCEPTION))
			SimpleLog.log(CLASS_NAME,"login").
				message("invalid CMPP3 connect !").end();
			return false;
		}

		try
		{
			//Write packet.
			connection.writePacket(connect);
			//Read packet.
			CMPPPacket cmpp = (CMPPPacket)connection.readPacket();
			//Check result.
			if(cmpp == null)
			{
				//Log event.
				if(LogRequests.isRequested(EventID.CMPP_PACKET | EventID.EXCEPTION))
				SimpleLog.log(CLASS_NAME,"login").
					message("fail to read packet !").end();
				return false;
			}
			//Check command
			if(cmpp.command != CMPPCommandID.CONNECT_RESPONSE)
			{
				//Log event.
				if(LogRequests.isRequested(EventID.CMPP_PACKET | EventID.EXCEPTION))
				SimpleLog.log(CLASS_NAME,"login").
					message("invalid connect response packet !").end();
				return false;
			}
			//Check sequence.
			if(cmpp.sequence != connect.sequence)
			{
				//Log event.
				if(LogRequests.isRequested(EventID.CMPP_PACKET | EventID.EXCEPTION))
				SimpleLog.log(CLASS_NAME,"login").
					message("sequence is not according to previous !").end();
				return false;
			}
			//Get connect response.
			CMPP3ConnectResponse response = (CMPP3ConnectResponse)cmpp;
			//Check result.
			if(response.status != 0)
			{
				//Log event.
				if(LogRequests.isRequested(EventID.CMPP_PACKET | EventID.EXCEPTION))
				SimpleLog.log(CLASS_NAME,"login").begin().
					append(CMPPStatus.toString(response.status)).append(" !").end();
				return false;
			}
			//Set authenticated.
			authenticated = true;
			//Return true.
			return true;
		}
		catch(Exception e)
		{
			//Log event.
			if(LogRequests.isRequested(EventID.CMPP_PACKET | 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.CMPP_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.
		CMPPTerminate terminate = new CMPPTerminate();
		//Set sequence.
		terminate.sequence = nextSequence();

		try
		{
			//Write packet.
			connection.writePacket(terminate);
			//Read packet.
			CMPPPacket cmpp = (CMPPPacket)connection.readPacket();
			//Check result.
			if(cmpp == null)
			{
				//Log event.
				if(LogRequests.isRequested(EventID.CMPP_PACKET | EventID.EXCEPTION))
				SimpleLog.log(CLASS_NAME,"logout").
					message("fail to read packet !").end();
				return false;
			}
			//Check command.
			if(cmpp.command == CMPPCommandID.TERMINATE)
			{
				//Create response.
				CMPPTerminateResponse response = new CMPPTerminateResponse(cmpp.sequence);
				//Write response.
				connection.writePacket(response);
				//Log event.
				if(LogRequests.isRequested(EventID.CMPP_PACKET | EventID.INFORMATION))
				SimpleLog.log(CLASS_NAME,"logout").
					message("terminate request was received !").end();
			}
			else if(cmpp.command == CMPPCommandID.TERMINATE_RESPONSE)
			{
				//Log event.
				if(LogRequests.isRequested(EventID.CMPP_PACKET | EventID.INFORMATION))
				SimpleLog.log(CLASS_NAME,"logout").
					message("terminate response was received !").end();
			}
			else
			{
				//Log event.
				if(LogRequests.isRequested(EventID.CMPP_PACKET | 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.CMPP_PACKET | 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.CMPP_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.CMPP_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.CMPP_PACKET | EventID.EXCEPTION))
				SimpleLog.log(CLASS_NAME,"close").exception(e,false).end();
			}
			//Log event.
			if(LogRequests.isRequested(EventID.CMPP_PACKET | EventID.INFORMATION))
			SimpleLog.log(CLASS_NAME,"close").
				message("connection was closed !").end();
		}
		else
		{
			//Log event.
			if(LogRequests.isRequested(EventID.CMPP_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("CMPP3API.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("CMPP3API.main : log mode(0x" + Hex.toString(logMode) + ") !");
        }
        //Initialize log.
        if(!SimpleLog.initialize(logDirectory,logMode))
        {
            System.out.println("CMPP3API.main : fail to initialize log !");
            return;
        }

		try
		{
			//Create API.
			CMPP3API cmppApi = new CMPP3API(enterpriseCode,serviceCode,account,password);
			//Connect.
			if(cmppApi.connect("127.0.0.1",7890))
			{
				//Print.
				System.out.println("CMPP3API.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.
						CMPP3Submit submit = new CMPP3Submit(cmppApi.nextSequence());
						//Set parameters.
						submit.msg_id = 1L;
						//Packet Total
						submit.pk_total = 1;
						//Packet Number
						submit.pk_number = 1;
						//Registered Delivery
						submit.registered_delivery = 1;//Report needed.
						//Service ID
						submit.service_id = "HELP";
						//Fee User Type
						submit.fee_user_type = 0;
						//Fee Terminal ID
						submit.fee_terminal_id = null;
						//TP_PID
						submit.tp_pid = 0;
						//TP_UDHI
						submit.tp_udhi = 0;
						//Message Format
						submit.msg_fmt = 0;
						//Message Source
						submit.msg_src = enterpriseCode;
						//Fee Type
						submit.fee_type = "01";
						//Fee Code
						submit.fee_code = "000000";
						//Valid Time
						submit.valid_time = null;
						//AT Time
						submit.at_time = null;
						//Source Terminal ID
						submit.src_terminal_id = serviceCode;
						//Total Destination User
						submit.dest_usr_tl = 1;
						//Destination Terminal ID
						submit.dest_terminal_id = new String[]{"13501026991"};
						//Message Content
						submit.msg_content = "Hello World !".getBytes("US-ASCII");
						//Message Length
						submit.msg_length = submit.msg_content.length;
                        //Link id.
                        submit.link_id = "012345678901234567890";
						//Reserved Field
						submit.reserve = null;

						//Write packet.
						cmppApi.writePacket(submit);
						//Add send count and clear flag.
						sendCount ++;
					}

					//Check available.
					if(cmppApi.available())
					{
						//Read packet.
						CMPPPacket packet = cmppApi.readPacket();
						//Check instance.
						if(packet instanceof CMPP3Deliver)
						{
							//Get deliver.
							CMPP3Deliver deliver = (CMPP3Deliver)packet;
							//Create deliver response.
							CMPP3DeliverResponse response = new CMPP3DeliverResponse(packet.sequence);
							//Set result.
							response.result = 0;
							//Set response.
							response.msg_id = deliver.msg_id;
							//Write response.
							cmppApi.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 registered_delivery.
							if((deliver.registered_delivery & 0x0f) == 1)
							{
								//Print.
								System.out.println("CMPP3API.main : report/status(" + deliver.status + ")");
							}
							else
							{
								//Print.
								System.out.println("CMPP3API.main : normal/source(" + deliver.src_terminal_id + ")");
							}
							//
							///////////////////////////////////////////////////////////////////////////
						}
						//Check instance.
						else if(packet instanceof CMPPActiveTest)
						{
							//Create activetest response.
							CMPPActiveTestResponse response = new CMPPActiveTestResponse(packet.sequence);
							//Set result.
							response.success_id = 0;
							//Write response.
							cmppApi.writePacket(response);
						}
						//Check command.
						else if(packet instanceof CMPP3SubmitResponse)
						{
							//Create submit response.
							CMPP3SubmitResponse response = (CMPP3SubmitResponse)packet;
							//Print.
							System.out.println("CMPP3API.main : submit/result(" + response.msg_id + ")");

							///////////////////////////////////////////////////////////////////////////
							//
							// Do process of submit response.
							//
							// <p>You can add your own processing here !</p>
							//
							//
							///////////////////////////////////////////////////////////////////////////
						}
					}
				}
			}
			else
			{
				//Print.
				System.out.println("CMPP3API.main : fail to connect !");
			}
			//Close.
			cmppApi.close();
			//Print.
			System.out.println("CMPP3API.main : connection was closed !");
		}
		catch(Exception e)
		{
			//Print.
			System.out.println("CMPP3API.main : " + e.getMessage());
			System.out.println("CMPP3API.main : unexpected exit !");
		}

        //Uninitialize log.
        SimpleLog.uninitialize();

        //Exit
        System.exit(0);
	}
}

