/*
 * This file is part of budgie-desktop
 *
 * Copyright © 2017-2020 Budgie Desktop Developers
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

const int MAX_CYCLES = 12;

public class Icon : Gtk.Image {
	private int widget_width = 36;
	private int widget_height = 30;
	private Budgie.PanelPosition panel_position = Budgie.PanelPosition.BOTTOM;
	public bool waiting = false;
	private int wait_cycle_counter = 0;
	private int attention_cycle_counter = 0;

	private double bounce_amount = 0.0;
	private double attention_amount = 0.0;

	public double bounce {
		public set {
			bounce_amount = value;
			this.queue_draw();
		}
		public get {
			return bounce_amount;
		}
	}

	public double attention {
		public set {
			attention_amount = value;
			this.queue_draw();
		}
		public get {
			return attention_amount;
		}
	}

	public double icon_opacity {
		public set {
			if (waiting) {
				opacity = value;
			} else {
				opacity = 1.0;
			}
		}
		public get {
			return opacity;
		}
	}

	public Icon() {
		size_allocate.connect(this.on_size_allocate);
	}

	protected void on_size_allocate(Gtk.Allocation allocation) {
		this.widget_width = allocation.width;
		this.widget_height = allocation.height;
	}

	public void animate_attention(Budgie.PanelPosition? position) {
		if (position != null) {
			this.panel_position = position;
		}

		if (attention_cycle_counter == 6) {
			attention_amount = 0;
			attention_cycle_counter = 0;
			return;
		}

		attention_cycle_counter++;

		BudgieTaskList.Animation attention_animation = new BudgieTaskList.Animation();
		attention_animation.widget = this;
		attention_animation.length = 50 * BudgieTaskList.MSECOND;
		attention_animation.tween = BudgieTaskList.sine_ease_in;

		if (attention_cycle_counter % 2 == 0) {
			attention_animation.changes = new BudgieTaskList.PropChange[] {
				BudgieTaskList.PropChange() {
					property = "attention",
					old = -5.0,
					@new = 5.0
				}
			};
		} else if (attention_cycle_counter == 5) {
			attention_animation.changes = new BudgieTaskList.PropChange[] {
				BudgieTaskList.PropChange() {
					property = "attention",
					old = 5.0,
					@new = 0.0
				}
			};
		} else {
			attention_animation.changes = new BudgieTaskList.PropChange[] {
				BudgieTaskList.PropChange() {
					property = "attention",
					old = (attention_cycle_counter == 1) ? 0.0 : 5.0,
					@new = -5.0
				}
			};
		}

		attention_animation.start((a) => {
			animate_attention(null);
		});
	}

	public void animate_wait() {
		if (!waiting) {
			wait_cycle_counter = 0;
			return;
		}

		if (wait_cycle_counter == MAX_CYCLES) {
			wait_cycle_counter = 0;
			return;
		}

		wait_cycle_counter++;

		BudgieTaskList.Animation wait_animation = new BudgieTaskList.Animation();
		wait_animation.widget = this;
		wait_animation.length = 700 * BudgieTaskList.MSECOND;
		wait_animation.tween = BudgieTaskList.sine_ease_in;
		wait_animation.changes = new BudgieTaskList.PropChange[] {
			BudgieTaskList.PropChange() {
				property = "icon_opacity",
				old = 1.0,
				@new = 0.3
			}
		};

		BudgieTaskList.Animation wait_animation1 = new BudgieTaskList.Animation();
		wait_animation1.widget = this;
		wait_animation1.length = 700 * BudgieTaskList.MSECOND;
		wait_animation1.tween = BudgieTaskList.sine_ease_in;
		wait_animation1.changes = new BudgieTaskList.PropChange[] {
			BudgieTaskList.PropChange() {
				property = "icon_opacity",
				old = 0.3,
				@new = 1.0
			}
		};

		wait_animation.start(() => {
			this.icon_opacity = 0.3;
		});

		Timeout.add(700, () => {
			if (!waiting) {
				wait_animation.stop();
				wait_animation1.stop();
				this.icon_opacity = 1.0;
				return false;
			}
			wait_animation1.start((a) => {
				this.icon_opacity = 1.0;
				animate_wait();
			});
			return false;
		});
	}

	public void animate_launch(Budgie.PanelPosition position) {
		this.panel_position = position;

		double old_value;

		if (position == Budgie.PanelPosition.TOP || position == Budgie.PanelPosition.BOTTOM) {
			old_value = (double)((this.widget_height-this.pixel_size)/2);
		} else {
			old_value = (double)((this.widget_width-this.pixel_size)/2);
		}

		BudgieTaskList.Animation launch_animation = new BudgieTaskList.Animation();
		launch_animation.widget = this;
		launch_animation.length = 1200 * BudgieTaskList.MSECOND;
		launch_animation.tween = BudgieTaskList.elastic_ease_out;
		launch_animation.changes = new BudgieTaskList.PropChange[] {
			BudgieTaskList.PropChange() {
				property = "bounce",
				old = old_value,
				@new = this.bounce
			}
		};

		launch_animation.start((a) => {
			this.bounce = 0.0;
		});
	}

	public override bool draw(Cairo.Context cr) {
		Gtk.Allocation alloc;
		get_allocation(out alloc);

		/* Have base implementation render first */
		var window = this.get_window();
		if (window == null) {
			return Gdk.EVENT_STOP;
		}
		/* Create a compatible buffer for the current scaling factor */
		var buffer = window.create_similar_image_surface(Cairo.Format.ARGB32,
														 alloc.width * this.scale_factor,
														 alloc.height * this.scale_factor,
														 this.scale_factor);
		var cr2 = new Cairo.Context(buffer);
		base.draw(cr2);

		/* Always start from 0 because the surface is correctly aligned */
		int x = 0;
		int y = 0;

		/* Offset the drawing */
		if (this.panel_position == Budgie.PanelPosition.LEFT) {
			x += (int)bounce_amount;
			y += (int)attention_amount;
		} else if (this.panel_position == Budgie.PanelPosition.RIGHT) {
			x -= (int)bounce_amount;
			y += (int)attention_amount;
		} else if (this.panel_position == Budgie.PanelPosition.TOP) {
			y += (int)bounce_amount;
			x += (int)attention_amount;
		} else {
			y -= (int)bounce_amount;
			x += (int)attention_amount;
		}

		/* Render with our own offsets now */
		cr.set_source_surface(buffer, x, y);
		cr.paint();

		return true;
	}
}
