import java.io.*;
import java.net.*;
import java.text.*;
import java.util.*;

class ProxyThread extends Thread {
  Socket mySocket;
  int counter;

  ProxyThread(Socket i, int c) {
    mySocket=i;
    counter=c;
  }

  public void run() {
    String cmd=null;
    String ccmd=null;
    String request=new String();
    DataInputStream in;
    try {
      in=new DataInputStream(mySocket.getInputStream());
      bOut=new BufferedOutputStream(mySocket.getOutputStream());
    }
    catch(Exception e) {
      System.out.println(e);
      return;
    }
    boolean done=false;

    while(!done) {
      String str;
      try{
        str=in.readLine();
      }
      catch(Exception e) {
        System.out.println(e);
        return;
      }
      if(str==null) {
        System.out.println(counter+":Done - aaaaaagh");
        done=true;
      }
      else if(str.trim().equals("exit"))
        done=true;
 // http server
      // received two returns in row, close connection
      else if(str.length()==0) { 
        if(ccmd.equals("GET ")) 
          handleGet(cmd,request);
        else if(ccmd.equals("POST "))
          handlePost(cmd,request,in);
        done=true;
      }
      // received GET ....
      else if(str.length()>3 && str.substring(0,3).equals("GET")) {
        ccmd=new String("GET ");
        str=str.substring(4,str.length());
        cmd=str.substring(0,str.indexOf(' '));
//        System.out.println(counter+":GET "+cmd);
      }
      // received POST ....
      else if(str.length()>4 && str.substring(0,4).equals("POST")) {
        ccmd=new String("POST ");
        str=str.substring(5,str.length());
        cmd=str.substring(0,str.indexOf(' '));
 //       System.out.println(counter+":POST "+cmd);
      }
      else {
        request=request+str+"\r\n";
//          System.out.println(counter+":"+str+":"+str.length());
      }
    }
    try {
      mySocket.close();
System.out.println("closing:"+counter);
    }
    catch(Exception e) {
      System.out.println(e);
    }
  }

  private void handleGet(String cmd, String request) {
    URL myURL=null; 
    try {      // Prepare connection
      myURL=new URL(cmd);
    }
    catch(Exception e) {
      System.out.println(e);
      return;
    }
    String myFile=myURL.getFile();      
    String myCmd="GET "+myFile+" HTTP/1.0";
    int myPort=getPort(myURL.getPort(),myURL.getProtocol());

    // In cache?
    FCache fc=new FCache(myURL.getHost(),myFile,myPort);
    if(fc.isCached()) {
System.out.println(counter+":Its cached!!");
      // ***************** get cached file
      FileInputStream aInStream;
      DataInputStream uin=null;
      try {   
        aInStream=new FileInputStream(fc.getName());
        uin=new DataInputStream(aInStream);
      }
      catch(Exception e) {
        System.out.println(e);
        return;
      }

      byte []buf=new byte[512];
      try {
        int len;   
        while((len=uin.read(buf))>0) {
          bOut.write(buf,0,len);
        }
        bOut.flush();
        uin.close();
      }
      catch(Exception e) {
        System.out.println(e);
        try {
          uin.close();
        }
        catch(Exception ex) {
          System.out.println(ex);
        }
        return;
      }
    }
    else {
      // ***************** get off network
      // send request
      Socket so=null;
      PrintWriter pOut=null;
      DataInputStream uin=null;
      try {
        so=new Socket(myURL.getHost(),myPort);
        pOut=new PrintWriter(so.getOutputStream(),true);
        uin=new DataInputStream(so.getInputStream());
      }
      catch(Exception e) {
        System.out.println(e);
        try {
          File f=new File(fc.getName());
//System.out.println(counter+":Deleting!!!!!!!!!!!!!!");
          f.delete();
        }
        catch(Exception ex) {
          System.out.println(ex);
        }
        return;
      }
      pOut.println(myCmd);
      pOut.println(request);
      pOut.println();

      boolean writeIt=fc.canCache();
      FileOutputStream fOut=null;
      PrintWriter pfOut=null;

      // read header
      PrintWriter pbOut=new PrintWriter(bOut,true);
      String s;
      try {
        s=uin.readLine();
        if(writeIt) {
          if(s.length()>12 && s.substring(0,13).equals("HTTP/1.0 401 ")) {
            writeIt=false;
            File f=new File(fc.getName());
//System.out.println(counter+":Deleting!!!!!!!!!!!!!!");
            f.delete();
          }
          else {
            fOut=new FileOutputStream(fc.getName());
            pfOut=new PrintWriter(fOut,true);
          }
        }
      }
      catch(Exception e) {
        System.out.println(e);
        try {
          File f=new File(fc.getName());
//System.out.println(counter+":Deleting!!!!!!!!!!!!!!");
          f.delete();
        }
        catch(Exception ex) {
          System.out.println(ex);
        }
        return;
      }
      try {
        while(s.length()!=0) { 
//         System.out.println(counter+"data:"+s);
          pbOut.println(s);
          if(writeIt)
            pfOut.println(s);
          s=uin.readLine();
        }
        pbOut.println();
        if(writeIt)
          pfOut.println();

        // read data
        byte []buf=new byte[512];
        int len;
        while((len=uin.read(buf))>0) {
          bOut.write(buf,0,len);
          if(writeIt)
            fOut.write(buf,0,len);
        }
        bOut.flush();
        if(writeIt) {
          fOut.flush();
          fOut.close();
        }
      }
      catch(Exception e) {
        System.out.println(e);
        if(writeIt) {
          try {
            fOut.close();
            File f=new File(fc.getName());
//System.out.println(counter+":Deleting!!!!!!!!!!!!!!");
            f.delete();
          }
          catch(Exception ex) {
            System.out.println(ex);
          }
        }
      }
    }
  }

