// ** This class was generated with DemFGen (vers:11/17/2009)

namespace edu.neu.ccs.demeterf.http.classes{

using edu.neu.ccs.demeterf.lib;
using System.Net.Sockets;
using System.Text;
using System.IO;

using System;



/** Representation of HTTPReq */
public class HTTPReq{
    protected readonly HTTPHead head;
    protected readonly List<MsgHead> keys;
    protected readonly ident body;

    /** Construct a(n) HTTPReq Instance */
    public HTTPReq(HTTPHead head, List<MsgHead> keys, ident body){
        this.head = head;
        this.keys = keys;
        this.body = body;
    }
    /** Is the given object Equal to this HTTPReq? */
    public override bool Equals(Object o){
        if(!(o is HTTPReq))return false;
        if(o == this)return true;
        HTTPReq oo = (HTTPReq)o;
        return (head.Equals(oo.head))&&(keys.Equals(oo.keys))&&(body.Equals(oo.body));
    }

    /** Field Class for HTTPReq.head */
    public class headF : edu.neu.ccs.demeterf.Fields.any{}
    /** Field Class for HTTPReq.keys */
    public class keysF : edu.neu.ccs.demeterf.Fields.any{}
    /** Field Class for HTTPReq.body */
    public class bodyF : edu.neu.ccs.demeterf.Fields.any{}

    /** Default Socket connection timeout (20 seconds) */
    public static readonly int DEFAULT_CONN_TIMEOUT = 20*1000;
    /** Default Socket Response timeout (1 second) */
    public static readonly int DEFAULT_RESP_TIMEOUT = 1000;

    /** Create an HTTP Request with a given Header, MessageHeaders, and Body */
    public static HTTPReq create(HTTPHead req, List<MsgHead> hds, String body){
        return createNoLen(req, hds.append(new MsgHead("Content-Length",""+body.Length)), body);
    }
    /** Create an HTTP Request with a given Header, MessageHeaders, and Body */
    private static HTTPReq createNoLen(HTTPHead req, List<MsgHead> hds, String body){
        return new HTTPReq(req, hds, new ident(body));
    }

    /** Create an HTTP Get Request for the given (relative) URL. Then use
     *    {@link #send(String,int) Send} for a Request/Response pair. */
    public static HTTPReq Get(String url) {
        return create(HTTPHead.Get(URL.Parse(url)), List<MsgHead>.create(), "");
    }
    /** Create an HTTP Get Request for the given relative URL, to be sent to the
     *    given Host.  Use this method for raw Socket/Connection sends, but use
     *    {@link #send(String,int) Send} for a Request/Response pair. */
    public static HTTPReq Get(String url, String host) {
        return create(HTTPHead.Get(URL.Parse(url)), hostHeader(host), "");
    }
    /** Create an HTTP Head Request for the given (relative) URL. Then use
     *    {@link #send(String,int) Send} for a Request/Response pair. */
    public static HTTPReq Head(String url) {
        return create(HTTPHead.Get(URL.Parse(url)), List<MsgHead>.create(), "");
    }
    /** Create an HTTP Head Request for the given relative URL, to be sent to the
     *    given Host.  Use this method for raw Socket/Connection sends, but use
     *    {@link #send(String,int) Send} for a Request/Response pair. */
    public static HTTPReq Head(String url, String host) {
        return create(HTTPHead.Head(URL.Parse(url)), hostHeader(host), "");
    }
    /** Create an HTTP Post Request to the given (relative) URL. Then use
     *    {@link #send(String,int) Send} for a Request/Response pair. */
    public static HTTPReq Post(String url, String body) {
        return create(HTTPHead.Post(URL.Parse(url)), List<MsgHead>.create(), body);
    }
    /** Create an HTTP Post Request to the given relative URL, to be sent to the
     *    given Host.  Use this method for raw Socket/Connection sends, but use
     *    {@link #send(String,int) Send} for a Request Response Pair. */
    public static HTTPReq Post(String url, String host, String body) {
        return create(HTTPHead.Post(URL.Parse(url)), hostHeader(host), body);
    }

    private static List<MsgHead> hostHeader(String h)
    { return List<MsgHead>.create(new MsgHead(new ident("Host"), new ident(h))); }
    
    /** Add the given Message Headers to this Request */
    private HTTPReq addHeaders(List<MsgHead> hs){
          return createNoLen(head,hs.append(keys),""+body);
    }
    /** Add a given Message Headers to this Request */
    public HTTPReq addHeader(String key, String val){
          return createNoLen(head,keys.append(new MsgHead(key,val)),""+body);
    }
    
    /** Request Types */
    public enum ReqType{ GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT, OPTIONS, OTHER };
    /** Return this Request's type */
    public ReqType getType(){ return head.getType(); }
    
    /** Return the URL arguments (key/value) for this request.  The URL arguments
     *    follow the base URL after a '?'.
     *    E.g.: <i>/foo/bar?bazzle=13&wizwoz='hello'</i>
     */
    public Map<String,String> urlArgs(){ return head.GetUrl().urlArgs(); }
    /** Return the Body arguments (key/value) from this request.  The Body arguments
     *    are on a single line of the body, like URLArgs, but without the '?'
     */
    public Map<String,String> bodyArgs(){ return splitArgs(""+body); }
    /** Return the relative URL, without any URL arguments */
    public String trimmedUrl(){ return head.GetUrl().trimArgs(); }
    
