/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.dbs; import gplx.*;
import gplx.dbs.sqls.*; import gplx.dbs.sqls.itms.*;
public class Db_attach_mgr {
	private Db_conn main_conn; private Io_url main_conn_url;
	private final    Ordered_hash links_hash = Ordered_hash_.New();
	private final    List_adp attach_list = List_adp_.New();
	public Db_attach_mgr() {}
	public Db_attach_mgr(Db_conn main_conn, Db_attach_itm... links_ary) {
		this.Conn_main_(main_conn);
		this.Conn_links_(links_ary);
	}
	public Db_conn Conn_main() {return main_conn;}
	public Db_attach_mgr Conn_main_(Db_conn conn) {
		this.main_conn = conn; this.main_conn_url = Db_conn_info_.To_url(conn.Conn_info());
		return this;
	}
	public Db_attach_mgr Conn_links_(Db_attach_itm... itms_ary) {
		links_hash.Clear();
		int len = itms_ary.length;
		for (int i = 0; i < len; ++i) {
			Db_attach_itm itm = itms_ary[i];
			links_hash.Add(itm.Key, itm);
		}
		return this;
	}
	public void Attach() {
		int len = attach_list.Len();
		for (int i = 0; i < len; ++i) {
			Db_attach_itm itm = (Db_attach_itm)attach_list.Get_at(i);
			main_conn.Env_db_attach(itm.Key, itm.Url);
		}
	}
	public void Detach() {
		int len = attach_list.Len();
		for (int i = 0; i < len; ++i) {
			Db_attach_itm itm = (Db_attach_itm)attach_list.Get_at(i);
			main_conn.Env_db_detach(itm.Key);
		}
		attach_list.Clear();	// clear list so multiple detachs don't fail
	}
	public String Resolve_sql(String sql) {
		attach_list.Clear();
		int hash_len = links_hash.Count();
		for (int i = 0; i < hash_len; ++i) {
			Db_attach_itm attach_itm = (Db_attach_itm)links_hash.Get_at(i);
			String tkn = "<" + attach_itm.Key + ">";
			if (String_.Has(sql, tkn)) {
				Io_url attach_url = attach_itm.Url;
				String repl = "";
				if (!attach_url.Eq(main_conn_url)) {
					repl = attach_itm.Key + ".";
					attach_list.Add(attach_itm);
				}
				sql = String_.Replace(sql, tkn, repl);
			}
		}
		attached_sql = sql;
		return sql;
	}
	public Db_attach_mgr Exec_sql_w_msg(String msg, String sql, Object... args) {
		Gfo_usr_dlg_.Instance.Plog_many("", "", msg);
		Exec_sql(sql, args);
		return this;
	}
	public Db_attach_mgr Exec_sql(String sql, Object... args) {
		String attach_sql = String_.Format(Resolve_sql(sql), args);
		this.Attach();
		try {main_conn.Exec_sql(attach_sql);}
		finally {this.Detach();}
		return this;
	}
	public String Test__attach_sql() {return attached_sql;} private String attached_sql;
	public String[] Test__attach_list_keys() {
		int rv_len = attach_list.Count();
		String[] rv = new String[rv_len];
		for (int i = 0; i < rv_len; ++i) {
			Db_attach_itm itm = (Db_attach_itm)attach_list.Get_at(i);
			rv[i] = itm.Key;
		}
		return rv;
	}
	public Db_stmt Test__make_stmt_and_attach(Db_qry qry, gplx.dbs.sqls.itms.Sql_from_clause from_itm) {	// NOTE: tries to do attach via DOM not SQL 
		attach_list.Clear();
		Sql_qry_wtr sql_wtr = main_conn.Engine().Sql_wtr();
		List_adp from_tbls = from_itm.Tbls;
		int from_tbls_len = from_tbls.Count();
		for (int i = 0; i < from_tbls_len; ++i) {
			Sql_tbl_itm from_tbl = (Sql_tbl_itm)from_tbls.Get_at(i);
			String from_tbl_db = from_tbl.Db;
			if (String_.Eq(Sql_tbl_itm.Db__null, from_tbl_db)) continue;	// tbl does not have db defined; only "tbl" not "db.tbl"; skip
			Db_attach_itm attach_itm = (Db_attach_itm)links_hash.Get_by(from_tbl_db); if (attach_itm == null) throw Err_.new_("dbs", "qry defines an unknown database for attach_wkr", "from_tbl_db", from_tbl_db, "sql", qry.To_sql__exec(sql_wtr)); 
			if (attach_itm.Url.Eq(main_conn_url)) // attach_db same as conn; blank db, so "tbl", not "db.tbl"
				from_tbl.Db_enabled = false;
			else
				attach_list.Add(attach_itm);
		}
		attached_sql = sql_wtr.To_sql_str(qry, true);
		this.Attach();
		for (int i = 0; i < from_tbls_len; ++i) {	// reverse blanking from above
			Sql_tbl_itm from_tbl = (Sql_tbl_itm)from_tbls.Get_at(i);
			from_tbl.Db_enabled = true;
		}
		return main_conn.Stmt_sql(attached_sql);
	}
}
