/*
 * Copyright (C) Azureus Software, Inc, All Rights Reserved.
 *
 * 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.
 *
 * 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 General Public License for more details ( see the LICENSE file ).
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package com.aelitis.azureus.ui.swt.columns.torrent;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.widgets.Display;

import org.gudy.azureus2.core3.disk.DiskManagerFileInfo;
import org.gudy.azureus2.core3.download.DownloadManager;
import org.gudy.azureus2.core3.download.DownloadManagerState;
import org.gudy.azureus2.core3.internat.MessageText;
import org.gudy.azureus2.core3.util.DisplayFormatters;
import org.gudy.azureus2.core3.util.SystemTime;
import org.gudy.azureus2.core3.util.UrlUtils;
import org.gudy.azureus2.plugins.download.DownloadTypeIncomplete;
import org.gudy.azureus2.plugins.ui.menus.MenuItem;
import org.gudy.azureus2.plugins.ui.menus.MenuItemFillListener;
import org.gudy.azureus2.plugins.ui.menus.MenuItemListener;
import org.gudy.azureus2.plugins.ui.tables.*;
import org.gudy.azureus2.ui.swt.Utils;
import org.gudy.azureus2.ui.swt.mainwindow.Colors;
import org.gudy.azureus2.ui.swt.mainwindow.SWTThread;
import org.gudy.azureus2.ui.swt.shells.GCStringPrinter;
import org.gudy.azureus2.ui.swt.views.MyTorrentsView;
import org.gudy.azureus2.ui.swt.views.ViewUtils;
import org.gudy.azureus2.ui.swt.views.table.CoreTableColumnSWT;
import org.gudy.azureus2.ui.swt.views.table.TableCellSWT;
import org.gudy.azureus2.ui.swt.views.table.TableCellSWTPaintListener;

import com.aelitis.azureus.core.download.DownloadManagerEnhancer;
import com.aelitis.azureus.core.download.EnhancedDownloadManager;
import com.aelitis.azureus.ui.common.table.TableRowCore;
import com.aelitis.azureus.ui.swt.UIFunctionsManagerSWT;
import com.aelitis.azureus.ui.swt.imageloader.ImageLoader;
import com.aelitis.azureus.ui.swt.skin.SWTSkinFactory;
import com.aelitis.azureus.ui.swt.skin.SWTSkinProperties;
import com.aelitis.azureus.ui.swt.utils.FontUtils;

/**
 * @author TuxPaper
 * @created Jun 13, 2006
 *
 */
