package com.algobase.share.network;

import java.io.InputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import java.util.Timer;
import java.util.TimerTask;

import java.net.HttpURLConnection;
import java.net.URL;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

import java.security.cert.X509Certificate;
import java.security.cert.CertificateException;

import android.webkit.URLUtil;

import android.util.Base64;
import android.util.Log;



public class HttpsClient {

   public class HttpsResult {

     byte[] byteArray; 
     boolean errorFlag;
     String errorText;

     public HttpsResult() { 
       byteArray = null;
       errorFlag = false;
       errorText = "";
     }

     public byte[] getBytes()  { return byteArray; }

     public String getString() { 
           return (byteArray == null) ? null : new String(byteArray); 
     }

     public boolean hasError()  { return errorFlag; }
     public String getErrorText() { return errorText; }
   }


   public static class OnProgressListener {
       public void onProgress(int kb) {}
   }

   OnProgressListener onProgressListener = null;

   public void show_toast(String s) {}

   String hostName;
   String portName;

   String user;
   String password;

   String requestType = "GET";
   String headerName;
   String headerValue;

   boolean useSSL;

   int connectTimeout = 5000;
   //int readTimeout = 5000;
   int readTimeout = 10000;

   String userAgent = null;

   String errorMsg = null;

   //boolean canceled = false;
   int limit = -1;

   public void showProgress(int kb) {}


   public HttpsClient() { init(null,null,false); } 

   public HttpsClient(String host, String port, boolean ssl) { 
    init(host,port,ssl); 
   }


