package cn.bran.japid.perf;

import japidviews.templates.AllPost;
import japidviews.templates.Posts;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import models.japidsample.Author;
import models.japidsample.Post;

import org.junit.Test;

import cn.bran.japid.template.RenderResult;

public class PerfTests {
	/**
	 * this is the baseline
	 * 
	 * @throws UnsupportedEncodingException
	 * @throws IOException
	 */
	@Test
	public void testSimpleJavaPerf() throws UnsupportedEncodingException, IOException {
		List<Post> posts = createPosts();

		ByteArrayOutputStream baos = new ByteArrayOutputStream(10000);
		// warm up
		for (int i = 0; i < 15; i++) {
			// a simple loop without templating
			baos.reset();
			StringBuffer sb = new StringBuffer(1000);
			for (Post pp : posts) {
				sb.append("你");
				sb.append("Title: " + pp.getTitle());
				sb.append("Author name: " + pp.getAuthor().name);
				sb.append(pp.getAuthor().gender);
				sb.append(pp.getPostedAt());
				sb.append("\n" + "	the real title: 你好\n" + "");
			}
			String string = sb.toString();
			baos.write(string.getBytes("UTF-8"));
		}

		long tt = System.currentTimeMillis();
		for (int i = 0; i < 100; i++) {
			// a simple loop without templating
			baos.reset();
			long t = System.currentTimeMillis();
//			StringBuffer sb = new StringBuffer(1000);
			StringBuilder sb = new StringBuilder(1000);
//			StringBundler sb = new StringBundler();
			for (Post pp : posts) {
				sb.append("你");
				sb.append("Title: " + pp.getTitle());
				sb.append("Author name: " + pp.getAuthor().name);
				sb.append(pp.getAuthor().gender);
				sb.append(pp.getPostedAt());
				sb.append("\n" + "	the real title: 你好\n" + "");
			}
			String string = sb.toString();
			baos.write(string.getBytes("UTF-8"));
			System.out.println(System.currentTimeMillis() - t);
		}
		System.out.println("total time: " + (System.currentTimeMillis() - tt));
	}

	@Test
	public void testJapid() throws InterruptedException {
		List<Post> posts = createPosts();
		// AllPost te = new AllPost(new
		// org.apache.commons.io.output.ByteArrayOutputStream());
		AllPost te = new AllPost(new StringBuilder(8000));
		long t = System.currentTimeMillis();
		long tt = System.currentTimeMillis();
		for (int i = 0; i < 100; i++) {
			// System.out.println("run templating: " + i);
			// StringWriter out = new StringWriter(1000);
			// PrintWriter printWriter = new PrintWriter(out);
			te = new AllPost(new StringBuilder(1000));
			t = System.currentTimeMillis();
			te.render("抬头", posts);
			System.out.println(System.currentTimeMillis() - t);
			// System.out.println(out.toString());
			// Thread.sleep(5);
		}
		System.out.println("total time: " + (System.currentTimeMillis() - tt));
	}

	/**
	 * @return
	 */
	private static List<Post> createPosts() {
		Author a = new Author();
		a.name = "作者";
		a.birthDate = new Date();
		a.gender = 'm';

		final Post p = new Post();
		p.setAuthor(a);
		p.setContent("这是一个很好的内容开始和结束都很好. ");
		p.setPostedAt(new Date());
		p.setTitle("测试");

		List<Post> posts = new ArrayList<Post>();

		for (int i = 0; i < 500; i++) {
			posts.add(p);
		}
		return posts;
	}

	@Test
	public void simleRun() throws UnsupportedEncodingException {
		Author a = new Author();
		a.name = "作者";
		a.birthDate = new Date();
		a.gender = 'm';

		final Post p = new Post();
		p.setAuthor(a);
		p.setContent("这是一个很好的内容开始和结束都很好. ");
		p.setPostedAt(new Date());
		p.setTitle("测试");

		List<Post> posts = new ArrayList<Post>();

		for (int i = 0; i < 5; i++) {
			posts.add(p);
		}

		long t = System.currentTimeMillis();
		RenderResult r = new AllPost(null).render("抬头", posts);
		System.out.println(System.currentTimeMillis() - t);
		System.out.println(r.getContent().toString());
	}


	@Test
	public void testSinglePageJapid() throws Exception {
		final List<Post> posts = createPosts();
		// execute the template
		long tt = System.currentTimeMillis();
		ByteArrayOutputStream baos = new ByteArrayOutputStream(10000);
		Posts te = new Posts();
		te.render("抬头", posts);
		te = new Posts();
		te.render("抬头", posts);
		te = new Posts();
		te.render("抬头", posts);
		baos.write(te.toString().getBytes("UTF-8"));
		te = new Posts();
		te.render("抬头", posts);
		baos.write(te.toString().getBytes("UTF-8"));
		baos.write(te.toString().getBytes("UTF-8"));
		for (int i = 0; i < 100; i++) {
			baos.reset();
			long t = System.currentTimeMillis();
			// System.out.println("run templating: " + i);
			/* Merge data-model with template */

			// StringWriter out = new StringWriter(1000);
			// PrintWriter printWriter = new PrintWriter(out);
			te = new Posts();
			te.render("抬头", posts);
			baos.write(te.toString().getBytes("UTF-8"));
			// System.out.println(out.toString());
			// out.flush();
			System.out.println(System.currentTimeMillis() - t);
			// System.out.println(out.toString());
			// Thread.sleep(5);
		}
		System.out.println("total time: " + (System.currentTimeMillis() - tt));

	}

