/*
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.core.threads; import gplx.*; import gplx.core.*;
public class Gfo_thread_grp implements Gfo_invk {
	private final    Object thread_lock = new Object();
	private final    List_adp list = List_adp_.New();
	private int active_cur;
	public Gfo_thread_grp(String key) {this.key = key;}
	public String Key() {return key;} private final    String key;
	public boolean Autorun() {return autorun;} public Gfo_thread_grp Autorun_(boolean v) {autorun = v; return this;} private boolean autorun = true;
	public int Active_max() {return active_max;} public Gfo_thread_grp Active_max_(int v) {active_max = v; return this;} private int active_max = 1;
	public void Add(Gfo_thread_itm... ary) {
		synchronized (thread_lock) {
			list.Add_many((Object[])ary);
		}
		if (autorun)
			this.Run();
	}
	public void Run() {
		int len = list.Len(); if (len == 0) return;		// nothing in list; occurs when last item calls Run
		for (int i = 0; i < len; ++i) {
			if (active_cur == active_max) break;		// already at limit; return
			Gfo_thread_itm itm = null;
			synchronized (thread_lock) {
				itm = (Gfo_thread_itm)List_adp_.Pop_first(list);
				++active_cur;
			}
			Thread_adp_.Start_by_msg(itm.Thread__name(), this, GfoMsg_.new_cast_(Invk_run_wkr).Add("v", itm));
		}			
	}
	public void Stop_all() {
		synchronized (thread_lock) {
			int len = list.Len();
			for (int i = 0; i < len; ++i) {
				Gfo_thread_itm itm = (Gfo_thread_itm)list.Get_at(i);
				itm.Thread__stop();
			}
			active_cur = 0;
			list.Clear();
		}
	}
	public void Del(String key) {
		synchronized (thread_lock) {
			List_adp deleted = List_adp_.New();
			int len = list.Len();
			for (int i = 0; i < len; ++i) {
				Gfo_thread_itm itm = (Gfo_thread_itm)list.Get_at(i);
				if (itm.Thread__can_delete(key))
					deleted.Add(itm);
			}
			len = deleted.Len();
			for (int i = 0; i < len; ++i) {
				Gfo_thread_itm itm = (Gfo_thread_itm)deleted.Get_at(i);
				itm.Thread__stop();
				list.Del(itm);
			}
		}
	}
	private void Run_wkr(Gfo_thread_itm itm) {
		try		{itm.Thread__exec();}
		catch	(Exception e) {Gfo_usr_dlg_.Instance.Warn_many("", "", "uncaught exception while running thread; name=~{0} err=~{1}", itm.Thread__name(), Err_.Message_gplx_log(e));}
		finally {
			synchronized (thread_lock) {
				--active_cur;
			}
			this.Run();
		}
	}
	public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {
		if		(ctx.Match(k, Invk_run_wkr))		Run_wkr((Gfo_thread_itm)m.ReadObj("v"));
		else	return Gfo_invk_.Rv_unhandled;
		return this;
	}
	private static final String Invk_run_wkr = "run_wkr";
}