  private void handlePost(String cmd, String request, 
       DataInputStream in) {
    try {      // Prepare connection
      URL myURL=new URL(cmd);
      String myFile=myURL.getFile();      
      String myCmd="POST "+myFile+" HTTP/1.0";
      int myPort=getPort(myURL.getPort(),myURL.getProtocol());

      Socket so=new Socket(myURL.getHost(),myPort);
      PrintWriter spOut=new PrintWriter(so.getOutputStream(),true);
//      BufferedOutputStream sbOut=
//        new BufferedOutputStream(so.getOutputStream());
      DataInputStream sIn=new DataInputStream(so.getInputStream());

      // send request
      spOut.println(myCmd);
      spOut.println(request);
//      spOut.println();

      // read message to post
      spOut.println(in.readLine());

      // read return data
      byte []buf=new byte[512];
      int len;
      while((len=sIn.read(buf))>0) {
//printBin(buf,len);
        bOut.write(buf,0,len);
      }
      bOut.flush();
    }
    catch(Exception e) {
      System.out.println(e);
    }
  }

  public void printBin(byte [] b, int len) {
    for(int i=0;i<len;i++) {
//      System.out.print(b[i]+" ");
      System.out.print((char)b[i]);
    }
    System.out.println();
  }

//  void copyToStream(DataInputStream in, BufferedOutputStream out) {
//      byte []buf=new byte[512];
//      int len;
//      while((len=in.read(buf))>0) {
//        out.write(buf,0,len);
//      }
//      out.flush();
//  }

  private int getPort(int port,String protocol) {
    if(port<0) {
      if(protocol.equals("http"))
        port=80;
      if(protocol.equals("ftp"))
        port=21;
      else
        port=80;
    }
    return port;
  }

  private BufferedOutputStream bOut;
}

class ProxyServer {
  public static void main(String[] args) {
    int i=1;
    try {
      ServerSocket s=new ServerSocket(8080);
      while(true) {
        Socket aSocket=s.accept();
        System.out.println("starting "+i);
        new ProxyThread(aSocket,i).start();
        i++;
      }
    }
    catch (Exception e) {
      System.out.println(e);
    }
  }
}

