package io.oversky524.myhttp;

import java.io.*;
import java.net.Socket;
import java.util.Map;

/**
 * Created by gaochao on 2016/4/6.
 */
public class MyHttpClient {
    private ConnectionPool mPool = new ConnectionPool();

    private MyHttpClient(Builder builder){
    }

    public Response execute(Request request){
        final String host = request.getHost();
        final int port = request.getPort();
        Connection connection = mPool.getConnection(host, port);
        System.out.println(connection);
        Response response = connection.put(request);
        try{
            Socket socket = connection.getSocket();
            writeRequest(request, socket);
            parseResponse(socket, response);
            if(DEBUG){
                Map<String, String> headers = response.getHeaders();
                for (String key : headers.keySet()){
                    System.out.println(key + ": " + headers.get(key));
                }
            }
            response.setInputStream(new ReleaseConnectionInputStream(connection, request));
        } catch (IOException e) {
            e.printStackTrace();
            response.setErrorThrowable(e);
        }
        return response;
    }

    private static void writeRequest(Request request, Socket socket) throws IOException {
        BufferedOutputStream outputStream = new BufferedOutputStream(socket.getOutputStream());
        outputStream.write(request.getHeaders().getBytes());
        outputStream.write(request.getBody());
        outputStream.flush();
    }

    private static void parseResponse(Socket socket, Response response) throws IOException {
        PushbackInputStream inputStream = new PushbackInputStream(socket.getInputStream());
        int readByte = 0;
        boolean firstLine = true;
        StringBuffer sb = new StringBuffer();
        while ((readByte = inputStream.read()) != -1){
            if(readByte == '\r'){//正常情况下，一个header应该以\r\n结尾
                inputStream.read();//consume '\n'
                if(firstLine){
                    firstLine = false;
                    parseResponseLine(sb.toString(), response);
                }else{
                    HeaderUtils.parseHeader(sb.toString(), response);
                }
                sb.setLength(0);
                readByte = inputStream.read();
                if(readByte == '\r'){
                    inputStream.read();//consume '\n'
                    break;
                }else{
                    inputStream.unread(readByte);//push back
                }
            }else if(readByte == '\n'){//有时，一个header也以\n结尾
                if(firstLine){
                    firstLine = false;
                    parseResponseLine(sb.toString(), response);
                }else{
                    HeaderUtils.parseHeader(sb.toString(), response);
                }
                sb.setLength(0);
                readByte = inputStream.read();
                if(readByte == '\n'){
                    break;
                }else{
                    inputStream.unread(readByte);//push back
                }
            }else{
                sb.append((char)readByte);
            }
        }
    }

    private static void parseResponseLine(String line, Response response){
        int index = line.indexOf(' ');
        String scheme = line.substring(0, index);
        response.setVersion(scheme.substring(scheme.indexOf('/') + 1));
        while (line.charAt(++index) == ' ');
        String code = line.substring(index, line.indexOf(' ', index));
        response.setStatusCode(Integer.valueOf(code));
    }

    private static final boolean DEBUG = true;

    public static class Builder{
        /*private ConnectionPool connectionPool;

        public Builder setConnectionPool(ConnectionPool pool){
            connectionPool = pool;
            return this;
        }*/

        public MyHttpClient build(){ return new MyHttpClient(this); }
    }

    private static class ReleaseConnectionInputStream extends FilterInputStream{
        private Connection mConnection;
        private Request mRequest;

        ReleaseConnectionInputStream(Connection connection, Request request) throws IOException {
            super(connection.getSocket().getInputStream());
            mConnection = connection;
            mRequest = request;
        }

        @Override
        public int read() throws IOException {
            if(mConnection == null) return -1;
            return release(super.read());
        }

        @Override
        public int read(byte[] b) throws IOException {
            if(mConnection == null) return -1;
            return release(super.read(b));
        }

        @Override
        public int read(byte[] b, int off, int len) throws IOException {
            if(mConnection == null) return -1;
            return release(super.read(b, off, len));
        }

        private int release(int result){
            if(result == -1){
                mConnection.release(mRequest);
                mConnection = null;
            }
            return result;
        }
    }
}

@@ -1454,32 +1454,90 @@
 ytes());%0A       
+ byte%5B%5D body = request.getBody();%0A        if(body != null)
  outputStream.wr
@@ -1540,33 +1540,20 @@
 m.write(
-request.getB
+b
 ody
-()
 );%0A     

fix NullPointerException when there is no request body