    /** Send this Request to the given Server/Port, and return its Response */
    public HTTPResp send(String server, int port){
        return send(server,port,0);
    }
    /** Send this Request to the given Server/Port, and return its Response with
     *    the given response timeout.  If the server does not respond in the given
     *    time an */
    public HTTPResp send(String server, int port, int respTimeout){
        return send(server,port,HTTPReq.DEFAULT_CONN_TIMEOUT, respTimeout);
    }
    /** Send this Request to the given Server/Port, and return its
     *    Response, but only wait for the specified timout (in Milliseconds) */
    public HTTPResp send(String server, int port, int connTimeout, int respTimeout) {
        Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        // Connect
        System.Net.IPHostEntry host = System.Net.Dns.GetHostEntry(server);
        sock.Connect(new System.Net.IPEndPoint(host.AddressList[0], port));
        // Add the header and Send
        addHeaders(hostHeader(server+":"+port)).toSocket(sock);
        // Read the Response
        HTTPResp res = HTTPResp.fromSocket(sock);
        sock.Close();
        return res;
    }
    /** Write this Request to the given Socket */
    public void toSocket(Socket s){
        try{
            StreamWriter outt = new StreamWriter(new NetworkStream(s));
            outt.Write(this.ToString());
            outt.Flush();
            s.Shutdown(SocketShutdown.Send);
        }catch(Exception e){ throw e; }
    }
    /** Read a request from a Socket, timeout if needed */
    public static HTTPReq fromSocket(Socket s){
        return fromSocket(s,0);
    }
    /** Read a request from a Socket */
    public static HTTPReq fromSocket(Socket s, long timeout){
        try{
            // Recieve Timer
            //s.ReceiveTimeout = HTTPReq.DEFAULT_RESP_TIMEOUT;

            s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, HTTPReq.DEFAULT_RESP_TIMEOUT);
            
            HTTPReq req = fromInputStream(new NetworkStream(s),timeout);
            s.Shutdown(SocketShutdown.Receive);
            return req;
        }catch(Exception e){ throw e; }
    }
    /** Read a Request from an InputStream */
    private static HTTPReq fromInputStream(Stream inpt){
        return fromInputStream(inpt,0);
    }
    /** Read a Request from an InputStream */
    public static HTTPReq fromInputStream(Stream inpt, long timeout){
        try{
            StreamReader inn = new StreamReader(inpt);
            String first = readLine(inn,timeout);
            if(first == null)throw new Exception("Empty HTTP Header");
            HTTPHead h = HTTPHead.Parse(first);
            return new HTTPReq(h, ParseMsgHeads(inn,timeout), ParseBody(inn,timeout));
        }catch(Exception e){ throw e; }
    }
    /** Parse a List of Message Headers */
    public static List<MsgHead> ParseMsgHeads(StreamReader inn, long timeout){
        String line = null;
        if((line = inn.ReadLine()) == null || line.Length == 0)
            return List<MsgHead>.create();
        int colon = line.IndexOf(":");
        return ParseMsgHeads(inn,timeout)
            .push(new MsgHead(new ident(line.Substring(0,colon)),
                              new ident(line.Substring(colon+2))));
    }
    /** Read a single Line */
    public static String readLine(StreamReader inn, long timeout){
        long start = millis();
        String line = "";
        while(true){
            line = null;
            try{
                line = inn.ReadLine();
                return line;
            }catch(IOException e){
                if(timeout > 0 && (millis()-start) > timeout)
                    throw e;
            }
        }
    }
    
    static long millis(){
        return System.DateTime.Now.Ticks/10000;
    }

    /** Parse the Body of a Request */
    public static ident ParseBody(StreamReader inn, long timeout){
        long start = millis();
        StringBuilder sb = new StringBuilder();
        char[] buff = new char[1024];
        int many = 0;
        if(!inn.EndOfStream)
        do{
            many = 0;
            try{
                many = inn.Read(buff,0,1024);
                if(many>0)
                    sb.Append(buff,0,many);
            }catch(IOException e){
                if(timeout > 0 && (millis()-start) > timeout)
                    throw e;
            }
        }while(!inn.EndOfStream || many > 0);
        return new ident(sb.ToString());
    }
    /** Return the Body of this Request */
    public String getBodyString(){ return ""+body; }
    /** Return the Headers of this Request as a Map */
    public Map<String,String> getHeaders(){ return getHeaders(keys); }


    class HeadFold : List<MsgHead>.Fold<Map<String,String>>{
        public override Map<String,String> fold(MsgHead h, Map<String,String> m){
            return m.put(""+h.GetKey(),""+h.GetValue());
        }
    }
    /** Return the Headers of this Request as a Map */
    public static Map<String,String> getHeaders(List<MsgHead> hds){
        return hds.fold(new HeadFold(), Map<String,String>.create<String>());
    }
    
    class SplitFold : List<String>.Fold<Map<String,String>>{
        public override Map<String,String> fold(String p, Map<String,String> m){
            String[] kv = p.Split('=');
            if(kv.Length < 2)return m;
            return m.put(kv[0],kv[1]);
        }
    }
    /** Split an argument String into key/value Map */
    public static Map<String,String> splitArgs(String s){
        return List<String>.create(s.Split('&')).fold(new SplitFold(), Map<String,String>.create<String>());
    }

    /** Decode an encoded URL */
    public static String decodeURL(String url){
        return Uri.UnescapeDataString(url);
    }
    /** Encode a (possibly special) URL */
    public static String encodeURL(String url){
        return Uri.EscapeDataString(url);
    }

    /** DGP method from Class PrintToString */
    public override String ToString(){ return global::edu.neu.ccs.demeterf.http.classes.PrintToString.PrintToStringM(this); }
    /** Getter for field HTTPReq.body */
    public ident GetBody(){ return body; }
    /** Getter for field HTTPReq.keys */
    public List<MsgHead> GetKeys(){ return keys; }
    /** Getter for field HTTPReq.head */
    public HTTPHead GetHead(){ return head; }

}
}