package hh.dejsem.lastmile;

import android.os.AsyncTask;

import java.io.BufferedInputStream;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.URL;
import java.util.Date;

import hh.dejsem.K;
import hh.dejsem.Prefs;
import hh.dejsem.net.NetData;
import hh.dejsem.net.NetNode;
import hh.dejsem.net.NetScIO;
import hh.lib.D;

public class LastMileMeterRun extends AsyncTask<Void, String, Void> {
	/**----------------------------------------------------------
	 * network operations of last mile throughput meter
	 * ----------------------------------------------------------*/
	D d;
	Exception exception;
	NetScIO nonSslIO = null;
	long t00, t0, td, n, nn;
	public boolean inProgress = true;

	public LastMileMeterRun(D d) {
		this.d = d.klon(this);
	}

	@Override
	protected Void doInBackground(Void... params) {
		if(d.ll(4)) d.l("doInBackground starting...");
		exception = null;
		try {
			if(K.netNode == null) K.netNode = new NetNode(d);
			meter();
		}
		catch(Exception e) { exception = e; }
		if(d.ll(4)) d.l("doInBackground finishing, exc=" + exception);
		return null;
	}

	@Override
	protected void onPostExecute(Void res) {
		if(d.ll(4)) d.l("onPostExecute, exc=" + exception);
		inProgress = false;
		if(exception != null) d.abendMsg(exception);
	}

	protected void onCancelled() { inProgress = false; }

	@Override
	protected final void onProgressUpdate(String... s) { d.ltv(s == null ? null : s[0]); }

	void meter() {
		HttpURLConnection urlConn = null;
		n = 0;
		nn = 0;
		try {
			String srv = Prefs.host;
			publishProgress("open connection to " + srv);
			urlConn = (HttpURLConnection)new URL("http://" + srv + "/hh.1M").openConnection();
			BufferedInputStream in = new BufferedInputStream(urlConn.getInputStream());
			publishProgress("DOWNLOAD stream of 1MB");
			byte[] buf = new byte[8192];
			t00 = new Date().getTime();
			t0 = t00;
			int r;
			while(!isCancelled() && (r = in.read(buf)) > -1) {
				n += r;
				td = new Date().getTime() - t0;
				if(td > 1000 || n > 127*1024) report();
			}
			td = new Date().getTime() - t00;
			publishProgress(String.format("finish, % 5.3f secs. elaspsed, %s", 0.001 * td, rate(nn, td)));

			if(!isCancelled()) {
				publishProgress("UPLOAD stream of 1MB");
				nonSslIO = meterUpConn();
				t00 = new Date().getTime();
				t0 = t00;
				nn = 0;
				n = 0;
				for(int i=0; i < 64; i++) {
					if(isCancelled()) { nonSslIO.closeSc(); break; }
					n += meterUpSend(nonSslIO, 16*1024);
					td = new Date().getTime() - t0;
					if(td > 1000 || n > 127*1024) report();
				}
				td = new Date().getTime() - t00;
				publishProgress(String.format("finish, % 5.3f secs. elaspsed, %s", 0.001 * td, rate(nn, td)));
			}
		}
		catch(Exception e) { this.exception = e; }
		finally {
			if(urlConn != null) urlConn.disconnect();
			if(nonSslIO != null) try { nonSslIO.closeSc(); } catch(Exception e) {}
		}
	}

	NetScIO meterUpConn() throws Exception {
		NetScIO netIO = new NetScIO(d);
		int port = K.BASE_PORT_NUM;
		if(d.ll(3)) d.l("start meter up, host=" + Prefs.host + ", port=" + port);
		netIO.connect(new InetSocketAddress(Prefs.host, port));
		return netIO;
	}

	int meterUpSend(NetScIO netIO, int l) throws Exception {		
		NetData data = new NetData(d, (l < 10 ? 10 : l));
		netIO.put(data.buf);
		netIO.out.flush();
		if(d.ll(5)) d.l(String.format("%d bytes uploaded", l));
		data.buf.rewind();
		data.buf.limit(10);
		while(data.buf.position() < 10) netIO.get(data.buf);
		data.unloadToByte();
		if(new String(data.nb, 0, 2).equals("=>")) return Integer.valueOf(new String(data.nb, 2, 8));
		else throw new Exception("bad number got");
	}

	void report() throws Exception {
		publishProgress(rate(n, td));
		t0 = new Date().getTime();
		nn += n;
		n = 0;
	}

	String rate(long n, long td) { return String.format("% 3.3f MB/s", 1000.0 * n / (1024 * 1024 * td)); }
}