/*
* 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 Dec 15, 2005
 */
package org.jboss.test.remoting.transport.multiplex.utility;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.Map;

import org.jboss.logging.Logger;
import org.jboss.remoting.transport.multiplex.utility.GrowablePipedInputStream;
import org.jboss.remoting.transport.multiplex.utility.GrowablePipedOutputStream;
import org.jboss.remoting.transport.multiplex.utility.ShrinkableByteArrayOutputStream;
import org.jboss.remoting.transport.multiplex.utility.VirtualSelector;


/**
 * A GrowableStreamTestCase.

 * @author <a href="mailto:r.sigal@computer.org">Ron Sigal</a>
 * @version $Revision: 1032 $
 * <p>
 * Copyright (c) 2005
 * </p>
 */

public class GrowableStreamTestCase extends SuccessCountingTestCase
{
   protected static final Logger log = Logger.getLogger(GrowableStreamTestCase.class);
   
   
   public void testConstructors()
   {
      log.info("entering: testConstructors()");
      
      try
      {
         GrowablePipedInputStream gpis1 = new GrowablePipedInputStream();
         GrowablePipedOutputStream gpos1 = new GrowablePipedOutputStream(gpis1);
         GrowablePipedInputStream gpis2 = new GrowablePipedInputStream();
         GrowablePipedOutputStream gpos2 = new GrowablePipedOutputStream(gpis2);
         GrowablePipedInputStream gpis3 = new GrowablePipedInputStream();
         GrowablePipedOutputStream gpos3 = new GrowablePipedOutputStream();
         
         try
         {
            gpis3.read();
            fail();
         }
         catch (Exception e)
         {
            assertTrue(e.getMessage().equals("Pipe not connected"));
         }
         
         
         try
         {
            gpos3.write(3);
            fail();
         }
         catch (Exception e)
         {
            assertTrue(e.getMessage().equals("Pipe not connected"));
         }
      }
      catch (Exception e)
      {
         log.error(e);
         e.printStackTrace();
         fail();
      }
      
      log.info("testConstructors() PASSES");
      OKCounter++;
   }
   
   
   public void testAvailable()
   {
      log.info("entering: testAvailable()");
      
      try
      {
         GrowablePipedInputStream  gpis = new GrowablePipedInputStream();
         GrowablePipedOutputStream gpos = new GrowablePipedOutputStream(gpis);
         int size = 64 * 1024;
         
         for (int i = 0; i < size; i++)
            gpos.write(i);
         
         assertTrue(gpis.available() == size);
         
      }
      catch (Exception e)
      {
         log.error(e);
         e.printStackTrace();
         fail();
      }
      
      log.info("testAvailable() PASSES");
      OKCounter++;
   }
   
   
   public void testIO()
   {
      log.info("entering: testIO()");
      
      try
      {
         final GrowablePipedInputStream gpis = new GrowablePipedInputStream();
         final GrowablePipedOutputStream gpos = new GrowablePipedOutputStream(gpis);
         final int size =  128 * 1024;
         final byte[] bytesIn = new byte[size];
         final byte[] bytesOut = new byte[size];
         
         for (int i = 0; i < size; i++)
         {
            bytesIn[i] = (byte) i;
         }
         
         class ThreadOut extends Thread
         {
            Exception e;
            public void run()
            {
               try
               {
                  for (int i = 0; i < size / 2; i++)
                     gpos.write(bytesIn[i]);
                  
                  int chunk = 4096;
                  
                  for (int i = size / 2; i < size; )
                  {
                     gpos.write(bytesIn, i, chunk);
                     i += chunk;
                  }
               }
               catch (IOException e)
               {
                  this.e = e;
               }
            }
            public Exception getException()
            {
               return e;
            }
         };
         
         class ThreadIn extends Thread
         {
            Exception e;
            public void run()
            {
               try
               {
                  for (int i = 0; i < size; i++)
                     bytesOut[i] = (byte) gpis.read();
               }
               catch (IOException e)
               {
                  this.e = e;
               }
            }
            public Exception getException()
            {
               return e;
            }
         };
         
         ThreadOut t1 = new ThreadOut();
         ThreadIn t2 = new ThreadIn();
         t1.start();
         t2.start();
         t1.join();
         t2.join();
         assertTrue(t1.getException() == null);
         assertTrue(t2.getException() == null);
       
         for (int i = 0; i < size; i++)
            assertTrue(bytesOut[i] == bytesIn[i]);
      }
      catch (Exception e)
      {
         log.error(e);
         e.printStackTrace();
         fail();
      }
      
      log.info("testIO() PASSES");
      OKCounter++;
   }
   