   void init(String host, String port, boolean ssl) 
   {
     hostName = host;
     portName = port;
     useSSL = ssl;

  // trust all certificates
  // create a trust manager that does not validate certificate chains

    X509TrustManager trustAllManager = new X509TrustManager() {

     public X509Certificate[] getAcceptedIssuers() {
         return new X509Certificate[] {};
     }

     public void checkClientTrusted(X509Certificate[] chain, String authType)
                                                 throws CertificateException {
     }

     public void checkServerTrusted(X509Certificate[] chain, String authType)
                                                 throws CertificateException {
     }
   }; 

  TrustManager[] mgrs = new TrustManager[] { trustAllManager };

  // install the all-trusting trust manager
  try { SSLContext sc = SSLContext.getInstance("TLS");
        sc.init(null, mgrs, new java.security.SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
  } catch (Exception e) {}


  // trust all hosts
    HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
          public boolean verify(String hostName, SSLSession session) {
                    return true;
          }
    });
  }

  public void setConnectTimeout(int t) { connectTimeout = t; }
  public void setReadTimeout(int t) { readTimeout = t; }

  public int getConnectTimeout() { return connectTimeout; }
  public int getReadTimeout() { return readTimeout; }

  public void setTimeout(int t) { connectTimeout = readTimeout = t; }

  public void setHost(String h) { hostName = h; }
  public void setPort(String p) { portName = p; }
  public void setSSL(boolean b) { useSSL = b; }

  public void setRequestType(String s) { requestType = s; }

  public void setUserAgent(String agent) { userAgent = agent; }

  public void setHeader(String name, String value)
  { headerName = name;
    headerValue = value;
   }

  public void setCredentials(final String usr, final String pass) 
  { user = usr;
    password = pass;
  }


  public String urlString(String uri)
  { 
    if (uri.length() > 0 && !uri.startsWith("/")) uri = "/" + uri;

    if (useSSL)
       return "https://" + hostName + ":" + portName + uri;
    else
       return "http://" + hostName + ":" + portName + uri;
   }


  public HttpsResult loadFile(String uri, File file)  { 
     return loadPage(uri,file); 
  }

  public HttpsResult loadFile1(String uri, File file)  { 
     return loadPage(uri,file); 
  }

  public HttpsResult loadString(String uri)  { 
     HttpsResult result =  loadPage(uri,null); 
     if (result.errorFlag == false && result.byteArray == null)
     { result.errorFlag = true;
       result.errorText = "Http: Empty result.";
      }
     return result;
  }


  public HttpsResult loadPage(String uri, File file) 
  {
    String url_str = urlString(uri);
    return loadPage1(url_str,file);
  }


  public HttpsResult loadPage1(String url_string, File file) 
  {

    final HttpsResult result = new HttpsResult();

/*
    String url_string = urlString(uri);
*/

    if (!URLUtil.isValidUrl(url_string))
    { result.errorFlag = true;
      result.errorText = "Invalid URL: " + url_string;
      return result;
    }

    try {

      URL url = new URL(url_string);
      final HttpURLConnection connection = 
                                  (HttpURLConnection)url.openConnection();

      if (connection == null)
      { result.errorFlag = true;
        result.errorText = "connection: null";
        return result;
      }

      connection.setConnectTimeout(connectTimeout);
      connection.setReadTimeout(readTimeout);

/*
      connection.setUseCaches(false);
      connection.setDoOutput(true);
      connection.setDoInput(true);
*/

      // required ?
      //connection.setRequestMethod("POST");
      //connection.setRequestMethod("GET");

      connection.setRequestMethod(requestType);

      if (userAgent != null)
        connection.setRequestProperty("User-Agent",userAgent);

      if (headerName != null)
        connection.setRequestProperty(headerName,headerValue);


      // authentication
      if (user != null && password != null)  {
          String s = user + ":" + password;
          String b64 = Base64.encodeToString(s.getBytes(), Base64.NO_WRAP);
          connection.setRequestProperty("Authorization", "Basic " + b64);
       }

      int code = connection.getResponseCode();

      if (code != 200) 
       { result.errorFlag = true;
         result.errorText = url_string + "\n" + connection.getResponseMessage();
         connection.disconnect();
        }
      else
        { InputStream is = connection.getInputStream();

          byte[] read_buf = new byte[1024];
          FileOutputStream os = null;

          //ByteArrayBuffer byte_buf = null;
          byte[] byte_buf =  null;

          if (file != null) 
             os = new FileOutputStream(file);
          else
             //byte_buf = new ByteArrayBuffer(50);
             byte_buf = new  byte[1024];

          int count = 0;
          int n = 0;

          //while (!canceled && (n = is.read(read_buf)) != -1) 
          while ((limit < 0 || count < limit) && (n = is.read(read_buf)) != -1) 
          { 
            if (os == null)
            { //byte_buf.append(read_buf,0,n);
              if (count+n > byte_buf.length)
              { byte[] tmp = new byte[2*byte_buf.length];
                //for(int i=0; i<count; i++) tmp[i] = byte_buf[i];
                System.arraycopy(byte_buf,0,tmp,0,count);
                byte_buf = tmp;
               }
               //for(int i=0; i<n; i++) byte_buf[count+i] = read_buf[i];
               System.arraycopy(read_buf,0,byte_buf,count,n);
               count += n;
             }
            else
            { os.write(read_buf,0,n);
              count += n;
              if (onProgressListener != null)
              { int kb = (int)(count/1024);
                onProgressListener.onProgress(kb);
               }
             }

           }

           if (os != null) 
              os.close();
           else
           { //result.byteArray = byte_buf.toByteArray();
             result.byteArray = new byte[count];
             System.arraycopy(byte_buf,0,result.byteArray,0,count);
            }

           is.close();

           connection.disconnect();
         }

     } catch (Exception e) { result.errorFlag = true;
                             //result.errorText = e.toString(); 
                             result.errorText = e.getClass().getName(); 
                            }


     return result;
  }

  public void set_limit(int x) { limit = x; }

//public void cancel() { canceled = true; }

  public void setOnProgressListener(OnProgressListener listener) 
  { onProgressListener = listener; }



/*
  public byte[] getBytes() { return byteArray; }
  public String getString() { return new String(byteArray); }

  public boolean hasError() { return errorFlag; }
  public String getError() { return urlString("") + "\n\n" + errorText; }

  public boolean writeToFile(File file)
  { boolean result = true;
    try {
       FileOutputStream os = new FileOutputStream(file);
       os.write(byteArray);
       os.close();
     } catch (IOException ex) { result = false; }
    return result;
   }
*/

  // for compatibility to HttpClient


   public HttpsClient(String url, int timeout) { 
    // store url in hostName
    init(url,"443",false); 
    setTimeout(timeout);
   }

  public boolean getFile(File file) 
  {
    HttpsResult result = loadPage1(hostName,file);

    if (result.hasError())
    { errorMsg = result.getErrorText(); 
      return false;
     }

    return true;
  }

  public String getError() { return errorMsg; }

}
