/*
 * This file is part of muCommander, http://www.mucommander.com
 *
 * muCommander 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 3 of the License, or
 * (at your option) any later version.
 *
 * muCommander 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.mucommander.job;

import com.mucommander.job.impl.TransferFileJob;
import com.mucommander.text.DurationFormat;
import com.mucommander.text.Translator;

/**
 * Contains information about job progress.
 *
 */
public class JobProgress {
	private FileJob job;
	private TransferFileJob transferFileJob;

	private long effectiveJobTime;
	private long lastTime;
	private int totalPercentInt;
	private String totalProgressText;
	private int filePercentInt;
	private String fileProgressText;
	private long currentBps;
	private long bytesTotal;
	private long totalBps;
	private long lastBytesTotal;
	private String jobStatusString;
	private long jobPauseStartDate;

	public JobProgress(FileJob job) {
		this.job = job;
		if (job instanceof TransferFileJob) {
			this.transferFileJob = (TransferFileJob) job;
		}
		lastBytesTotal = 0;
		lastTime = System.currentTimeMillis();
	}

	
	/**
	 * Calculates the job progress status. This method calculates variables used
	 * to show job progress information. It can update information only on a
	 * processed file (when <code>labelOnly</code> is <code>true</code>). If
	 * <code>labelOnly</code> is false it will try to update full information on
	 * a job progress (e.g. percent completed, bytes per second, etc.).
	 * 
	 * @param fullUpdate
	 * 			 <code>true</code> update all information about processed file.<br/>
	 * 			 <code>false</code> update only label of a processed file.<br/>
	 * 		     Note that if a job has just finished this flag is ignored 
	 * 			 and all variables are recalculated.
	 * @return <code>true</code> if full job progress has been updated,
	 *         <code>false</code> if only label has been updated.
	 */
	public boolean calcJobProgress(boolean fullUpdate) {
		FileJobState jobState = job.getState();
		jobPauseStartDate = job.getPauseStartDate();
		if (jobState == FileJobState.FINISHED || jobState == FileJobState.INTERRUPTED) {
			jobStatusString = Translator.get("progress_dialog.job_finished");
			// Job just finished, let's loop one more time to ensure that
			// components (progress bar in particular)
			// reflect job completion
			fullUpdate = true;
		} else {
			jobStatusString = job.getStatusString();
		}
		if (!fullUpdate) {
			return false;
		}
		// Do not refresh progress information is job is paused, simply sleep
		if (jobState == FileJobState.PAUSED) {
			return false;
		}
		// Now is updated with current time, or job end date if job has finished
		// already.
		long now = job.getEndDate();
		if (now == 0) { // job hasn't finished yet
			now = System.currentTimeMillis();
		}

		long currentFileRemainingTime = 0;
		long totalRemainingTime;

		effectiveJobTime = job.getEffectiveJobTime();
		if (effectiveJobTime == 0) {
			effectiveJobTime = 1; // To avoid potential zero divisions
		}

		if (transferFileJob != null) {
			bytesTotal = transferFileJob.getTotalByteCount() - transferFileJob.getTotalSkippedByteCount();
			totalBps = (long) (bytesTotal * 1000d / effectiveJobTime);
			if (now - lastTime > 0) { // To avoid divisions by zero 
				currentBps = (long) ((bytesTotal - lastBytesTotal) * 1000d / (now - lastTime));
			} else {
				currentBps = 0;
			}

			// Update current file progress bar
			float filePercentFloat = transferFileJob.getFilePercentDone();
			filePercentInt = (int) (100 * filePercentFloat);

			fileProgressText = filePercentInt + "%";
			// Append estimated remaining time (ETA) if current file transfer is
			// not already finished (100%)
			if (filePercentFloat < 1) {
				fileProgressText += " - ";

				long currentFileSize = transferFileJob.getCurrentFileSize();
				// If current file size is not available, ETA cannot be
				// calculated
				if (currentFileSize == -1) {
					fileProgressText += "?";
				}
				// Avoid potential divisions by zero
				else if (totalBps == 0) {
					currentFileRemainingTime = -1;
					fileProgressText += DurationFormat.getInfiniteSymbol();
				} else {
					currentFileRemainingTime = (long) ((1000 * (currentFileSize - 
							transferFileJob.getCurrentFileByteCount())) / (float) totalBps);
					fileProgressText += DurationFormat.format(currentFileRemainingTime);
				}
			}

			lastBytesTotal = bytesTotal;
			lastTime = now;
		}

		// Update total progress bar
		// Total job percent is based on the *number* of files remaining, not
		// their actual size.
		// So this is very approximate.
		float totalPercentFloat = job.getTotalPercentDone();
		totalPercentInt = (int) (100 * totalPercentFloat);

		totalProgressText = totalPercentInt + "%";

		// Add a rough estimate of the total remaining time (ETA):
		// total remaining time is based on the total job percent completed
		// which itself is based on the *number*
		// of files remaining, not their actual size. So this is very
		// approximate.
		// Do not add ETA if job is already finished (100%)
		if (totalPercentFloat < 1) {
			totalProgressText += " - ";

			// Avoid potential divisions by zero
			if (totalPercentFloat == 0) {
				totalProgressText += "?";
			} else {
				// Make sure that total ETA is never smaller than current file
				// ETA
				totalRemainingTime = (long) ((1 - totalPercentFloat) * 
						(effectiveJobTime / totalPercentFloat));
				totalRemainingTime = Math.max(totalRemainingTime,
						currentFileRemainingTime);
				totalProgressText += DurationFormat.format(totalRemainingTime);
			}
		}
		return true;
	}

	public String getJobStatusString() {
		return jobStatusString;
	}

	public boolean isTransferFileJob() {
		return transferFileJob != null;
	}

	public int getFilePercentInt() {
		return filePercentInt;
	}

	public String getFileProgressText() {
		return fileProgressText;
	}

	public long getBytesTotal() {
		return bytesTotal;
	}

	public long getTotalBps() {
		return totalBps;
	}

	public long getLastTime() {
		return lastTime;
	}

	public long getCurrentBps() {
		return currentBps;
	}

	public int getTotalPercentInt() {
		return totalPercentInt;
	}

	public String getTotalProgressText() {
		return totalProgressText;
	}

	public long getEffectiveJobTime() {
		return effectiveJobTime;
	}

	public long getJobPauseStartDate() {
		return jobPauseStartDate;
	}

}
