/*
  This file is a part of Angry IP Scanner source code,
  see http://www.angryip.org/ for more information.
  Licensed under GPLv2.
 */
package net.azib.ipscan.feeders;

import net.azib.ipscan.core.ScanningSubject;
import net.azib.ipscan.util.InetAddressUtils;

import java.net.InetAddress;
import java.net.UnknownHostException;

import static net.azib.ipscan.util.InetAddressUtils.decrement;
import static net.azib.ipscan.util.InetAddressUtils.increment;
import static net.azib.ipscan.util.OctetConverter.octetsToInt;

/**
 * IP Range Feeder.
 * It contains the starting and ending values, which
 * are then iterated sequentially.
 * 
 * @author Anton Keks
 */
public class RangeFeeder extends AbstractFeeder {
	private InetAddress startIP;
	private InetAddress endIP;
	private InetAddress originalEndIP;
	private InetAddress currentIP;
	boolean isReverse;
	
	double percentageComplete;
	double percentageIncrement;
	
	public String getId() {
		return "feeder.range";
	}
	
	public RangeFeeder() {
	}

	public RangeFeeder(String startIP, String endIP) {
		try {
			this.startIP = this.currentIP = InetAddress.getByName(startIP);
			this.endIP = this.originalEndIP = InetAddress.getByName(endIP);
			initInterfaces(this.startIP);
			this.isReverse = false;
		}
		catch (UnknownHostException e) {
			throw new FeederException("malformedIP");
		}
		if (this.startIP.getClass() != this.endIP.getClass()) {
			throw new FeederException("differentProtocols");
		}
		if (InetAddressUtils.greaterThan(this.startIP, this.endIP)) {
			this.isReverse = true;
			this.endIP = decrement(decrement(this.endIP));
		}
		initPercentageIncrement();
		this.endIP = increment(this.endIP);
	}

	/**
	 * Initalizes fields, used for computation of percentage of completion.
	 */
	private void initPercentageIncrement() {
		var endAddress = this.endIP.getAddress();
		long rawEndIP = octetsToInt(endAddress, endAddress.length - 4);
		long rawStartIP = octetsToInt(this.startIP.getAddress(), endAddress.length - 4);
		// make 32-bit unsigned values
		rawEndIP = rawEndIP >= 0 ? rawEndIP : rawEndIP + Integer.MAX_VALUE;
		rawStartIP = rawStartIP >= 0 ? rawStartIP : rawStartIP + Integer.MAX_VALUE;
		// compute 1% of the whole range
		percentageIncrement = Math.abs(100.0 / (rawEndIP - rawStartIP + 1));
		percentageComplete = 0;
	}
	
	@Override public boolean hasNext() {
		// equals() is faster than greaterThan()
		return !currentIP.equals(endIP); 
	}

	@Override public ScanningSubject next() {
		percentageComplete += percentageIncrement;
		var prevIP = this.currentIP;
		if (this.isReverse) {
			this.currentIP = decrement(prevIP);
		} else {
			this.currentIP = increment(prevIP);
		}
		return subject(prevIP);
	}

	public int percentageComplete() {
		return (int)Math.round(percentageComplete);
	}
	
	@Override public String getInfo() {
		// let's return the range
		return startIP.getHostAddress() + " - " + originalEndIP.getHostAddress();
	}
}