   public void testVirtualSelector()
   {
      log.info("entering: testVirtualSelector()");
      
      try
      {
         final VirtualSelector vs = new VirtualSelector();
         final GrowablePipedInputStream  gpis1 = new GrowablePipedInputStream(vs);
         final GrowablePipedOutputStream gpos1 = new GrowablePipedOutputStream(gpis1);
         final GrowablePipedInputStream  gpis2 = new GrowablePipedInputStream(vs);
         final GrowablePipedOutputStream gpos2 = new GrowablePipedOutputStream(gpis2);
         final int size =  128 * 1024;
         final byte[] bytesOut1 = new byte[size];
         final byte[] bytesOut2 = new byte[size];
         final ShrinkableByteArrayOutputStream bytesIn1  = new ShrinkableByteArrayOutputStream();
         final ShrinkableByteArrayOutputStream bytesIn2  = new ShrinkableByteArrayOutputStream();
         
         for (int i = 0; i < size; i++)
         {
            bytesOut1[i] = (byte) i;
            bytesOut2[i] = (byte) (i+1);
         }
         
         vs.register(gpis1, new Integer(1));
         vs.register(gpis2, new Integer(2));
         
         class ThreadIn extends Thread
         {
            Exception e;
            
            public void run()
            {
               Map readyMap;
               log.info("starting run()");
               try
               {
                  while ((readyMap = vs.select()) != null)
                  {
                     log.debug("readyMap != null");
                     Iterator it = readyMap.keySet().iterator();
                     while (it.hasNext())
                     {
                        InputStream is = (InputStream) it.next();
                        
                        if (is.available() == 0)
                        {
                           it.remove();
                           continue;
                        }
                        
                        Integer attachment = (Integer) readyMap.get(is);
                        
                        ShrinkableByteArrayOutputStream bytesIn;
                        if (attachment.intValue() == 1)
                           bytesIn = bytesIn1;
                        else
                           bytesIn = bytesIn2;
 
                        try
                        {
                           int available = is.available();
                           log.debug("available: " + available);
                           log.debug("stream: " + attachment);
                           for (int i = 0; i < available; i++)
                              bytesIn.write((byte) is.read());
                        }
                        catch (IOException e)
                        {
                           this.e = e;
                        }
                     }
                  }
               }
               catch (Exception e)
               {
                  log.error(e);
                  e.printStackTrace();
               }
               log.info("ending run()");
            }
            public Exception getException()
            {
               return e;
            }
         };
         
         class ThreadOut extends Thread
         {
            OutputStream os;
            byte[] bytesOut;
            Exception e;
            
            public ThreadOut(OutputStream os, byte[] bytesOut)
            {
               this.os = os;
               this.bytesOut = bytesOut;
            }
            
            public void run()
            {
               try
               {
                  for (int i = 0; i < size / 2; i++)
                     os.write(bytesOut[i]);
                  
                  int chunk = 4096;
                  
                  for (int i = size / 2; i < size; )
                  {
                     os.write(bytesOut, i, chunk);
                     i += chunk;
                  }
//                  for (int i = 0; i < size; i++)
//                  {
//                     log.warn("writing: " + bytesOut[i]);
//                     os.write(bytesOut[i]);
//                  }
               }
               catch (IOException e)
               {
                  this.e = e;
               }
            }
            public Exception getException()
            {
               return e;
            }
         };
         
         ThreadIn  tin =  new ThreadIn();
         ThreadOut t1out = new ThreadOut(gpos1, bytesOut1);
         ThreadOut t2out = new ThreadOut(gpos2, bytesOut2);
         
         tin.start();
         t1out.start();
         t2out.start();
         
         t1out.join();
         t2out.join();
         Thread.sleep(1000);
         log.info("waitUntilEmpty()");
         vs.waitUntilEmpty();
         log.info("close()");
         vs.close();
         log.info("tin.join()");
         tin.join();
         
         assertTrue(tin.getException() == null);
         assertTrue(t1out.getException() == null);
         assertTrue(t2out.getException() == null);
       
         byte[] bytes = bytesIn1.toByteArray();
         for (int i = 0; i < size; i++)
            assertTrue(Integer.toString(i), bytesOut1[i] == bytes[i]);
         
         bytes = bytesIn2.toByteArray();
         for (int i = 0; i < size; i++)
            assertTrue(bytesOut2[i] == bytes[i]);
      }
      catch (Exception e)
      {
         log.error(e);
         e.printStackTrace();
         fail();
      }
      
      log.info("testVirtualSelector() PASSES");
      OKCounter++;
   }
}

