using Gtk;
using Gdk;

public class Tootle.Views.Timeline : IAccountListener, IStreamListener, Views.Base {

	public string url { get; construct set; }
	public bool is_public { get; construct set; default = false; }
	public Type accepts { get; set; default = typeof (API.Status); }

	protected InstanceAccount? account = null;
	protected ulong on_status_added_sigig;

	public bool is_last_page { get; set; default = false; }
	public string? page_next { get; set; }
	public string? page_prev { get; set; }
	public string? stream = null;

	construct {
		app.refresh.connect (on_refresh);
		status_button.clicked.connect (on_refresh);
		account_listener_init ();

		on_status_added_sigig = on_status_added.connect (add_status);
		on_status_removed.connect (remove_status);
	}
	~Timeline () {
		streams.unsubscribe (stream, this);
	}

	public virtual bool is_status_owned (API.Status status) {
		return status.is_owned ();
	}

	public void prepend (Widget? w) {
		append (w, true);
	}

	public virtual void append (Widget? w, bool first = false) {
		if (w == null) {
			warning ("Attempted to add an empty widget");
			return;
		}

		if (first)
			content_list.prepend (w);
		else
			content_list.insert (w, -1);
	}

	public override void clear () {
		this.page_prev = null;
		this.page_next = null;
		this.is_last_page = false;
		base.clear ();
	}

	public void get_pages (string? header) {
		page_next = page_prev = null;
		if (header == null)
			return;

		var pages = header.split (",");
		foreach (var page in pages) {
			var sanitized = page
				.replace ("<","")
				.replace (">", "")
				.split (";")[0];

			if ("rel=\"prev\"" in page)
				page_prev = sanitized;
			else
				page_next = sanitized;
		}

		is_last_page = page_prev != null & page_next == null;
	}

	public virtual string get_req_url () {
		if (page_next != null)
			return page_next;
		return url;
	}

	public virtual Request append_params (Request req) {
		if (page_next == null)
			return req.with_param ("limit", @"$(settings.timeline_page_size)");
		else
			return req;
	}

	public virtual void on_request_finish () {}

	public virtual bool request () {
		var req = append_params (new Request.GET (get_req_url ()))
		.with_account (account)
		.with_ctx (this)
		.then ((sess, msg) => {
			Network.parse_array (msg, node => {
				try {
					var e = Entity.from_json (accepts, node);
					var w = e as Widgetizable;
					append (w.to_widget ());
				}
				catch (Error e) {
					warning (@"Timeline item parse error: $(e.message)");
				}
			});

			get_pages (msg.response_headers.get_one ("Link"));
			on_content_changed ();
			on_request_finish ();
		})
		.on_error (on_error);
		req.exec ();

		return GLib.Source.REMOVE;
	}

	public virtual void on_refresh () {
		scrolled.vadjustment.value = 0;
		status_button.sensitive = false;
		clear ();
		status_message = STATUS_LOADING;
		GLib.Idle.add (request);
	}

	public virtual string? get_stream_url () {
		return null;
	}

	public virtual void on_account_changed (InstanceAccount? acc) {
		account = acc;
		reconnect_stream ();
		on_refresh ();
	}

	public void reconnect_stream () {
		streams.unsubscribe (stream, this);
		streams.subscribe (get_stream_url (), this, out stream);
	}

	protected override void on_bottom_reached () {
		if (is_last_page) {
			info ("Last page reached");
			return;
		}
		request ();
	}

	protected virtual void add_status (API.Status status) {
		var allow_update = true;
		if (is_public)
			allow_update = settings.public_live_updates;

		if (settings.live_updates && allow_update)
			prepend (status.to_widget ());
	}

	protected virtual void remove_status (string id) {
		if (settings.live_updates) {
			content_list.get_children ().@foreach (w => {
				var sw = w as Widgets.Status;
				if (sw != null && sw.status.id == id)
					sw.destroy ();
			});
		}
	}

}
