/*
* 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.
*/

package org.jboss.test.remoting.transport.multiplex;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import org.jboss.jrunit.extensions.ServerTestCase;
import org.jboss.logging.Logger;
import org.jboss.remoting.transport.multiplex.MasterServerSocket;
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 PrimeScenarioServer extends ServerTestCase implements MultiplexConstants
{
   private static final Logger log = Logger.getLogger(PrimeScenarioServer.class);

   private Socket socket = null;
   private ServerSocket serverSocket = null;

   private ObjectOutputStream os = null;
   private ObjectInputStream is = null;

   private boolean testOver = false;

   /**
    * 
    */
   public PrimeScenarioServer()
   {
   }


   protected void runPrimeScenario()
   {
      new PrimeScenarioThread().start();
   }


   protected void sendCallBacks(Socket socket)
   {
      log.info("Starting server callback tests");

      String hostName = socket.getInetAddress().getHostName();
      int port = socket.getPort();
      Socket socket1 = null;
      Socket socket2 = null;


      try
      {
         Thread.sleep(2000);
      }
      catch(InterruptedException ignored)
      {
      }

      try
      {
         InetAddress localAddress = socket.getLocalAddress();
         InetAddress remoteAddress = socket.getInetAddress();
         socket1 = new VirtualSocket(hostName, port);

         log.info("testCallBacks(): created socket for asynchronous tests on port: "
                  + ((VirtualSocket) socket1).getLocalVirtualPort());

         InetSocketAddress localSocketAddress = new InetSocketAddress(socket.getLocalAddress(), socket.getLocalPort());
         InetSocketAddress remoteSocketAddress = new InetSocketAddress(hostName, port);
         socket2 = new VirtualSocket(remoteAddress, socket.getPort(), localAddress, socket.getLocalPort());
         log.info("testCallBacks(): created second socket for asynchronous tests on port: "
                  + ((VirtualSocket) socket1).getLocalVirtualPort());
         //        socket2 = new VirtualSocket(remoteAddress, socket.getPort());
//         
//         socket2 = new VirtualSocket();
//         socket2.bind(localSocketAddress);
//         socket2.connect(remoteSocketAddress);
//         log.info("testCallBacks(): created socket for asynchronous tests on port: "
//               + ((VirtualSocket)socket2).getLocalVirtualPort());
      }
      catch(UnknownHostException e)
      {
         log.error("unknown host exception");
         e.printStackTrace();
      }
      catch(IOException e)
      {
         log.error("i/o exception");
         e.printStackTrace();
      }

      try
      {
         ObjectOutputStream oos1 = new ObjectOutputStream(socket1.getOutputStream());
         ObjectOutputStream oos2 = new ObjectOutputStream(socket2.getOutputStream());

         oos1.write(3);
         log.info("SERVER: asynchronous wrote byte:    " + 3);

         oos2.write(7);
         log.info("SERVER: asynchronous wrote byte:    " + 7);

         oos1.writeInt(11);
         log.info("SERVER: asynchronous wrote int:     " + 11);

         oos2.writeInt(13);
         log.info("SERVER: asynchronous wrote int:     " + 13);

         oos1.writeUTF("def");
         log.info("SERVER: asynchronous wrote String:  \"def\"");

         oos2.writeUTF("pqr");
         log.info("SERVER: asynchronous wrote String:  \"pqr\"");

         oos1.writeObject(new Integer(23));
         log.info("SERVER: asynchronous wrote Integer(23)");

         oos2.writeObject(new Integer(29));
         log.info("SERVER: asynchronous wrote Integer(29)");

         oos1.flush();
         oos2.flush();
      }
      catch(IOException e)
      {
         log.error("i/o error writing to virtual sockets", e);
         e.printStackTrace();
      }


      try
      {
         socket1.close();
         socket2.close();
      }
      catch(IOException ignored)
      {
         log.info("error closing sockets");
      }

      log.info("Ending server callback tests");
   }


   protected void setUp()
   {
      new PrimeScenarioThread().start();
   }


   protected void tearDown() throws Exception
   {
   }


   public static void main(String[] args)
   {
      new PrimeScenarioServer().runPrimeScenario();
   }


   class PrimeScenarioThread extends Thread
   {
      public void run()
      {
         log.info("Start prime scenario test");

         try
         {
            serverSocket = new MasterServerSocket(primeScenarioServerPort);
            log.info("ServerTest.runTest(): created MasterServerSocket");
            log.info("localAddress: " + serverSocket.getLocalSocketAddress() + ", local port: " + serverSocket.getLocalPort());
         }
         catch(IOException e)
         {
            log.error("ServerTest.runTest(): cannot create MasterServerSocket");
            e.printStackTrace();
            System.exit(1);
         }
         catch(Exception e)
         {
            log.error(e);
            e.printStackTrace();
            System.exit(1);
         }

         while(!testOver)
         {
            try
            {
               socket = serverSocket.accept();
               log.info("ServerTest.runTest(): created VirtualSocket for synchronous tests on port: "
                        + ((VirtualSocket) socket).getLocalVirtualPort());
               log.info("localAddress: " + socket.getLocalAddress() + ", local port: " + socket.getLocalPort());
               log.info("remoteAddress: " + socket.getInetAddress() + ", remote port: " + socket.getPort());
               Thread thread = new ServerTestThread(socket);
               thread.start();
            }
            catch(SocketException ignored)
            {
               // it's because the ServerSocket was closed
            }
            catch(IOException e)
            {
               log.error("ServerTest.main(): cannot create VirtualSocket");
               e.printStackTrace();
            }
            catch(Exception e)
            {
               log.error(e);
               e.printStackTrace();
            }
         }

      }
   }

   class ServerTestThread extends Thread
   {
      private Socket socket;
      private ObjectInputStream ois;
      private ObjectOutputStream oos;
      private DataInputStream is;
      private DataOutputStream os;


      public ServerTestThread(Socket socket)
      {
         this.socket = socket;

         try
         {
            ois = new ObjectInputStream(socket.getInputStream());
            oos = new ObjectOutputStream(socket.getOutputStream());
         }
         catch(IOException e)
         {
            log.error("ServerTest.ServerTestThread(): i/o error creating InputStream or OutputStream", e);
            e.printStackTrace();
         }
      }

      public void run()
      {
         log.info("Starting server test thread");

         sendCallBacks(socket);

         try
         {
            int i1 = ois.read();
            log.info("SERVER: synchronous read byte    " + i1);
            oos.write(i1);
            log.info("SERVER: synchronous wrote byte    " + i1);

            int i2 = ois.readInt();
            log.info("SERVER: synchronous read int:    " + i2);
            oos.writeInt(i2);
            log.info("SERVER: synchronous wrote int:    " + i2);
         }
         catch(IOException e)
         {
            log.error("ServerTest.ServerTestThread.run(): i/o error", e);
            e.printStackTrace();
            return;
         }

         boolean stillReading = true;

         while(stillReading)
         {
            try
            {
               Object o = ois.readObject();

               if(o instanceof Boolean && ((Boolean) o).booleanValue() == false)
               {
                  stillReading = false;
               }

               log.info("SERVER: synchronous read object: " + o + " (" + o.getClass() + ")");
               oos.writeObject(o);
               log.info("SERVER: synchronous wrote object: " + o + " (" + o.getClass() + ")");
            }
            catch(IOException e)
            {
               log.error("ServerTest.ServerTestThread.run(): i/o error", e);
               e.printStackTrace();
               break;
            }
            catch(ClassNotFoundException e)
            {
               log.error("ServerTest.ServerTestThread.run(): class not found", e);
               e.printStackTrace();
               break;
            }
         }

         try
         {
            socket.close();
         }
         catch(IOException ignored)
         {
            log.info("i/o error closing socket");
         }

         testOver = true;

         try
         {
            serverSocket.close();
         }
         catch(IOException ignored)
         {
            log.info("i/o error closing server socket");
         }

         log.info("Ending server test thread");
      }
   }


}