package se.unlogic.eagledns;

import org.apache.log4j.Logger;
import org.xbill.DNS.DClass;
import org.xbill.DNS.Record;
import org.xbill.DNS.Zone;
import org.xbill.DNS.ZoneTransferException;
import org.xbill.DNS.ZoneTransferIn;

import java.io.IOException;
import java.sql.Timestamp;
import java.util.List;


public class CachedSecondaryZone {

	private Logger log = Logger.getLogger(this.getClass());
	protected ZoneProvider zoneProvider;
	private SecondaryZone secondaryZone;

	public CachedSecondaryZone(ZoneProvider zoneProvider, SecondaryZone secondaryZone) {

		this.zoneProvider = zoneProvider;
		this.secondaryZone = secondaryZone;
		//this.update();

		if(this.secondaryZone.getZoneCopy() != null){

			log.info("Using stored zone data for sedondary zone " + this.secondaryZone.getZoneName());
		}
	}


	public SecondaryZone getSecondaryZone() {

		return secondaryZone;
	}


	public void setSecondaryZone(SecondaryZone secondaryZone) {

		this.secondaryZone = secondaryZone;
	}

	/**
	 * Updates this secondary zone from the primary zone
	 * @param axfrTimeout
	 */
	public void update(int axfrTimeout) {


		try {
			ZoneTransferIn xfrin = ZoneTransferIn.newAXFR(this.secondaryZone.getZoneName(), this.secondaryZone.getRemoteServerAddress(), null);
			xfrin.setDClass(DClass.value(this.secondaryZone.getDclass()));
			xfrin.setTimeout(axfrTimeout);

			List<?> records = xfrin.run();

			if (!xfrin.isAXFR()) {

				log.warn("Unable to transfer zone " + this.secondaryZone.getZoneName() + " from server " + this.secondaryZone.getRemoteServerAddress() + ", response is not a valid AXFR!");

				return;
			}

			Zone axfrZone = new Zone(this.secondaryZone.getZoneName(),records.toArray(new Record[records.size()]));

			log.debug("Zone " + this.secondaryZone.getZoneName() + " successfully transfered from server " + this.secondaryZone.getRemoteServerAddress());

			if(!axfrZone.getSOA().getName().equals(this.secondaryZone.getZoneName())){

				log.warn("Invalid AXFR zone name in response when updating secondary zone " + this.secondaryZone.getZoneName() + ". Got zone name " + axfrZone.getSOA().getName() + " in respons.");
			}

			if(this.secondaryZone.getZoneCopy() == null || this.secondaryZone.getZoneCopy().getSOA().getSerial() != axfrZone.getSOA().getSerial()){

				this.secondaryZone.setZoneCopy(axfrZone);
				this.secondaryZone.setDownloaded(new Timestamp(System.currentTimeMillis()));
				this.zoneProvider.zoneUpdated(this.secondaryZone);

				log.info("Zone " + this.secondaryZone.getZoneName() + " successfully updated from server " + this.secondaryZone.getRemoteServerAddress());
			}else{

				log.info("Zone " + this.secondaryZone.getZoneName() + " is already up to date with serial " + axfrZone.getSOA().getSerial());
				this.zoneProvider.zoneChecked(secondaryZone);
			}

		} catch (IOException e) {

			log.warn("Unable to transfer zone " + this.secondaryZone.getZoneName() + " from server " + this.secondaryZone.getRemoteServerAddress() + ", " + e);

			checkExpired();

		} catch (ZoneTransferException e) {

			log.warn("Unable to transfer zone " + this.secondaryZone.getZoneName() + " from server " + this.secondaryZone.getRemoteServerAddress() + ", " + e);

			checkExpired();

		}catch (RuntimeException e) {

			log.warn("Unable to transfer zone " + this.secondaryZone.getZoneName() + " from server " + this.secondaryZone.getRemoteServerAddress() + ", " + e);

			checkExpired();

		}finally{

			this.secondaryZone.setDownloaded(new Timestamp(System.currentTimeMillis()));
		}
	}


	private void checkExpired() {

		if(this.secondaryZone.getZoneCopy() != null && (System.currentTimeMillis() - this.secondaryZone.getDownloaded().getTime()) > (this.secondaryZone.getZoneCopy().getSOA().getExpire() * 1000)){

			log.warn("AXFR copy of secondary zone " + secondaryZone.getZoneName() + " has expired, deleting zone data...");

			this.secondaryZone.setZoneCopy(null);
			this.secondaryZone.setDownloaded(null);
			this.zoneProvider.zoneUpdated(this.secondaryZone);
		}
	}
}