public class ColumnProgressETA
	extends CoreTableColumnSWT
	implements TableCellAddedListener, TableCellMouseListener,
	TableCellRefreshListener, TableCellSWTPaintListener
{
	public static final Class DATASOURCE_TYPE = DownloadTypeIncomplete.class;

	public static final String COLUMN_ID = "ProgressETA";

	private static final int borderWidth = 1;

	private static final int COLUMN_WIDTH = 200;

	public static final long SHOW_ETA_AFTER_MS = 30000;

	private final static Object CLICK_KEY = new Object();

	protected static final String CFG_SHOWETA = "ColumnProgressETA.showETA";

	protected static final String CFG_SHOWSPEED = "ColumnProgressETA.showSpeed";

	private static Font fontText = null;

	Display display;

	private Color cBGdl;

	private Color cBGcd;

	private Color cBorder;

	private Color cText;

	Color textColor;

	private Image imgBGTorrent;

	private Color cTextDrop;

	private ViewUtils.CustomDateFormat cdf;

	private ColumnTorrentFileProgress fileProgress;

	protected boolean showETA;

	protected boolean showSpeed;

	/**
	 * 
	 */
	public ColumnProgressETA(String sTableID) {
		super(DATASOURCE_TYPE, COLUMN_ID, ALIGN_CENTER, COLUMN_WIDTH, sTableID);
		addDataSourceType(DiskManagerFileInfo.class);
		initializeAsGraphic(COLUMN_WIDTH);
		setAlignment(ALIGN_LEAD);
		setMinWidth(COLUMN_WIDTH);

		display = SWTThread.getInstance().getDisplay();

		SWTSkinProperties skinProperties = SWTSkinFactory.getInstance().getSkinProperties();
		cBGdl = skinProperties.getColor("color.progress.bg.dl");
		if (cBGdl == null) {
			cBGdl = Colors.blues[Colors.BLUES_DARKEST];
		}
		cBGcd = skinProperties.getColor("color.progress.bg.cd");
		if (cBGcd == null) {
			cBGcd = Colors.green;
		}
		cBorder = skinProperties.getColor("color.progress.border");
		if (cBorder == null) {
			cBorder = Colors.grey;
		}
		cText = skinProperties.getColor("color.progress.text");
		if (cText == null) {
			cText = Colors.black;
		}
		cTextDrop = skinProperties.getColor("color.progress.text.drop");

		cdf = ViewUtils.addCustomDateFormat(this);

		ImageLoader imageLoader = ImageLoader.getInstance();
		imgBGTorrent = imageLoader.getImage("image.progress.bg.torrent");

		fileProgress = new ColumnTorrentFileProgress(display);

		TableContextMenuItem menuShowETA = addContextMenuItem(
				"ColumnProgressETA.showETA", MENU_STYLE_HEADER);
		menuShowETA.setStyle(TableContextMenuItem.STYLE_CHECK);
		menuShowETA.addFillListener(new MenuItemFillListener() {
			public void menuWillBeShown(MenuItem menu, Object data) {
				menu.setData(new Boolean(showETA));
			}
		});
		menuShowETA.addMultiListener(new MenuItemListener() {
			public void selected(MenuItem menu, Object target) {
				showETA = ((Boolean) menu.getData()).booleanValue();
				setUserData(CFG_SHOWETA, showETA ? 1 : 0);
			}
		});

		TableContextMenuItem menuShowSpeed = addContextMenuItem(
				"ColumnProgressETA.showSpeed", MENU_STYLE_HEADER);
		menuShowSpeed.setStyle(TableContextMenuItem.STYLE_CHECK);
		menuShowSpeed.addFillListener(new MenuItemFillListener() {
			public void menuWillBeShown(MenuItem menu, Object data) {
				menu.setData(new Boolean(showSpeed));
			}
		});
		menuShowSpeed.addMultiListener(new MenuItemListener() {
			public void selected(MenuItem menu, Object target) {
				showSpeed = ((Boolean) menu.getData()).booleanValue();
				setUserData(CFG_SHOWSPEED, showSpeed ? 1 : 0);
			}
		});

	}
	
	

	public void fillTableColumnInfo(TableColumnInfo info) {
		info.addCategories(new String[] {
			CAT_CONTENT,
			CAT_ESSENTIAL,
			CAT_TIME,
		});
		info.setProficiency(TableColumnInfo.PROFICIENCY_BEGINNER);
	}

	public void cellAdded(TableCell cell) {
		cell.setMarginHeight(3);
		cell.setMarginWidth(8);
	}

	public void cellMouseTrigger(TableCellMouseEvent event) {

		Object ds = event.cell.getDataSource();
		if (ds instanceof DiskManagerFileInfo) {
			fileProgress.fileInfoMouseTrigger(event);
			return;
		}

		DownloadManager dm = (DownloadManager) ds;
		if (dm == null) {
			return;
		}

		String clickable = (String) dm.getUserData(CLICK_KEY);

		if (clickable == null) {

			return;
		}

		event.skipCoreFunctionality = true;

		if (event.eventType == TableCellMouseEvent.EVENT_MOUSEUP) {

			String url = UrlUtils.getURL(clickable);

			if (url != null) {

				Utils.launch(url);
			}
		}
	}

	public void refresh(TableCell cell) {
		Object ds = cell.getDataSource();

		// needs to be long so we can shift it for sortVal
		long percentDone = getPercentDone(ds);

		long sortValue = 0;

		if (ds instanceof DownloadManager) {
			DownloadManager dm = (DownloadManager) cell.getDataSource();
			// close enough to unique with abs..
			int hashCode = Math.abs(DisplayFormatters.formatDownloadStatus(dm).hashCode());

			long completedTime = dm.getDownloadState().getLongParameter(
					DownloadManagerState.PARAM_DOWNLOAD_COMPLETED_TIME);
			if (completedTime <= 0 || !dm.isDownloadComplete(false)) {
				sortValue = (percentDone << 31) + hashCode;
			} else {
				sortValue = ((completedTime / 1000) << 31) + hashCode;
			}
			
		} else if (ds instanceof DiskManagerFileInfo) {
			DiskManagerFileInfo fileInfo = (DiskManagerFileInfo) ds;
			int st = fileInfo.getStorageType();
			if ((st == DiskManagerFileInfo.ST_COMPACT || st == DiskManagerFileInfo.ST_REORDER_COMPACT)
					&& fileInfo.isSkipped()) {
				sortValue = 1;
			} else if (fileInfo.isSkipped()) {
				sortValue = 2;
			} else if (fileInfo.getPriority() > 0) {

				int pri = fileInfo.getPriority();
				sortValue = 4;

				if (pri > 1) {
					sortValue += pri;
				}
			} else {
				sortValue = 3;
			}
			sortValue = (fileInfo.getDownloadManager().getState() * 10000)
					+ percentDone + sortValue;
		}

		long eta = showETA ? getETA(cell) : 0;
		long speed = showSpeed ? getSpeed(ds) : 0;

		//System.out.println("REFRESH " + sortValue + ";" + ds);
		Comparable old = cell.getSortValue();
		boolean sortChanged = cell.setSortValue(sortValue);

		if (sortChanged && old != null && !(old instanceof String)) {
			UIFunctionsManagerSWT.getUIFunctionsSWT().refreshIconBar();
		}

		long lastETA = 0;
		long lastSpeed = 0;
		TableRow row = cell.getTableRow();
		if (row != null) {
			if (showETA) {
				Object data = row.getData("lastETA");
				if (data instanceof Number) {
					lastETA = ((Number) data).longValue();
				}
				row.setData("lastETA", new Long(eta));
			}
			if (showSpeed) {
				Object data = row.getData("lastSpeed");
				if (data instanceof Number) {
					lastSpeed = ((Number) data).longValue();
				}
				row.setData("lastSpeed", new Long(speed));
			}
		}

		if (!sortChanged && (lastETA != eta || lastSpeed != speed)) {
			cell.invalidate();
		}
	}

	// @see org.gudy.azureus2.ui.swt.views.table.TableCellSWTPaintListener#cellPaint(org.eclipse.swt.graphics.GC, org.gudy.azureus2.ui.swt.views.table.TableCellSWT)
	public void cellPaint(GC gc, TableCellSWT cell) {
		Object ds = cell.getDataSource();
		if (ds instanceof DiskManagerFileInfo) {
			fileProgress.fillInfoProgressETA(cell.getTableRowCore(), gc,
					(DiskManagerFileInfo) ds, cell.getBounds());
			return;
		}

		if (!(ds instanceof DownloadManager)) {
			return;
		}

		DownloadManager dm = (DownloadManager) cell.getDataSource();

		int percentDone = getPercentDone(ds);
		long eta = showETA ? getETA(cell) : 0;

		//Compute bounds ...
		int newWidth = cell.getWidth();
		if (newWidth <= 0) {
			return;
		}
		int newHeight = cell.getHeight();

		Color fgFirst = gc.getForeground();

		final Color fgOriginal = fgFirst;

		Rectangle cellBounds = cell.getBounds();

		int xStart = cellBounds.x;
		int yStart = cellBounds.y;

		int xRelProgressFillStart = borderWidth;
		int yRelProgressFillStart = borderWidth;
		int xRelProgressFillEnd = newWidth - xRelProgressFillStart - borderWidth;
		int yRelProgressFillEnd = yRelProgressFillStart + 13;
		boolean showSecondLine = yRelProgressFillEnd + 10 < newHeight;

		if (xRelProgressFillEnd < 10 ) {
			return;
		}
		String sStatusLine = null;

		// Draw Progress bar
		// ImageLoader imageLoader = ImageLoader.getInstance();

		Rectangle boundsImgBG;

		if (!ImageLoader.isRealImage(imgBGTorrent)) {
			boundsImgBG = new Rectangle(0, 0, 0, 13);
		} else {
			boundsImgBG = imgBGTorrent.getBounds();
		}

		if (fontText == null) {
			fontText = FontUtils.getFontWithHeight(gc.getFont(), gc,
					boundsImgBG.height - 3);
		}

		if (!showSecondLine) {
			yRelProgressFillStart = (cellBounds.height / 2)
					- ((boundsImgBG.height) / 2);
		}

		yRelProgressFillEnd = yRelProgressFillStart + boundsImgBG.height;

		int progressWidth = newWidth - 2;
		gc.setForeground(cBorder);
		gc.drawRectangle(xStart + xRelProgressFillStart - 1, yStart
				+ yRelProgressFillStart - 1, progressWidth + 1, boundsImgBG.height + 1);

		int pctWidth = (int) (percentDone * (progressWidth) / 1000);
		gc.setBackground(percentDone == 1000 || dm.isDownloadComplete(false) ? cBGcd : cBGdl);
		gc.fillRectangle(xStart + xRelProgressFillStart, yStart
				+ yRelProgressFillStart, pctWidth, boundsImgBG.height);
		if (progressWidth > pctWidth) {
			gc.setBackground(Colors.white);
			gc.fillRectangle(xStart + xRelProgressFillStart + pctWidth, yStart
					+ yRelProgressFillStart, progressWidth - pctWidth, boundsImgBG.height);
		}

		if (boundsImgBG.width > 0) {
			gc.drawImage(imgBGTorrent, 0, 0, boundsImgBG.width, boundsImgBG.height,
					xStart + xRelProgressFillStart, yStart + yRelProgressFillStart,
					progressWidth, boundsImgBG.height);
		}

		if (sStatusLine == null) {
			if (dm.isUnauthorisedOnTracker()) {
				sStatusLine = dm.getTrackerStatus();
				// fgFirst = Colors.colorError;	pftt, no colours allowed apparently
			} else {
				if (showETA && eta > 0) {
					String sETA = ViewUtils.formatETA(eta,
							MyTorrentsView.progress_eta_absolute, cdf.getDateFormat());
					sStatusLine = MessageText.getString(
							"MyTorrents.column.ColumnProgressETA.2ndLine", new String[] {
								sETA
							});
				} else {
					sStatusLine = DisplayFormatters.formatDownloadStatus(dm).toUpperCase();
				}
			}

			int cursor_id;

			if (sStatusLine != null && sStatusLine.indexOf("http://") == -1) {

				dm.setUserData(CLICK_KEY, null);

				cursor_id = SWT.CURSOR_ARROW;

			} else {

				dm.setUserData(CLICK_KEY, sStatusLine);

				cursor_id = SWT.CURSOR_HAND;

				if (!cell.getTableRow().isSelected()) {

					fgFirst = Colors.blue;
				}
			}

			((TableCellSWT) cell).setCursorID(cursor_id);
		}

		gc.setTextAntialias(SWT.ON);
		gc.setFont(fontText);
		if (showSecondLine && sStatusLine != null) {
			gc.setForeground(fgFirst);
			boolean over = GCStringPrinter.printString(gc, sStatusLine,
					new Rectangle(cellBounds.x, yStart + yRelProgressFillEnd,
							cellBounds.width, newHeight - yRelProgressFillEnd), true,
					false, SWT.CENTER);
			cell.setToolTip(over ? sStatusLine : null);
			gc.setForeground(fgOriginal);
		}

		String sSpeed = "";
		if (showSpeed) {
			long lSpeed = getSpeed(ds);
			if (lSpeed > 0) {
				sSpeed = " ("
						+ DisplayFormatters.formatByteCountToKiBEtcPerSec(lSpeed, true)
						+ ")";
			}
		}

		String sPercent = DisplayFormatters.formatPercentFromThousands(percentDone);

		Rectangle area = new Rectangle(xStart + xRelProgressFillStart + 3, yStart
				+ yRelProgressFillStart, xRelProgressFillEnd - xRelProgressFillStart
				- 6, yRelProgressFillEnd - yRelProgressFillStart);
		GCStringPrinter sp = new GCStringPrinter(gc, sPercent + sSpeed, area, true,
				false, SWT.LEFT);
		if (cTextDrop != null) {
			area.x++;
			area.y++;
			gc.setForeground(cTextDrop);
			sp.printString();
			area.x--;
			area.y--;
		}
		gc.setForeground(cText);
		sp.printString();
		Point pctExtent = sp.getCalculatedSize();

		area.width -= (pctExtent.x + 3);
		area.x += (pctExtent.x + 3);

		if (!showSecondLine && sStatusLine != null) {
			boolean fit = GCStringPrinter.printString(gc, sStatusLine,
					area.intersection(cellBounds), true, false, SWT.RIGHT);
			cell.setToolTip(fit ? null : sStatusLine);
		}

		gc.setFont(null);
	}

	private int getPercentDone(Object ds) {
		if (ds instanceof DownloadManager) {
			return ((DownloadManager) ds).getStats().getDownloadCompleted(true);
		} else if (ds instanceof DiskManagerFileInfo) {
			DiskManagerFileInfo fileInfo = (DiskManagerFileInfo) ds;
			long length = fileInfo.getLength();
			if (length == 0) {
				return 1000;
			}
			return (int) (fileInfo.getDownloaded() * 1000 / length);
		}
		return 0;
	}

	private long getETA(TableCell cell) {
		Object ds = cell.getDataSource();
		if (ds instanceof DiskManagerFileInfo) {
			return 0;
		}
		DownloadManager dm = (DownloadManager) cell.getDataSource();

		long diff = SystemTime.getCurrentTime() - dm.getStats().getTimeStarted();
		if (diff > SHOW_ETA_AFTER_MS) {
			return dm.getStats().getSmoothedETA();
		}
		return 0;
	}

	private int getState(TableCell cell) {
		DownloadManager dm = (DownloadManager) cell.getDataSource();
		if (dm == null) {
			return DownloadManager.STATE_ERROR;
		}
		return dm.getState();
	}

	private boolean isStopped(TableCell cell) {
		int state = getState(cell);
		return state == DownloadManager.STATE_QUEUED
				|| state == DownloadManager.STATE_STOPPED
				|| state == DownloadManager.STATE_STOPPING
				|| state == DownloadManager.STATE_ERROR;
	}

	private long getSpeed(Object ds) {
		if (!(ds instanceof DownloadManager)) {
			return 0;
		}

		return ((DownloadManager) ds).getStats().getDataReceiveRate();
	}

	public EnhancedDownloadManager getEDM(DownloadManager dm) {
		DownloadManagerEnhancer dmEnhancer = DownloadManagerEnhancer.getSingleton();
		if (dmEnhancer == null) {
			return null;
		}
		return dmEnhancer.getEnhancedDownload(dm);
	}

	private void log(TableCell cell, String s) {
		System.out.println(((TableRowCore) cell.getTableRow()).getIndex() + ":"
				+ System.currentTimeMillis() + ": " + s);
	}

	public void postConfigLoad() {
		super.postConfigLoad();

		Object oShowETA = getUserData(CFG_SHOWETA);
		if (oShowETA == null) {
			showETA = false; // we could read a global default from somewhere
		} else if (oShowETA instanceof Number) {
			showETA = ((Number) oShowETA).intValue() == 1;
		}

		Object oShowSpeed = getUserData(CFG_SHOWSPEED);
		if (oShowSpeed == null) {
			showSpeed = false; // we could read a global default from somewhere
		} else if (oShowSpeed instanceof Number) {
			showSpeed = ((Number) oShowSpeed).intValue() == 1;
		}

		cdf.update();
	}
}
