/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/

/*
 * Created on Jul 24, 2005
 */
package org.jboss.test.remoting.transport.multiplex;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;

import junit.framework.TestCase;

import org.jboss.logging.Logger;
import org.jboss.remoting.transport.multiplex.VirtualServerSocket;
import org.jboss.remoting.transport.multiplex.VirtualSocket;

/**
 * 
 * <p>
 * Copyright (c) 2005
 * <p>
 * @author <a href="mailto:r.sigal@computer.org">Ron Sigal</a>
 */
public class PrimeScenarioClient extends TestCase implements MultiplexConstants
{
   private static final Logger log = Logger.getLogger(PrimeScenarioClient.class);
   
   private Socket socket = null;
   private ServerSocket serverSocket = null;
   
   private ObjectOutputStream oos = null;
   private ObjectInputStream  ois = null;
   
   private boolean synchronousSuccess;
   private boolean asynchronousSuccess;
   protected boolean primeScenarioDone = false;

   
   public static void main(String[] args)
   {
      try
      {
         new PrimeScenarioClient().testPrimeScenario();
      }
      catch (IOException e)
      {
         log.error("cannot run prime scenario test");
         e.printStackTrace();
      }
   }

   
   public void testPrimeScenario() throws IOException
   {
      log.info("Starting prime scenario test");
      IOException savedException = null;
      
      for (int i = 0; i < 5; i++)
      {
         try
         {
            socket = new VirtualSocket(primeScenarioServerHost, primeScenarioServerPort);
            break;
         }
         catch (UnknownHostException e)
         {
            log.error("UnknownHostException for \"localhost\"");
            e.printStackTrace();
            System.exit(1);
         }
         catch (IOException e)
         {
            savedException = e;
            
            try 
            {
               Thread.sleep(1000);
            }
            catch (InterruptedException ignored)
            {
            }
         }
      }
      
      if (socket == null)
      {
         log.error("unable to create virtual socket");
         savedException.printStackTrace();
         throw savedException;
      }
      
      Thread callBackThread = new Thread()
       	                      {
						          public void run()
						          {
						             getCallBacks();
						          }
					       	  };
      callBackThread.start();
       	   
      try
      {
         oos = new ObjectOutputStream(socket.getOutputStream());
         ois = new ObjectInputStream(socket.getInputStream());
         
         log.info("CLIENT: beginning synchronous write's");
         
         oos.write(3);
         log.info("CLIENT: synchronous wrote byte     " + 3);
         
         oos.writeInt(7);
         log.info("CLIENT: synchronous wrote int:     " + 7);
         
         oos.writeObject(new Integer(13));
         log.info("CLIENT: synchronous wrote Integer(13)");
         
         oos.writeObject("abc");
         log.info("CLIENT: synchronous wrote String:  \"abc\"");
         
         oos.writeObject(new Boolean(false));
         log.info("CLIENT: synchronous wrote boolean: false");
         
         oos.flush();
         
         log.info("CLIENT: beginning synchronous read's");
            
         int b = ois.read();
         log.info("CLIENT: synchronous read byte     " + b);
         
         int i0 =  ois.readInt();
         log.info("CLIENT: synchronous read int:     " + i0);
         
         Integer i1 = (Integer) ois.readObject();
         log.info("CLIENT: synchronous read Integer: " + i1);
         
         String s = (String) ois.readObject();
         log.info("CLIENT: synchronous read String:  " + s);
         
         Boolean bool = (Boolean) ois.readObject();
         log.info("CLIENT: synchronous read boolean: " + bool);
         
         synchronousSuccess = (b == 3) && (i0 == 7) && (i1.intValue() == 13)
                               && s.equals("abc") && (bool.booleanValue() == false);
         
         if (synchronousSuccess)
            log.info("CLIENT: synchronous test PASSES");
         else
            log.info("CLIENT: synchronous test FAILS");
         
      }
      catch (IOException e)
      {
         log.error("i/o error", e);
         e.printStackTrace();
      }
      catch (ClassNotFoundException e)
      {
         log.error("class not found", e);
         e.printStackTrace();    
      }
      catch (Exception e)
      {
         log.error(e);
         e.printStackTrace();
      }
      
      try
      {
         socket.close();
      }
      catch (IOException ignore)
      {
         log.error("i/o exception closing socket");
      }
      
      log.info("Ending client test (main thread)");
      
      try
      {
         callBackThread.join();
      }
      catch (InterruptedException e)
      {
         log.info("interrupt waiting to join call back thread");
      }
      
      primeScenarioDone = true;
      log.info("ending prime scenario test");
      assertTrue(synchronousSuccess);
      assertTrue(asynchronousSuccess);
   }
   
   
   protected void getCallBacks()
   {
      log.info("Starting client callback test thread");
      IOException savedException = null;
      
      for (int i = 0; i < 5; i++)
      {
         try
         {
            serverSocket = new VirtualServerSocket(socket.getLocalPort());
            break;
         }
         catch (IOException e)
         {
            savedException = e;   
            
            try 
            {
               Thread.sleep(1000);
            }
            catch (InterruptedException ignored)
            {
            }
         }
      }
      
      if (serverSocket == null)
      {
         log.error("unable to get VirtualServerSocket");
         savedException.printStackTrace();
         return;
      }
      
      Socket socket1 = null;
      Socket socket2 = null;
      
      try
      {
         try
         {
         	Thread.sleep(2000);
         }
         catch (InterruptedException e)
         {
            log.error(e);
         }
         
         log.info("creating socket 1");
         socket1 = serverSocket.accept();
         log.info("creating socket 2");
         socket2 = serverSocket.accept();
      }
      catch (IOException e)
      {
         log.error("i/o error creating virtual sockets", e);
         e.printStackTrace();   
      } 
      
      try
      {
         ObjectInputStream ois1 = new ObjectInputStream(socket1.getInputStream());
         ObjectInputStream ois2 = new ObjectInputStream(socket2.getInputStream());
         
         int b1 = ois1.read();
         int b2 = ois2.read();
         
         int i1 = ois1.readInt();
         int i2 = ois2.readInt();
         
         String s1 = ois1.readUTF();
         String s2 = ois2.readUTF();
         
         Integer ii1 = (Integer) ois1.readObject();
         Integer ii2 = (Integer) ois2.readObject();
         
         log.info("CLIENT: socket 1: asynchronous read byte:   " + b1);
         log.info("CLIENT: socket 2: asynchronous read byte:   " + b2);  
         
         log.info("CLIENT: socket 1: asynchronous read int:    " + i1);
         log.info("CLIENT: socket 2: asynchronous read int:    " + i2); 
         
         log.info("CLIENT: socket 1: asynchronous read String: " + s1);
         log.info("CLIENT: socket 2: asynchronous read String: " + s2);  
         
         log.info("CLIENT: socket 1: asynchronous read Integer: " + ii1);
         log.info("CLIENT: socket 2: asynchronous read Integer: " + ii2);
         
         asynchronousSuccess = b1 == 3 && b2 == 7 && i1 == 11 && i2 == 13 && s1.equals("def")
                               && s2.equals("pqr") && ii1.intValue() == 23 && ii2.intValue() == 29;
         
         asynchronousSuccess &= ((VirtualSocket)socket).getMultiplexingManager() 
                                == ((VirtualServerSocket) serverSocket).getMultiplexingManager();
         
         if (asynchronousSuccess)
            log.info(("CLIENT: asynchronous test PASSES"));
         else
            log.info(("CLIENT: asynchronous test FAILS"));
            
      }
      catch (IOException e)
      {
         log.error("i/o error reading from virtual sockets", e);
         e.printStackTrace();   
      } catch (ClassNotFoundException e)
      {
         log.error("class not found exception");
         e.printStackTrace();
      }
      
      try
      {
         serverSocket.close();
         socket1.close();
         socket2.close();
      }
      catch (IOException ignored)
      {
         log.error("i/o error closing server socket");
         ignored.printStackTrace();
      }
      
      log.info("Ending client callback test thread");
   }

}
