/*
 * DriveInfo.java
 *
 * Copyright (c) 2007  Vreixo Formoso
 *
 * This library 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.
 * See COPYING file for details.
 */

package org.pykix.libburnia.libburn;

import java.util.EnumSet;

/**
 * Information on a drive in the system.
 * 
 * @author Vreixo Formoso
 * @since 0.1
 */
/*
 * This is not a proxy, but it contains a reference to the
 * C burn_drive_info struct it represents. This is only for two
 * pair of functions, forget and getAdr, than need that reference.
 * 
 * FIXME in a future it would be a good idea to ensure that this
 * reference is part of the drive_infos array, cause otherwise that
 * means that this DriveInfo is used after a new scan.
 * 
 * TODO do good java comments to getters
 */
public class DriveInfo {
	
	/** Name of the vendor of the drive. */
	private String vendor;
	
	/** Name of the drive. */
	private String product;
	
	/** Revision of the drive. */
	private String revision;
	
	/** Location of the drive in the filesystem. */
	/* This is currently the string which is used as persistent
	 * drive address. But be warned: there is NO GUARANTEE that this
	 * will stay so. Always use function {@link #getAdr} to
	 * inquire a persistent address.
	 */
	private String location;
	
	/** Can the drive read DVD-RAM discs. */
	private boolean readDvdram;
	
	/** Can the drive read DVD-R discs. */
	private boolean readDvdr;
	
	/** Can the drive read DVD-ROM discs. */
	private boolean readDvdrom;
	
	/** Can the drive read CD-R discs. */
	private boolean readCdr;
	
	/** Can the drive read CD-RW discs. */
	private boolean readCdrw;

	/** Can the drive write DVD-RAM discs. */
	private boolean writeDvdram;
	
	/** Can the drive write DVD-R discs. */
	private boolean writeDvdr;
	
	/** Can the drive write CD-R discs. */
	private boolean writeCdr;
	
	/** Can the drive write CD-RW discs. */
	private boolean writeCdrw;

	/** Can the drive simulate a write. */
	private boolean writeSimulate;

	/** Can the drive report C2 errors. */
	private boolean c2Errors;

	/** The size of the drive's buffer (in kilobytes). */
	private int bufferSize;
	
	/** 
	 * The supported block types in tao mode.
	 * They should be tested with the desired block type.
	 * 
	 * @see BlockType
	 */
	private EnumSet<BlockType> taoBlockTypes;
	
	/** 
	 * The supported block types in sao mode.
	 * They should be tested with the desired block type.
	 * 
	 * @see BlockType
	 */
	private EnumSet<BlockType> saoBlockTypes;
	
	/** 
	 * The supported block types in raw mode.
	 * They should be tested with the desired block type.
	 * 
	 * @see BlockType
	 */
	private EnumSet<BlockType> rawBlockTypes;
	
	/** 
	 * The supported block types in packet mode.
	 * They should be tested with the desired block type.
	 * 
	 * @see BlockType
	 */
	private EnumSet<BlockType> packetBlockTypes;

	/** 
	 * The value by which this drive can be indexed when using functions
	 * in the library. This is the value to pass to all libbburn functions
	 * that operate on a drive. 
	 */
	private Drive drive;
	
	/* reference to the C struct */
	private long ptr;

	DriveInfo(long ptr, String vendor, String product, String revision, 
			String location, boolean readDvdram, boolean readDvdr, 
			boolean readDvdrom, boolean readCdr, boolean readCdrw, 
			boolean writeDvdram, boolean writeDvdr, boolean writeCdr, 
			boolean writeCdrw, boolean writeSimulate, boolean errors, 
			int bufferSize, int taoBlockTypes, int saoBlockTypes, 
			int rawBlockTypes, int packetBlockTypes, long drive) {
		
		super();
		this.ptr = ptr;
		this.vendor = vendor;
		this.product = product;
		this.revision = revision;
		this.location = location;
		this.readDvdram = readDvdram;
		this.readDvdr = readDvdr;
		this.readDvdrom = readDvdrom;
		this.readCdr = readCdr;
		this.readCdrw = readCdrw;
		this.writeDvdram = writeDvdram;
		this.writeDvdr = writeDvdr;
		this.writeCdr = writeCdr;
		this.writeCdrw = writeCdrw;
		this.writeSimulate = writeSimulate;
		c2Errors = errors;
		this.bufferSize = bufferSize;
		this.taoBlockTypes = blockTypeBitMask2EnumSet(taoBlockTypes);
		this.saoBlockTypes = blockTypeBitMask2EnumSet(saoBlockTypes);
		this.rawBlockTypes = blockTypeBitMask2EnumSet(rawBlockTypes);
		this.packetBlockTypes = blockTypeBitMask2EnumSet(packetBlockTypes);
		
		//TODO can this lead to duplicate objects??
		this.drive = new Drive(drive);
	}

