package com.ebao.aureum.model;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.CharEncoding;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.ebao.aureum.def.ObjectDef;

import freemarker.template.Configuration;
import freemarker.template.Template;

public class HtmlModelWriter implements ModelWriter {

	private static Log log = LogFactory.getLog(HtmlModelWriter.class);
	private ObjectsWrapper objectsWrapper = new ObjectsWrapper();
	private Configuration cfg;
	private String folder;
	private boolean ignoreEmptyCell;

	@Override
	public void write(ObjectDef def, ObjectModel object) {
		objectsWrapper.addObject(object);
		ObjectWrapper objectWrapper = new ObjectWrapper(object);
		print(objectWrapper);

		for (TableModel table : object.getTables()) {
			for (RowModel row : table.getRows()) {
				RowWrapper rowWrapper = new RowWrapper(object, table, row);
				print(rowWrapper);
			}
		}

		print(objectsWrapper);
	}

	private String formatFileString(AbstractWrapper wrapper) {
		String wFolder = wrapper.getFolder();
		if (StringUtils.isNotEmpty(wFolder)) {
			wFolder = wrapper.getFolder() + File.separator;
		} else {
			wFolder = StringUtils.EMPTY;
		}
		if (folder == null) {
			return wFolder + wrapper.getFile();
		} else {
			return folder + File.separator + wFolder + wrapper.getFile();
		}
	}

	private void print(AbstractWrapper wrapper) {
		Writer w = null;
		try {
			String file = formatFileString(wrapper);
			FileUtils.forceMkdir(new File(file).getParentFile());

			if (cfg == null) {
				cfg = new Configuration();
				cfg.setClassForTemplateLoading(this.getClass(),
						"/freemarker/model");
			}
			Template t = cfg.getTemplate(wrapper.getTemplate());
			t.setAutoFlush(true);
			w = new OutputStreamWriter(new FileOutputStream(file),
					CharEncoding.UTF_8);
			// build data
			Map<String, Object> d = new HashMap<String, Object>();
			d.put("d", wrapper);
			d.put("ignoreEmptyCell", ignoreEmptyCell);
			t.process(d, w);
		} catch (Exception e) {
			log.error("print error " + wrapper.getFile(), e);
		} finally {
			if (w != null) {
				try {
					w.close();
				} catch (IOException e) {
					log.error("close writer error " + wrapper.getFile(), e);
				}
			}
		}
	}

	public class ObjectWrapper extends AbstractWrapper {

		private ObjectModel object;

		public ObjectWrapper(ObjectModel object) {
			this.template = "object.ftl";
			this.folder = String.valueOf(object.getRootValue());
			this.file = "object.html";
			this.object = object;
		}

		public ObjectModel getObject() {
			return object;
		}

	}

	public class RowWrapper extends AbstractWrapper {

		private ObjectModel object;
		private TableModel table;
		private RowModel row;
		private int index;

		public RowWrapper(ObjectModel object, TableModel table, RowModel row) {
			index = table.index(row);

			this.template = "row.ftl";
			this.folder = String.valueOf(object.getRootValue())
					+ File.separator + String.valueOf(table.getTableName());
			this.file = (index + 1) + ".html";
			this.object = object;
			this.table = table;
			this.row = row;
		}

		public ObjectModel getObject() {
			return object;
		}

		public TableModel getTable() {
			return table;
		}

		public RowModel getRow() {
			return row;
		}

		public int getIndex() {
			return index;
		}

	}

	public class ObjectsWrapper extends AbstractWrapper {

		private List<Map<String, Object>> objects = new ArrayList<Map<String, Object>>();

		public ObjectsWrapper() {
			this.template = "main.ftl";
			this.folder = StringUtils.EMPTY;
			this.file = "main.html";
		}

		public void addObject(ObjectModel model) {
			Map<String, Object> object = new HashMap<String, Object>();
			objects.add(object);

			object.put("num", objects.size());
			object.put("object", model.getRootValue());
		}

		public List<Map<String, Object>> getObjects() {
			return objects;
		}

	}

	public abstract class AbstractWrapper {
		protected String file;
		protected String folder;
		protected String template;

		public String getFile() {
			return file;
		}

		public String getFolder() {
			return folder;
		}

		public String getTemplate() {
			return template;
		}
	}

	public String getFolder() {
		return folder;
	}

	public void setFolder(String folder) {
		this.folder = folder;
	}

	public boolean isIgnoreEmptyCell() {
		return ignoreEmptyCell;
	}

	public void setIgnoreEmptyCell(boolean ignoreEmptyCell) {
		this.ignoreEmptyCell = ignoreEmptyCell;
	}

	
}
