/*
 * Copyright 2005 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */

package org.apache.jmeter.util;

import java.io.FilterInputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * "Slow" socket implementation to emulate dial-up modems etc
 */
public class SlowSocket extends Socket {

    private final int CPS; // Characters per second to emulate
    
    /**
     * 
     * @param cps characters per second
     * @param host hostname
     * @param port port
     * @param localAddr local address
     * @param localPort local port
     * 
     * @throws IOException
     * @throws IllegalArgumentException if cps <=0
     */
    public SlowSocket(int cps, String host, int port, InetAddress localAddr, int localPort) throws IOException {
        super(host, port, localAddr, localPort);
        if (cps <=0) {
            throw new IllegalArgumentException("Speed (cps) <= 0");
        }
        CPS=cps;
    }

    /**
     * 
     * @param cps characters per second
     * @param host hostname
     * @param port port
     * 
     * @throws UnknownHostException
     * @throws IOException
     * @throws IllegalArgumentException if cps <=0
     */
    public SlowSocket(int cps, String host, int port) throws UnknownHostException, IOException {
        super(host, port);
        if (cps <=0) {
            throw new IllegalArgumentException("Speed (cps) <= 0");
        }
        CPS=cps;
    }

    // Override so we can intercept the stream
    public OutputStream getOutputStream() throws IOException {
        return new SlowOutputStream(super.getOutputStream(),CPS);
    }
    
    // Override so we can intercept the stream
    public InputStream getInputStream() throws IOException {
        return new SlowInputStream(super.getInputStream(),CPS);
    }

    // Conversions for milli and nano seconds
    private static final int MS_PER_SEC = 1000;
    private static final int NS_PER_SEC = 1000000000;
    private static final int NS_PER_MS  = NS_PER_SEC/MS_PER_SEC;
    
    private static void pause(int bytes, int cps){
    	long sleepMS = (bytes*MS_PER_SEC)/cps;
    	int  sleepNS = (int)(((bytes*MS_PER_SEC)/cps) % NS_PER_MS);
        try {
            Thread.sleep(sleepMS,sleepNS);
        } catch (InterruptedException ignored) {
        }
    }
    
    private class SlowInputStream extends FilterInputStream {

    	private final int CPS;
    	
        public SlowInputStream(InputStream in, int cps) {
            super(in);
            CPS=cps;
        }

        public int read() throws IOException {
            pause(1,CPS);
            return in.read();
        }

        public int read(byte[] b, int off, int len) throws IOException {
            pause(len,CPS);
            return in.read(b, off, len);
        }

    }
    
    private class SlowOutputStream extends FilterOutputStream {

    	private final int CPS;
    	
        public SlowOutputStream(OutputStream out, int cps) {
            super(out);
            CPS=cps;
        }

        public void write(byte[] b, int off, int len) throws IOException {
            pause(len,CPS);
            out.write(b, off, len);
        }

        public void write(int b) throws IOException {
            pause(1,CPS);
            out.write(b);
        }
    }
}
@@ -2284,31 +2284,27 @@
 tputStream()
-,CPS
 );%0A
+
     %7D%0A    %0A 
@@ -2467,20 +2467,16 @@
 Stream()
-,CPS
 );%0A    %7D
@@ -2707,23 +2707,16 @@
 private 
-static 
 void pau
@@ -2727,25 +2727,16 @@
 nt bytes
-, int cps
 )%7B%0A    %09
@@ -2769,19 +2769,19 @@
 ER_SEC)/
-cps
+CPS
 ;%0A    %09i
@@ -2798,16 +2798,10 @@
 S = 
-(int)
 ((
-(
 byte
@@ -2814,19 +2814,19 @@
 ER_SEC)/
-cps
+CPS
 ) %25 NS_P
@@ -2830,17 +2830,16 @@
 S_PER_MS
-)
 ;%0A      
@@ -3026,42 +3026,8 @@
  %7B%0A%0A
-    %09private final int CPS;%0A    %09%0A
     
@@ -3067,25 +3067,16 @@
 tream in
-, int cps
 ) %7B%0A    
@@ -3094,37 +3094,16 @@
 er(in);%0A
-            CPS=cps;%0A
         
@@ -3163,36 +3163,32 @@
          pause(1
-,CPS
 );%0A            r
@@ -3301,36 +3301,32 @@
        pause(len
-,CPS
 );%0A            r
@@ -3444,42 +3444,8 @@
  %7B%0A%0A
-    %09private final int CPS;%0A    %09%0A
     
@@ -3492,17 +3492,8 @@
  out
-, int cps
 ) %7B%0A
@@ -3520,29 +3520,8 @@
 t);%0A
-            CPS=cps;%0A
     
@@ -3623,20 +3623,16 @@
 ause(len
-,CPS
 );%0A     
@@ -3719,32 +3719,32 @@
 s IOException %7B%0A
+
             paus
@@ -3750,12 +3750,8 @@
 se(1
-,CPS
 );%0A 

Tidy up a bit