	public int getBufferSize() {
		return bufferSize;
	}

	public boolean c2Errors() {
		return c2Errors;
	}

	public Drive getDrive() {
		return drive;
	}

	public String getLocation() {
		return location;
	}

	public EnumSet<BlockType> getPacketBlockTypes() {
		return packetBlockTypes;
	}

	public String getProduct() {
		return product;
	}

	public EnumSet<BlockType> getRawBlockTypes() {
		return rawBlockTypes;
	}

	public boolean readCdr() {
		return readCdr;
	}

	public boolean readCdrw() {
		return readCdrw;
	}

	public boolean readDvdr() {
		return readDvdr;
	}

	public boolean readDvdram() {
		return readDvdram;
	}

	public boolean readDvdrom() {
		return readDvdrom;
	}

	public String getRevision() {
		return revision;
	}

	public EnumSet<BlockType> getSaoBlockTypes() {
		return saoBlockTypes;
	}

	public EnumSet<BlockType> getTaoBlockTypes() {
		return taoBlockTypes;
	}

	public String getVendor() {
		return vendor;
	}

	public boolean writeCdr() {
		return writeCdr;
	}

	public boolean writeCdrw() {
		return writeCdrw;
	}

	public boolean writeDvdr() {
		return writeDvdr;
	}

	public boolean writeDvdram() {
		return writeDvdram;
	}

	public boolean writeSimulate() {
		return writeSimulate;
	}
	
	/**
	 * Release memory about a single drive and any exclusive lock on it.
	 * Become unable to inquire or grab it. 
	 * Expect FATAL consequences if you try.
	 * 
	 * @param force
	 * 		controls degree of permissible drive usage at the moment this
	 * 		function is called, and the amount of automatically provided
	 * 		drive shutdown :
	 *            0= drive must be ungrabbed and BURN_DRIVE_IDLE
	 *            1= try to release drive resp. accept BURN_DRIVE_GRABBING
	 * 		Use these two only. Further values are to be defined.
	 * @return
	 * 		1 on success, 2 if drive was already forgotten,
	 * 		0 if not permissible, &lt; 0 on other failures, 
	 * 
	 * TODO throw exceptions instead of these return values
	 */
	public int forget(int force) {
		return burn_drive_info_forget(ptr, force);
	}
	
	/**
	 * Inquire the persistent address of the given drive.
	 * 
	 * @return
	 * 		The persistent address of the drive.
	 * @throws BurnException
	 * 		If address can't be getted, what means a libburn 
	 * 		internal problem.
	 */
	public String getAdr() throws BurnException {
		String adr = burn_drive_get_adr(ptr);
		if ( adr == null ) {
			throw new BurnException("Internal libray error");
		}
		return adr;
	}
	
	@Override
	public String toString() {
		return vendor + " " + product + " " + revision + " / " + location;
	}
	
	private static native int burn_drive_info_forget(long di, int force);

	private static native String burn_drive_get_adr(long drive_info);

	private EnumSet<BlockType> blockTypeBitMask2EnumSet(int bm) {
		
		EnumSet<BlockType> blockTypes = EnumSet.noneOf(BlockType.class);
		
		for ( BlockType bt : EnumSet.allOf(BlockType.class) ) {
			if ( (bm & bt.code) != 0 ) {
				blockTypes.add(bt);
			}
		}
		
		return blockTypes;
	}
	
}