	// @Test
	// public void testFreeMarker() throws Exception {
	// final List<Post> posts = createPosts();
	// Map<String, Object> m = new HashMap<String, Object>() {
	// {
	// put("posts", posts);
	// }
	// };
	//
	// /* ------------------------------------------------------------------- */
	// /* You should do this ONLY ONCE in the whole application life-cycle: */
	//
	// /* Create and adjust the configuration */
	// Configuration cfg = new Configuration();
	// cfg.setDirectoryForTemplateLoading(new
	// File("D:\\eclipse-workspace\\Japid\\tests\\"));
	// cfg.setObjectWrapper(new DefaultObjectWrapper());
	//
	// /* ------------------------------------------------------------------- */
	// /* You usually do these for many times in the application life-cycle: */
	//
	// /* Get or create a template */
	// Template temp = cfg.getTemplate("posts.ftl");
	//
	// // execute the template
	// long tt = System.currentTimeMillis();
	// for (int i = 0; i < 100; i++) {
	// long t = System.currentTimeMillis();
	// // System.out.println("run templating: " + i);
	// /* Merge data-model with template */
	// Writer out = new StringWriter();
	// temp.process(m, out);
	// // System.out.println(out.toString());
	// // out.flush();
	// System.out.println(System.currentTimeMillis() - t);
	// // System.out.println(out.toString());
	// // Thread.sleep(5);
	// }
	// System.out.println("total time: " + (System.currentTimeMillis() - tt));
	//
	// }

	/**
	 * one od the idea to improve template performace is to compile the static
	 * content as UTF-8 encoded byte array instead of keep as string, which
	 * eventually will be converted to bytearray at runtime.
	 * 
	 * initial test show that 1000 iterations of convert a 40 chinese char
	 * string to byte array costs about 12 ms whereas direct array write costs
	 * only 2 ms, a 6 folds better performance. With ascii chars the gap is
	 * about 4 folds.
	 * 
	 * @throws IOException
	 * 
	 */
	@Test
	public void testByteArrayVsString() throws IOException {
		// String ss = "asdfasdfasdfasdfasdfasdfasdfasdf";
		String ss = "仰望着天空寻找一位失去的故友悄无声息的离开了也带上了命运";

		byte[] ba = ss.getBytes("UTF-8");

		long t1 = 0;
		long t2 = 0;

		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		// warm up
		baos.write(ba);
		baos.write(ba);
		baos.write(ba);
		baos.write(ba);
		Charset UTF8 = Charset.forName("UTF-8");
		for (int i = 0; i < 1000; i++) {
			{
				baos.reset();
				long t0 = System.nanoTime();
				baos.write(ba);
				t1 += System.nanoTime() - t0;
			}
			{
				baos.reset();
				long t0 = System.nanoTime();
				// baos.write(ss.getBytes(UTF8));
				baos.write(ss.getBytes("UTF-8")); // seems to be faster than
				// using the Charset object?
				t2 += System.nanoTime() - t0;
			}
		}
		System.out.println("raw byte copy took nano: " + t1);
		System.out.println("String to bytes took nano: " + t2);

		List<byte[]> statics = new ArrayList<byte[]>();
		statics.add(new byte[] { 12, 23 });
		statics.add(new byte[] { 12, 23 });
	}

	@Test
	public void strings() {
		String ss = "仰望着天空寻找一位失去的故友悄无声息的离开了也带上了命运";
		Charset UTF8 = Charset.forName("UTF-8");
		CharsetEncoder enc = UTF8.newEncoder();
		// enc.

	}

	@Test
	public void bytes() throws UnsupportedEncodingException {
		String src = "你我\n他";
		byte[] bytes = src.getBytes("UTF-8");
		System.out.println(bytesSrcNotation(bytes));

	}

	private String bytesSrcNotation(byte[] ba) {
		StringBuffer sb = new StringBuffer();
		sb.append("new byte[]{");
		for (byte b : ba) {
			sb.append(b).append(',');
		}
		sb.append("}");
		return sb.toString();
	}

	// @Test public void testUrlMapper() {
	// BranTemplateBase.urlMapper = new DummyUrlMapper();
	// ByteArrayOutputStream baos = new ByteArrayOutputStream();
	// Actions ac = new Actions(baos);
	// Post p = new Post();
	// p.title = "hello";
	// ac.render(p);
	// System.out.println(baos.toString());
	// }

	// @Test public void testMessage() {
	// // BranTemplateBase.urlMapper = new DummyUrlMapper();
	// BranTemplateBase.messageProvider = new SimpleMessageProvider();
	//		
	// ByteArrayOutputStream baos = new ByteArrayOutputStream();
	// Msg ac = new Msg(baos);
	// ac.render();
	// System.out.println(baos.toString());
	// }

	/**
	 * System.nanoTime is about 40 X slower than currentTimeMillies(), which
	 * takes about 40ns per call on my machine Windows
	 * 
	 * On Ubuntu, both are abot the same: 700 ns. The currentTimeMillies() is very slow
	 * compared with windows. 
	 */
	@Test
	public void testSysMilliNano() {
		int COUNT = 1000000; // 1 M
		long start = System.nanoTime();
		long end = start;
		for (int i = 0; i < COUNT; i++) {
			end = System.nanoTime();
		}
		System.out.println("nanoTime:          " + (end - start) / COUNT + " ns");

		long dummy = 0;
		start = System.nanoTime();
		for (int i = 0; i < COUNT; i++) {
			dummy = System.currentTimeMillis();
		}
		end = System.nanoTime();
		System.out.println("currentTimeMillis: " + (end - start) / COUNT + " ns");
	}
}
