package jbCAP;

import jVARIANT.VARIANT;
import jVARIANT.VARENUM;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;

/** @file bCapSocketTCP.java
 *
 *  @brief b-CAP client library
 *
 *  @version	1.2
 *	@date		2014/5/26
 *	@author		DENSO WAVE (m)
 *
 */
class bCapSocketTCP extends bCapSocket implements Runnable{
	private Socket m_sock;
	private DataInputStream m_in;
	private DataOutputStream m_out;

	private short m_sSerial;
		
	private Object[] m_args;
	private bCapPacket m_msgRet;
	
	private int m_zipMode;
	private static final int MODE_ZIP = 1;
	private static final int ZIP_THRESHOLD = 1; // [kB]
	
	private static bCapConverter TCPConverter = new bCapConverter(1);
	
	// Constructor
	protected bCapSocketTCP(String host, int port, int timeout, int wdt, int zipMode)
	{
		if(zipMode < 0 || 9 < zipMode){
			m_zipMode = 0;
		}else{
			m_zipMode = zipMode;
		}
		
		m_args = new Object[]{1, host, port, timeout, wdt};
		
		Thread th = new Thread(this);
		
		th.start();
		WaitThread(th);
		
		m_args = null;
	}
	
	@Override
	protected void Release()
	{
		m_args = new Object[]{2, null};
		
		Thread th = new Thread(this);
		
		th.start();
		WaitThread(th);
		
		m_args = null;
	}
		
	@Override
	protected int IsConnected()
	{
		if(m_sock == null)
		{
			return E_NOT_CONNECTED;
		}
		else
		{
			return 0;
		}
	}
	
	@Override
	protected synchronized bCapPacket SendMessage(bCapPacket msg)
	{
		m_args = new Object[]{3, msg};
		m_msgRet = null;
		
		Thread th = new Thread(this);
		
		th.start();
		WaitThread(th);
		
		m_args = null;
		return m_msgRet;
	}
	
	public void run(){
		switch((Integer)m_args[0]){
			case 1:
				th_init((String)m_args[1], (Integer)m_args[2], (Integer)m_args[3], (Integer)m_args[4]);
				break;
			case 2:
				th_release();
				break;
			case 3:
				th_sendmessage((bCapPacket)m_args[1]);
				break;
		}
	}
	
	private void th_init(String host, int port, int timeout, int wdt){
		try
		{
			m_sock = new Socket(host, port);
			
			if(timeout < 0)
			{
				m_sock.setSoTimeout(0);
			}
			else
			{
				m_sock.setSoTimeout(timeout);
			}
	
			m_in = new DataInputStream(m_sock.getInputStream());
			m_out = new DataOutputStream(m_sock.getOutputStream());
			
			m_sSerial = 1;
			
			String Arg = ", ZipMode=" + Integer.toString(m_zipMode);
			
			if(wdt >= 80){
				Arg = Arg + ", WDT=" + Integer.toString(wdt);
			}

			bCapStart(new VARIANT(VARENUM.VT_BSTR, Arg));
		}
		catch(Throwable ex)
		{
			th_release();
		}
	}

	private void th_release(){
		bCapStop();
		
		// DataOutputStream
		try
		{
			m_out.close();
		}
		catch(Throwable ignore){
			// Do nothing
		}
		finally
		{
			m_out = null;
		}
		
		// DataInputStream
		try
		{
			m_in.close();
		}
		catch(Throwable ignore)
		{
			// Do nothing
		}
		finally
		{
			m_in = null;
		}
		
		// Socket
		try
		{
			m_sock.close();
		}
		catch(Throwable ignore)
		{
			// Do nothing
		}
		finally
		{
			m_sock = null;
		}
	}
	
	private void th_sendmessage(bCapPacket msg){
		byte[] bSend, bReceive, bSize = new byte[5];
		int iSize;
		m_msgRet = new bCapPacket();
		
		try
		{
			msg.m_Serial = m_sSerial;
			msg.m_Reserv = (m_zipMode != 0 ? (short)MODE_ZIP : (short)0);
			
			// Send Message
			bSend = TCPConverter.Encode(msg);
			
			// Compress send packet
			if((m_zipMode != 0) && bSend.length >= ZIP_THRESHOLD * 1000){
				bSend = TCPConverter.ZipDeflater(bSend, m_zipMode);
			}
			
			m_out.write(bSend);
			
			do{
				// Receive message size
				m_in.read(bSize);
				
				iSize = TCPConverter.MsgSize2Int(bSize);
				
				bReceive = new byte[iSize];
				
				// Receive message
				m_in.readFully(bReceive, 5, iSize - 5);
				
				// Copy packet of buffer size
				System.arraycopy(bSize, 0, bReceive, 0, 5);
				
				// Check mode
				m_msgRet = TCPConverter.DecodeInfo(bReceive);
				if(m_msgRet.m_ID >= 1){
					if((bReceive[bReceive.length - 2] == MODE_ZIP)){
						bReceive = TCPConverter.ZipInflater(bReceive);
					}
				}
				
				// byte -> bCapPacket
				m_msgRet = TCPConverter.Decode(bReceive);
				
				if(msg.m_Serial != m_msgRet.m_Serial){
					continue;
				}

				if(m_msgRet.m_ID != S_EXECUTING){
					break;
				}
			}while(true);
		}catch(Throwable e){
			// Return FAILED
			m_msgRet = ReturnErrorPacket(e);
		}
		finally
		{
			m_sSerial = (short)((m_sSerial != -1) ? m_sSerial + 1 : 1);
		}
	}
}
