package hiro.yoshioka.sql.notes;

import hiro.yoshioka.classmanager.ClassManager;
import hiro.yoshioka.sql.AbsBasicSQL;
import hiro.yoshioka.sql.engine.Request;
import hiro.yoshioka.sql.engine.GettingResourceRequest;
import hiro.yoshioka.sql.notes.ddl.Database;
import hiro.yoshioka.sql.notes.ddl.Form;
import hiro.yoshioka.sql.notes.ddl.NotesDXLParser2;
import hiro.yoshioka.sql.notes.reflect.WolfDBDirectory;
import hiro.yoshioka.sql.notes.reflect.WolfDatabase;
import hiro.yoshioka.sql.notes.reflect.WolfDocument;
import hiro.yoshioka.sql.notes.reflect.WolfDxlExporter;
import hiro.yoshioka.sql.notes.reflect.WolfForm;
import hiro.yoshioka.sql.notes.reflect.WolfItem;
import hiro.yoshioka.sql.notes.reflect.WolfNoteCollection;
import hiro.yoshioka.sql.notes.reflect.WolfView;
import hiro.yoshioka.sql.params.ConnectionProperties;
import hiro.yoshioka.sql.params.DBResourceCapturingFilter;
import hiro.yoshioka.sql.params.DBUserPass;
import hiro.yoshioka.sql.resource.DBRoot;
import hiro.yoshioka.sql.resource.DBSchema;
import hiro.yoshioka.sql.resource.DBTable;
import hiro.yoshioka.sql.resource.IDBResource;
import hiro.yoshioka.sql.resource.IDBSchema;
import hiro.yoshioka.sql.resource.IDBTable;
import hiro.yoshioka.sql.resource.TableType;
import hiro.yoshioka.sql.resource.notes.ItemType;
import hiro.yoshioka.sql.resource.notes.NotesDBColumn;
import hiro.yoshioka.sql.resource.notes.NotesDBSchema;
import hiro.yoshioka.sql.resource.notes.NotesDBTable;
import hiro.yoshioka.util.StringUtil;

