/*
 * Copyright (c) 2014-2016 Jan Strauß <jan[at]over9000.eu>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package eu.over9000.skadi.model;

import eu.over9000.skadi.remote.data.ChannelMetadata;
import eu.over9000.skadi.util.NotificationUtil;
import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.scene.chart.XYChart;
import javafx.scene.image.Image;

import java.time.LocalTime;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.stream.IntStream;

public class Channel {
	private static final String DEFAULT_CHANNEL_LOGO = "http://static-cdn.jtvnw.net/jtv_user_pictures/xarth/404_user_150x150.png";
	private final StringProperty name;
	private final StringProperty title;
	private final LongProperty viewer;
	private final LongProperty uptime;
	private final ObjectProperty<Boolean> online;
	private final ObjectProperty<Boolean> wasOnline;
	private final StringProperty game;
	private final ListProperty<XYChart.Data<Number, Number>> viewerHistory;
	private final LongProperty viewerHistoryAverage;
	private final StringProperty logoURL;
	private final ObjectProperty<LocalTime> lastUpdated;
	private final LongProperty followers;
	private final LongProperty views;
	private final ObjectProperty<Boolean> partner;
	private final ObjectProperty<Image> preview;

	public Channel(final String name) {
		this.name = new SimpleStringProperty(name);
		title = new SimpleStringProperty("-");
		viewer = new SimpleLongProperty(0);
		uptime = new SimpleLongProperty(0);
		online = new SimpleObjectProperty<>();
		wasOnline = new SimpleObjectProperty<>();
		game = new SimpleStringProperty("-");
		viewerHistory = new SimpleListProperty<>(FXCollections.observableArrayList());
		viewerHistoryAverage = new SimpleLongProperty();
		viewerHistoryAverage.bind(Bindings.createIntegerBinding(buildAvgFunc(), viewerHistory));
		logoURL = new SimpleStringProperty(DEFAULT_CHANNEL_LOGO);
		lastUpdated = new SimpleObjectProperty<>(LocalTime.now());
		followers = new SimpleLongProperty();
		views = new SimpleLongProperty();
		partner = new SimpleObjectProperty<>();
		preview = new SimpleObjectProperty<>();
	}

	public void updateFrom(final ChannelMetadata u) {
		if (u.hasTitle()) {
			setTitle(u.getTitle());
		}
		if (u.hasOnline()) {
			updateOnline(u.getOnline());
		}
		if (u.hasViewer()) {
			updateViewer(u.getViewer());
		}
		if (u.hasGame()) {
			setGame(u.getGame());
		}
		if (u.hasUptime()) {
			setUptime(u.getUptime());
		}
		if (u.hasLogoURL()) {
			setLogoURL(u.getLogoURL());
		}
		if (u.hasFollowers()) {
			setFollowers(u.getFollowers());
		}
		if (u.hasViews()) {
			setViews(u.getViews());
		}
		if (u.hasPartner()) {
			setPartner(u.getPartner());
		}

		setLastUpdated(LocalTime.now());

		if (cameOnline()) {
			NotificationUtil.showOnlineNotification(this);
		}
	}

	private boolean cameOnline() {
		final boolean wasNotOnline = (getWasOnline() != null) && !getWasOnline();
		final boolean isNowOnline = (isOnline() != null) && isOnline();

		return wasNotOnline && isNowOnline;
	}

	private Callable<Integer> buildAvgFunc() {
		return () -> viewerHistory.isEmpty() ? 0 : (int) viewerHistory.stream().flatMapToInt(data -> IntStream.of(data.getYValue().intValue())).average().getAsDouble();
	}

	private void updateViewer(final long viewer) {
		setViewer(viewer);
		getViewerHistory().add(new XYChart.Data<>(System.currentTimeMillis(), viewer));
	}

	private void updateOnline(final Boolean online) {
		wasOnline.set(isOnline());
		setOnline(online);
	}

	@Override
	public String toString() {
		return "Channel [name=" + getName() + "]";
	}

	public String buildURL() {
		return "http://www.twitch.tv/" + getName() + "/";
	}

	@Override
	public boolean equals(final Object o) {
		if (this == o) {
			return true;
		}
		if (o == null || getClass() != o.getClass()) {
			return false;
		}
		final Channel channel = (Channel) o;
		return Objects.equals(name, channel.name);
	}

	@Override
	public int hashCode() {
		return Objects.hash(name);
	}

	public final StringProperty nameProperty() {
		return name;
	}

	public final String getName() {
		return nameProperty().get();
	}

	public final StringProperty titleProperty() {
		return title;
	}

	public final String getTitle() {
		return titleProperty().get();
	}

	public final void setTitle(final String title) {
		titleProperty().set(title);
	}

	public final LongProperty viewerProperty() {
		return viewer;
	}

	public final long getViewer() {
		return viewerProperty().get();
	}

	public final void setViewer(final long viewer) {
		viewerProperty().set(viewer);
	}

	public final LongProperty uptimeProperty() {
		return uptime;
	}

	public final long getUptime() {
		return uptimeProperty().get();
	}

	public final void setUptime(final long uptime) {
		uptimeProperty().set(uptime);
	}

	public final ObjectProperty<Boolean> onlineProperty() {
		return online;
	}

	public final Boolean isOnline() {
		return onlineProperty().get();
	}

	public final void setOnline(final Boolean online) {
		onlineProperty().set(online);
	}

	public final StringProperty gameProperty() {
		return game;
	}

	public final String getGame() {
		return gameProperty().get();
	}

	public final void setGame(final String game) {
		gameProperty().set(game);
	}

	public final ListProperty<XYChart.Data<Number, Number>> viewerHistoryProperty() {
		return viewerHistory;
	}

	public final javafx.collections.ObservableList<javafx.scene.chart.XYChart.Data<Number, Number>> getViewerHistory() {
		return viewerHistoryProperty().get();
	}

	public final LongProperty viewerHistoryAverageProperty() {
		return viewerHistoryAverage;
	}

	public final long getViewerHistoryAverage() {
		return viewerHistoryAverageProperty().get();
	}

	public final StringProperty logoURLProperty() {
		return logoURL;
	}

	public final String getLogoURL() {
		return logoURLProperty().get();
	}

	public final void setLogoURL(final String logoURL) {
		logoURLProperty().set(logoURL);
	}

	public final ObjectProperty<LocalTime> lastUpdatedProperty() {
		return lastUpdated;
	}

	public final LocalTime getLastUpdated() {
		return lastUpdatedProperty().get();
	}

	public final void setLastUpdated(final LocalTime lastUpdated) {
		lastUpdatedProperty().set(lastUpdated);
	}

	public final ObjectProperty<Boolean> wasOnlineProperty() {
		return wasOnline;
	}

	public final Boolean getWasOnline() {
		return wasOnlineProperty().get();
	}

	public final LongProperty followersProperty() {
		return followers;
	}

	public final long getFollowers() {
		return followersProperty().get();
	}

	public final void setFollowers(final long followers) {
		followersProperty().set(followers);
	}

	public final LongProperty viewsProperty() {
		return views;
	}

	public final long getViews() {
		return viewsProperty().get();
	}

	public final void setViews(final long views) {
		viewsProperty().set(views);
	}

	public final ObjectProperty<Boolean> partnerProperty() {
		return partner;
	}

	public final Boolean getPartner() {
		return partnerProperty().get();
	}

	public final void setPartner(final Boolean partner) {
		partnerProperty().set(partner);
	}

	public Image getPreview() {
		return preview.get();
	}

	public void setPreview(final Image preview) {
		this.preview.set(preview);
	}

	public ObjectProperty<Image> previewProperty() {
		return preview;
	}
}