import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class NotesRunnerMeta extends AbsNotesRunner implements Callable<DBRoot> {
	static final String[] CONST_COLUMNS;
	ConnectionProperties connectionProperties;
	IDBTable onlyTable;
	IDBSchema onlySchema;
	GettingResourceRequest caption_request;

	static {
		CONST_COLUMNS = new String[] { AbsNotesRunner.Form,
				AbsNotesRunner.UniversalID, AbsNotesRunner.HttpURL,
				AbsNotesRunner.NotesURL };
	}

	public NotesRunnerMeta(ClassManager manager, GettingResourceRequest request) {
		this(manager, request, null);
	}

	public NotesRunnerMeta(ClassManager manager,
			GettingResourceRequest request, IDBResource resource) {
		super(manager, request.getConnectionProperties());
		this.caption_request = request;
		this.connectionProperties = request.getConnectionProperties();
		if (resource instanceof IDBSchema) {
			this.onlySchema = (IDBSchema) resource;
		} else if (resource instanceof IDBTable) {
			this.onlyTable = (IDBTable) resource;
		}
	}

	public NotesRunnerMeta(ClassManager manager, ConnectionProperties properties) {
		this(manager, properties, null);
	}

	public NotesRunnerMeta(ClassManager manager,
			ConnectionProperties properties, IDBTable table) {
		super(manager, properties);
		this.connectionProperties = properties;
		this.onlyTable = table;
	}

	public static void main(java.lang.String[] args) {
		ConnectionProperties p = new ConnectionProperties();

		// dom
		p.setHost("10.0.0.100");
		p.setAuthenticate(new DBUserPass("user", "password"));

		p.setCaptureWithDDL(false);
		p.setCaptureWithColumnInfo(false);
		p.setCaptureWithViewInfo(false);

		p.setCapturingFilter(DBResourceCapturingFilter.MATCHES);
		p.addFilePathRegrex(".*hoge.*");
		ClassManager cm = new ClassManager();
		try {
			ExecutorService ex = Executors.newSingleThreadExecutor();
			Future<DBRoot> future = null;

			future = ex.submit(new NotesRunnerMeta(cm, p));

			try {
				System.out.println(future.get());
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (ExecutionException e) {
				e.printStackTrace();
			}
			ex.shutdown();
		} catch (Exception e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
	}

	@Override
	public DBRoot call() throws Exception {
		initClass();
		createSession();

		DBRoot root = null;

		try {
			String p = wolf_session.getPlatform();
			String nv = wolf_session.getNotesVersion();

			fLogger.info("onlyTable=" + onlyTable);
			if (this.onlyTable == null) {

				int total = 0;
				if (this.onlySchema == null) {
					root = new DBRoot(userid);
					if (StringUtil.isEmpty(userid)) {
						root.setName("lotus.domino.Database");
					} else {
						root.setName("lotus.domino.Database[" + userid + "]");
					}
					root.setPropertyValue("This DB Resources updated at ",
							String.format("%tF %tT", new java.util.Date(),
									new java.util.Date()));
					root.setPropertyValue("Current Platform", p);
					root.setPropertyValue("Current Domino Version", nv);
					root.setPropertyValue("DatabaseProductName", "Lotus");
					root.setPropertyValue("supportTokens",
							AbsBasicSQL.DEFAULT_SQL_TOKENS);

					WolfDBDirectory wolf_directory = wolf_session
							.getDbDirectory(StringUtil.EMPTY_STRING);
					WolfDatabase wolf_database = wolf_directory
							.getFirstDatabase(WolfDBDirectory.dbDirectory_database_type);
					out: while (wolf_database != null) {
						IDBSchema schema = null;
						try {
							schema = createDBSchema(wolf_database, root);
							if (schema == null) {
								continue;
							}
							String filePath = schema
									.getPropertyValue("filePath");
							if (caption_request != null) {
								caption_request.beginTask(
										String.format("Capture[%s]", filePath),
										1);
								if (caption_request.canceld()) {
									fLogger.info("canseled");
									return null;
								}
							}

							if (!connectionProperties.isCaptureWithTableInfo()) {
								continue;
							}
							if (filePath.matches("iNotes.+Forms85.nsf")) {
								continue;
							}
							if (filePath.matches("names.nsf")) {
								continue;
							}
							setDBResourceForSchema(wolf_database, root, schema,
									total++);
						} catch (Exception e) {
							System.err.println(e.getMessage());
							e.printStackTrace();
						} finally {
							wolf_database.recycle();
							wolf_database = wolf_directory.getNextDatabase();
						}
						if (root.childrenNum() % 5 == 0) {
							System.gc();
						}
					}
					wolf_database = null;
				} else {
					root = this.onlySchema.getRoot();
					WolfDatabase wolf_database = getDatabaseBySchema(this.onlySchema);
					if (wolf_database != null) {
						this.onlySchema.removeAll();
						setDBResourceForSchema(wolf_database, root,
								this.onlySchema, total++);
						wolf_database.recycle();
					}
					wolf_database = null;
				}
			} else {
				root = onlyTable.getRoot();
				try {
					WolfDatabase wolf_database = getDatabaseByTable(onlyTable);
					if (!wolf_database.isOpen()) {
						try {
							wolf_database.open();
						} catch (Exception e) {
							onlyTable.getParent().setComment(
									"Out:" + e.getMessage());
							e.printStackTrace();
						}
					}
					List<WolfForm> wolf_formList = wolf_database.getForms();
					if (caption_request != null) {
						caption_request.beginTask(
								String.format("%s.getForms",
										onlyTable.getName()),
								wolf_formList.size());
					}
					for (WolfForm wolf_form : wolf_formList) {
						if (caption_request != null) {
							if (caption_request.canceld()) {
								fLogger.info("canseled");
								return root;
							}
						}
						if (!wolf_form.isThisForm(onlyTable)) {
							wolf_form.recycle();
							continue;
						}
						setColumns(wolf_form, onlyTable);
						wolf_form.recycle();
						if (caption_request != null) {
							caption_request.worked(1);
						}
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}

		} finally {
			System.out.println("session recycle");
			wolf_session.recycle();
			System.out.println("session recycled");
		}
		System.out.println("ret=" + root);
		return root;
	}

	private void setDDL(WolfDatabase wolf_database, IDBSchema schema)
			throws IllegalArgumentException, IllegalAccessException,
			InvocationTargetException {

		WolfNoteCollection wolf_noteCollection = null;
		WolfDxlExporter wolf_dxlExporter = null;
		try {
			wolf_noteCollection = wolf_database.createNoteCollection(false);

			// noteco
			wolf_noteCollection.selectAllDesignElements(true);
			wolf_noteCollection.setSelectHelpAbout(false);
			wolf_noteCollection.buildCollection();

			wolf_dxlExporter = wolf_session.createDxlExporter();
			wolf_dxlExporter.setOutputDOCTYPE(false);
			wolf_dxlExporter.setConvertNotesBitmapsToGIF(true);

			String dxl = wolf_dxlExporter.exportDxl(wolf_noteCollection);

			Set<String> formSet = new LinkedHashSet<String>();
			for (IDBTable tbl : schema.getTableOrViews()) {
				formSet.add(tbl.getName());
				formSet.add(tbl.getAlias());
			}
			caption_request.subTask("parse Dxl: " + formSet);
			Database db = NotesDXLParser2.parseDxl(dxl, formSet);
			for (IDBTable tbl : schema.getTableOrViews()) {
				if (caption_request.canceld()) {
					fLogger.info("canseled");
					return;
				}
				Form form = db.getFrom(tbl.getName());
				if (form == null) {
					form = db.getFrom(tbl.getAlias());
				}
				if (form != null) {
					((NotesDBTable) tbl).setForm(form);
				}
			}
		} catch (Exception e1) {
			e1.printStackTrace();
		} finally {
			wolf_noteCollection.recycle();
			if (wolf_dxlExporter != null) {
				wolf_dxlExporter.recycle();
			}
		}

	}

	private void setDBResourceForSchema(WolfDatabase wolf_database,
			DBRoot root, IDBSchema schema, int total)
			throws IllegalArgumentException, IllegalAccessException,
			InvocationTargetException, MalformedURLException,
			SecurityException, ClassNotFoundException, NoSuchMethodException {
		if (!wolf_database.isOpen()) {
			try {
				wolf_database.open();
			} catch (Exception e) {
				schema.setComment("Out:" + e.getMessage());
				e.printStackTrace();

				return;
			}
		}
		String title = schema.getName();
		String filePath = schema.getPropertyValue("filePath");
		List<WolfForm> wolf_formList = wolf_database.getForms();
		if (caption_request != null) {
			caption_request.beginTask(String.format(
					"[%s] %s.getForms Total:%d", filePath, title,
					wolf_formList.size()), wolf_formList.size());
		}
		for (int n = 0; n < wolf_formList.size(); n++) {
			WolfForm wolf_form = wolf_formList.get(n);
			if (caption_request != null) {
				if (caption_request.canceld()) {
					fLogger.info("canseled");
					return;
				}
			}
			NotesDBTable table = new NotesDBTable(schema);
			table.setTableType(TableType.TABLE);
			table.setName(wolf_form.getName());
			schema.putTable(table);

			Properties prop = new Properties();
			Vector<String> readers = wolf_form.getReaders();
			// System.out.println("Form readers=" + readers);
			if (readers == null || readers.size() == 0) {
				prop.setProperty("Readers", "Nothing");
			} else {
				prop.setProperty("Readers", readers.toString());
			}

			for (String alias : wolf_form.getAliases()) {
				if (!StringUtil.isEmpty(alias)) {
					table.setAlias(alias);
				}
			}
			System.out.println("table:" + table.getName() + "// alias ="
					+ table.getAlias());

			if (doCaptureColumn(table, this.caption_request)) {
				for (String fieldName : wolf_form.getFields()) {
					NotesDBColumn col = new NotesDBColumn(table);
					col.setName(fieldName);
					ItemType type = wolf_form.getFieldType(fieldName);
					col.setItemType(type);
					table.putResource(fieldName, col);
				}
				for (String fieldName : CONST_COLUMNS) {
					NotesDBColumn col = new NotesDBColumn(table);
					col.setName(fieldName);
					col.setItemType(ItemType.TEXT);
					table.putResource(fieldName, col);
				}
			}
			wolf_form.recycle();
			if (caption_request != null) {
				if (total % 3 == 0) {
					caption_request
							.subTask(String.format("%d/%d::%s", n,
									wolf_formList.size(),
									table.getNameWithAsComment()));
					caption_request.worked(3);
				}
			}
		}
		if (connectionProperties.isCaptureWithDDL()) {
			setDDL(wolf_database, schema);
		}

		if (connectionProperties.isCaptureWithViewInfo()) {
			if (!"homepage.nsf".equals(filePath)) {
				System.out.println("wolf_database=" + wolf_database);
				for (WolfView wolf_view : wolf_database.getViews()) {
					if (caption_request != null) {
						if (caption_request.canceld()) {
							fLogger.info("canseled");
							return;
						}
					}
					DBTable view = new DBTable(schema);
					view.setTableType(TableType.VIEW);
					view.setName(wolf_view.getName());
					view.setPropertyValue("database", title);

					if (caption_request != null) {
						caption_request.subTask(view.getNameWithAsComment());
					}
					schema.putTable(view);
					try {
						/*
						 * why raise Exception at column count 0?
						 */
						if (wolf_view.getColumnCount() <= 0) {
							continue;
						}
					} catch (Exception e) {
						continue;
					}
					setColumns(wolf_view.getFirstDocument(), view);
					setColumns(wolf_view.getLastDocument(), view);
					wolf_view.recycle();
				}
			}
		}
	}

	boolean doCaptureColumn(IDBTable dbTable, Request request) {
		if (this.connectionProperties == null) {
			return false;
		}
		if (this.connectionProperties.isCaptureWithColumnInfo()) {
			return true;
		}
		ConnectionProperties con_prop = request.getConnectionProperties();
		DBRoot old = con_prop.getDBRootResource();
		if (old != null && old.isRecentryUsed(dbTable)) {
			return true;
		}
		return false;

	}

	private IDBSchema createDBSchema(WolfDatabase wolf_database, DBRoot root)
			throws IllegalArgumentException, IllegalAccessException,
			InvocationTargetException {
		IDBSchema schema = null;
		String categories = wolf_database.getCategories();
		String title = wolf_database.getTitle();
		String filePath = wolf_database.getFilePath();
		String templateName = wolf_database.getTemplateName();
		// String url = wolf_database.getURL();
		Integer currentAccessLevel = wolf_database.getCurrentAccessLevel();
		String fileName = wolf_database.getFileName();
		String server = wolf_database.getServer();

		if (!connectionProperties
				.isCapturingTarget(categories, filePath, title)) {
			return null;
		}

		schema = (DBSchema) root.getResource(title);
		if (schema == null) {
			schema = new NotesDBSchema(root);
			schema.setName(title);
			schema.setComment(filePath);
			root.putResource(title, schema);
		}

		Properties prop = new Properties();
		prop.setProperty("categories", categories);
		prop.setProperty("currentAccessLevel",
				String.valueOf(currentAccessLevel));
		prop.setProperty("fileName", fileName);
		prop.setProperty("filePath", filePath);
		prop.setProperty("server", server);
		prop.setProperty("templateName", templateName);
		schema.setProperties(prop);

		return schema;
	}

	private void setColumns(WolfForm formInstance, IDBTable table)
			throws IllegalArgumentException, IllegalAccessException,
			InvocationTargetException, MalformedURLException,
			SecurityException, ClassNotFoundException, NoSuchMethodException {
		for (String fieldName : formInstance.getFields()) {
			NotesDBColumn col = new NotesDBColumn(table);
			col.setName(fieldName);
			ItemType type = formInstance.getFieldType(fieldName);
			col.setItemType(type);
			table.putResource(fieldName, col);
		}
		for (String fieldName : CONST_COLUMNS) {
			NotesDBColumn col = new NotesDBColumn(table);
			col.setName(fieldName);
			col.setItemType(ItemType.TEXT);
			if (AbsNotesRunner.HttpURL.equals(fieldName)
					|| AbsNotesRunner.NotesURL.equals(fieldName)) {
				col.setOnClickable(true);
			}
			table.putResource(fieldName, col);
		}
	}

	private void setColumns(WolfDocument documentInstance, IDBTable table)
			throws IllegalArgumentException, IllegalAccessException,
			InvocationTargetException, MalformedURLException,
			SecurityException, ClassNotFoundException, NoSuchMethodException {
		if (documentInstance != null) {
			for (WolfItem itemInstance : documentInstance.getItems()) {
				String itemName = itemInstance.getName();
				if (table.getResource(itemName) != null) {
					continue;
				}
				NotesDBColumn col = new NotesDBColumn(table);
				col.setName(itemName);
				col.setItemType(itemInstance.getType());
				table.putResource(itemName, col);
			}
			for (String fieldName : CONST_COLUMNS) {
				if (table.getResource(fieldName) == null) {
					NotesDBColumn col = new NotesDBColumn(table);
					col.setName(fieldName);
					col.setItemType(ItemType.TEXT);
					if (AbsNotesRunner.HttpURL.equals(fieldName)
							|| AbsNotesRunner.NotesURL.equals(fieldName)) {
						col.setOnClickable(true);
					}
					table.putResource(fieldName, col);
				}
			}

		}
	}
}
