// CHECKSTYLE:OFF

/*---------------------------------------------------------------------------
 * Copyright (C) 1999 - 2007 Maxim Integrated Products, All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL MAXIM INTEGRATED PRODUCTS BE LIABLE FOR ANY CLAIM, DAMAGES
 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name of Maxim Integrated Products
 * shall not be used except as stated in the Maxim Integrated Products
 * Branding Policy.
 *---------------------------------------------------------------------------
 */

package com.dalsemi.onewire.adapter;

import java.io.File;
// imports
import java.util.Enumeration;
import java.util.Vector;

import com.dalsemi.onewire.OneWireException;

/**
 * The DSPortAdapter class for all TMEX native adapters.
 *
 * Instances of valid DSPortAdapter's are retrieved from methods in
 * {@link com.dalsemi.onewire.OneWireAccessProvider OneWireAccessProvider}.
 *
 * <P>
 * The TMEXAdapter methods can be organized into the following categories:
 * </P>
 * <UL>
 * <LI><B> Information </B>
 * <UL>
 * <LI>{@link #getAdapterName() getAdapterName}
 * <LI>{@link #getPortTypeDescription() getPortTypeDescription}
 * <LI>{@link #getClassVersion() getClassVersion}
 * <LI>{@link #adapterDetected() adapterDetected}
 * <LI>{@link #getAdapterVersion() getAdapterVersion}
 * <LI>{@link #getAdapterAddress() getAdapterAddress}
 * </UL>
 * <LI><B> Port Selection </B>
 * <UL>
 * <LI>{@link #getPortNames() getPortNames}
 * <LI>{@link #selectPort(String) selectPort}
 * <LI>{@link #getPortName() getPortName}
 * <LI>{@link #freePort() freePort}
 * </UL>
 * <LI><B> Adapter Capabilities </B>
 * <UL>
 * <LI>{@link #canOverdrive() canOverdrive}
 * <LI>{@link #canHyperdrive() canHyperdrive}
 * <LI>{@link #canFlex() canFlex}
 * <LI>{@link #canProgram() canProgram}
 * <LI>{@link #canDeliverPower() canDeliverPower}
 * <LI>{@link #canDeliverSmartPower() canDeliverSmartPower}
 * <LI>{@link #canBreak() canBreak}
 * </UL>
 * <LI><B> 1-Wire Network Semaphore </B>
 * <UL>
 * <LI>{@link #beginExclusive(boolean) beginExclusive}
 * <LI>{@link #endExclusive() endExclusive}
 * </UL>
 * <LI><B> 1-Wire Device Discovery </B>
 * <UL>
 * <LI>Selective Search Options
 * <UL>
 * <LI>{@link #targetAllFamilies() targetAllFamilies}
 * <LI>{@link #targetFamily(int) targetFamily(int)}
 * <LI>{@link #targetFamily(byte[]) targetFamily(byte[])}
 * <LI>{@link #excludeFamily(int) excludeFamily(int)}
 * <LI>{@link #excludeFamily(byte[]) excludeFamily(byte[])}
 * <LI>{@link #setSearchOnlyAlarmingDevices() setSearchOnlyAlarmingDevices}
 * <LI>{@link #setNoResetSearch() setNoResetSearch}
 * <LI>{@link #setSearchAllDevices() setSearchAllDevices}
 * </UL>
 * <LI>Search With Automatic 1-Wire Container creation
 * <UL>
 * <LI>{@link #getAllDeviceContainers() getAllDeviceContainers}
 * <LI>{@link #getFirstDeviceContainer() getFirstDeviceContainer}
 * <LI>{@link #getNextDeviceContainer() getNextDeviceContainer}
 * </UL>
 * <LI>Search With NO 1-Wire Container creation
 * <UL>
 * <LI>{@link #findFirstDevice() findFirstDevice}
 * <LI>{@link #findNextDevice() findNextDevice}
 * <LI>{@link #getAddress(byte[]) getAddress(byte[])}
 * <LI>{@link #getAddressAsLong() getAddressAsLong}
 * <LI>{@link #getAddressAsString() getAddressAsString}
 * </UL>
 * <LI>Manual 1-Wire Container creation
 * <UL>
 * <LI>{@link #getDeviceContainer(byte[]) getDeviceContainer(byte[])}
 * <LI>{@link #getDeviceContainer(long) getDeviceContainer(long)}
 * <LI>{@link #getDeviceContainer(String) getDeviceContainer(String)}
 * <LI>{@link #getDeviceContainer() getDeviceContainer()}
 * </UL>
 * </UL>
 * <LI><B> 1-Wire Network low level access (usually not called directly) </B>
 * <UL>
 * <LI>Device Selection and Presence Detect
 * <UL>
 * <LI>{@link #isPresent(byte[]) isPresent(byte[])}
 * <LI>{@link #isPresent(long) isPresent(long)}
 * <LI>{@link #isPresent(String) isPresent(String)}
 * <LI>{@link #isAlarming(byte[]) isAlarming(byte[])}
 * <LI>{@link #isAlarming(long) isAlarming(long)}
 * <LI>{@link #isAlarming(String) isAlarming(String)}
 * <LI>{@link #select(byte[]) select(byte[])}
 * <LI>{@link #select(long) select(long)}
 * <LI>{@link #select(String) select(String)}
 * </UL>
 * <LI>Raw 1-Wire IO
 * <UL>
 * <LI>{@link #reset() reset}
 * <LI>{@link #putBit(boolean) putBit}
 * <LI>{@link #getBit() getBit}
 * <LI>{@link #putByte(int) putByte}
 * <LI>{@link #getByte() getByte}
 * <LI>{@link #getBlock(int) getBlock(int)}
 * <LI>{@link #getBlock(byte[], int) getBlock(byte[], int)}
 * <LI>{@link #getBlock(byte[], int, int) getBlock(byte[], int, int)}
 * <LI>{@link #dataBlock(byte[], int, int) dataBlock(byte[], int, int)}
 * </UL>
 * <LI>1-Wire Speed and Power Selection
 * <UL>
 * <LI>{@link #setPowerDuration(int) setPowerDuration}
 * <LI>{@link #startPowerDelivery(int) startPowerDelivery}
 * <LI>{@link #setProgramPulseDuration(int) setProgramPulseDuration}
 * <LI>{@link #startProgramPulse(int) startProgramPulse}
 * <LI>{@link #startBreak() startBreak}
 * <LI>{@link #setPowerNormal() setPowerNormal}
 * <LI>{@link #setSpeed(int) setSpeed}
 * <LI>{@link #getSpeed() getSpeed}
 * </UL>
 * </UL>
 * <LI><B> Advanced </B>
 * <UL>
 * <LI>{@link #registerOneWireContainerClass(int, Class)
 * registerOneWireContainerClass}
 * </UL>
 * </UL>
 *
 * @see com.dalsemi.onewire.OneWireAccessProvider
 * @see com.dalsemi.onewire.container.OneWireContainer
 *
 * @version 0.01, 20 March 2001
 * @author DS
 */
public class TMEXAdapter extends DSPortAdapter {

	// --------
	// -------- Variables
	// --------

	/** flag to intidate if native driver got loaded */
	private static boolean driverLoaded = false;

	/** TMEX port type number (0-15) */
	protected int portType;

	/** Current 1-Wire Network Address */
	protected byte[] RomDta = new byte[8];

	/** Flag to indicate next search will look only for alarming devices */
	private boolean doAlarmSearch = false;

	/** Flag to indicate next search will be a 'first' */
	private boolean resetSearch = true;

	/** Flag to indicate next search will not be preceded by a 1-Wire reset */
	private boolean skipResetOnSearch = false;

	// --------
	// -------- Constructors/Destructor
	// --------

	/**
	 * Constructs a default adapter
	 *
	 * @throws ClassNotFoundException
	 */
	public TMEXAdapter() throws ClassNotFoundException {
		var nativeDLLName = "IBTMJAVA.dll";
		if (System.getProperty("os.arch").indexOf("64") != -1) {
			nativeDLLName = "ibtmjava64.dll";
		}
		// check if native driver got loaded
		if (!driverLoaded) {
			throw new ClassNotFoundException("native driver '" + nativeDLLName + "' not loaded");
		}

		// set default port type
		this.portType = getDefaultTypeNumber();

		// attempt to set the portType, will throw exception if does not exist
		if (!this.setPortType_Native(this.portType)) {
			throw new ClassNotFoundException("TMEX adapter type does not exist");
		}
	}

	/**
	 * Constructs with a specified port type
	 *
	 *
	 * @param newPortType
	 * @throws ClassNotFoundException
	 */
	public TMEXAdapter(int newPortType) throws ClassNotFoundException {

		// set default port type
		this.portType = newPortType;

		var nativeDLLName = "IBTMJAVA.dll";
		if (System.getProperty("os.arch").indexOf("64") != -1) {
			nativeDLLName = "ibtmjava64.dll";
		}

		// check if native driver got loaded
		if (!driverLoaded) {
			throw new ClassNotFoundException("native driver '" + nativeDLLName + "' not loaded");
		}

		// attempt to set the portType, will throw exception if does not exist
		if (!this.setPortType_Native(this.portType)) {
			throw new ClassNotFoundException("TMEX adapter type does not exist");
		}
	}

	/**
	 * Finalize to Cleanup native
	 */
	@Override
	protected void finalize() {
		this.cleanup_Native();
	}

	// --------
	// -------- Methods
	// --------

	/**
	 * Retrieve the name of the port adapter as a string. The 'Adapter' is a device
	 * that connects to a 'port' that allows one to communicate with an iButton or
	 * other 1-Wire device. As example of this is 'DS9097U'.
	 *
	 * @return <code>String</code> representation of the port adapter.
	 */
	@Override
	public native String getAdapterName();

	/**
	 * Retrieve a description of the port required by this port adapter. An example
	 * of a 'Port' would 'serial communication port'.
	 *
	 * @return <code>String</code> description of the port type required.
	 */
	@Override
	public native String getPortTypeDescription();

	/**
	 * Retrieve a version string for this class.
	 *
	 * @return version string
	 */
	@Override
	public String getClassVersion() {
		return ("0.02, native: " + this.getVersion_Native());
	}

	// --------
	// -------- Port Selection
	// --------

	/**
	 * Retrieve a list of the platform appropriate port names for this adapter. A
	 * port must be selected with the method 'selectPort' before any other
	 * communication methods can be used. Using a communication method before
	 * 'selectPort' will result in a <code>OneWireException</code> exception.
	 *
	 * @return enumeration of type <code>String</code> that contains the port names
	 */
	@Override
	public Enumeration<String> getPortNames() {
		var portVector = new Vector<String>();
		var header = this.getPortNameHeader_Native();

		for (var i = 0; i < 16; i++) {
			portVector.addElement((header + Integer.toString(i)));
		}

		return portVector.elements();
	}

	/**
	 * Specify a platform appropriate port name for this adapter. Note that even
	 * though the port has been selected, it's ownership may be relinquished if it
	 * is not currently held in a 'exclusive' block. This class will then try to
	 * re-acquire the port when needed. If the port cannot be re-acquired when the
	 * exception <code>PortInUseException</code> will be thrown.
	 *
	 * @param portName name of the target port, retrieved from getPortNames()
	 *
	 * @return <code>true</code> if the port was acquired, <code>false</code> if the
	 *         port is not available.
	 *
	 * @throws OneWireIOException If port does not exist, or unable to communicate
	 *                            with port.
	 * @throws OneWireException   If port does not exist
	 */
	@Override
	public native boolean selectPort(String portName) throws OneWireIOException, OneWireException;

	/**
	 * Free ownership of the selected port if it is currently owned back to the
	 * system. This should only be called if the recently selected port does not
	 * have an adapter or at the end of your application's use of the port.
	 *
	 * @throws OneWireException If port does not exist
	 */
	@Override
	public native void freePort() throws OneWireException;

	/**
	 * Retrieve the name of the selected port as a <code>String</code>.
	 *
	 * @return <code>String</code> of selected port
	 *
	 * @throws OneWireException if valid port not yet selected
	 */
	@Override
	public native String getPortName() throws OneWireException;

	// --------
	// -------- Adapter detection
	// --------

	/**
	 * Detect adapter presence on the selected port.
	 *
	 * @return <code>true</code> if the adapter is confirmed to be connected to the
	 *         selected port, <code>false</code> if the adapter is not connected.
	 *
	 * @throws OneWireIOException
	 * @throws OneWireException
	 */
	@Override
	public native boolean adapterDetected() throws OneWireIOException, OneWireException;

	/**
	 * Retrieve the version of the adapter.
	 *
	 * @return <code>String</code> of the adapter version. It will return "<na>" if
	 *         the adapter version is not or cannot be known.
	 *
	 * @throws OneWireIOException on a 1-Wire communication error such as no device
	 *                            present. This could be caused by a physical
	 *                            interruption in the 1-Wire Network due to shorts
	 *                            or a newly arriving 1-Wire device issuing a
	 *                            'presence pulse'.
	 * @throws OneWireException   on a communication or setup error with the 1-Wire
	 *                            adapter
	 */
	@Override
	public native String getAdapterVersion() throws OneWireIOException, OneWireException;

	/**
	 * Retrieve the address of the adapter if it has one.
	 *
	 * @return <code>String</code> of the adapter address. It will return "<na>" if
	 *         the adapter does not have an address. The address is a string
	 *         representation of an 1-Wire address.
	 *
	 * @throws OneWireIOException on a 1-Wire communication error such as no device
	 *                            present. This could be caused by a physical
	 *                            interruption in the 1-Wire Network due to shorts
	 *                            or a newly arriving 1-Wire device issuing a
	 *                            'presence pulse'.
	 * @throws OneWireException   on a communication or setup error with the 1-Wire
	 *                            adapter
	 * @see com.dalsemi.onewire.utils.Address
	 */
	@Override
	public String getAdapterAddress() throws OneWireIOException, OneWireException {
		return "<na>"; // ??? implement later
	}

	// --------
	// -------- Adapter features
	// --------

	/*
	 * The following interogative methods are provided so that client code can react
	 * selectively to underlying states without generating an exception.
	 */

	/**
	 * Returns whether adapter can physically support overdrive mode.
	 *
	 * @return <code>true</code> if this port adapter can do OverDrive,
	 *         <code>false</code> otherwise.
	 *
	 * @throws OneWireIOException on a 1-Wire communication error with the adapter
	 * @throws OneWireException   on a setup error with the 1-Wire adapter
	 */
	@Override
	public native boolean canOverdrive() throws OneWireIOException, OneWireException;

	/**
	 * Returns whether the adapter can physically support hyperdrive mode.
	 *
	 * @return <code>true</code> if this port adapter can do HyperDrive,
	 *         <code>false</code> otherwise.
	 *
	 * @throws OneWireIOException on a 1-Wire communication error with the adapter
	 * @throws OneWireException   on a setup error with the 1-Wire adapter
	 */
	@Override
	public native boolean canHyperdrive() throws OneWireIOException, OneWireException;

	/**
	 * Returns whether the adapter can physically support flex speed mode.
	 *
	 * @return <code>true</code> if this port adapter can do flex speed,
	 *         <code>false</code> otherwise.
	 *
	 * @throws OneWireIOException on a 1-Wire communication error with the adapter
	 * @throws OneWireException   on a setup error with the 1-Wire adapter
	 */
	@Override
	public native boolean canFlex() throws OneWireIOException, OneWireException;

	/**
	 * Returns whether adapter can physically support 12 volt power mode.
	 *
	 * @return <code>true</code> if this port adapter can do Program voltage,
	 *         <code>false</code> otherwise.
	 *
	 * @throws OneWireIOException on a 1-Wire communication error with the adapter
	 * @throws OneWireException   on a setup error with the 1-Wire adapter
	 */
	@Override
	public native boolean canProgram() throws OneWireIOException, OneWireException;

	/**
	 * Returns whether the adapter can physically support strong 5 volt power mode.
	 *
	 * @return <code>true</code> if this port adapter can do strong 5 volt mode,
	 *         <code>false</code> otherwise.
	 *
	 * @throws OneWireIOException on a 1-Wire communication error with the adapter
	 * @throws OneWireException   on a setup error with the 1-Wire adapter
	 */
	@Override
	public native boolean canDeliverPower() throws OneWireIOException, OneWireException;

	/**
	 * Returns whether the adapter can physically support "smart" strong 5 volt
	 * power mode. "smart" power delivery is the ability to deliver power until it
	 * is no longer needed. The current drop it detected and power delivery is
	 * stopped.
	 *
	 * @return <code>true</code> if this port adapter can do "smart" strong 5 volt
	 *         mode, <code>false</code> otherwise.
	 *
	 * @throws OneWireIOException on a 1-Wire communication error with the adapter
	 * @throws OneWireException   on a setup error with the 1-Wire adapter
	 */
	@Override
	public native boolean canDeliverSmartPower() throws OneWireIOException, OneWireException;

	/**
	 * Returns whether adapter can physically support 0 volt 'break' mode.
	 *
	 * @return <code>true</code> if this port adapter can do break,
	 *         <code>false</code> otherwise.
	 *
	 * @throws OneWireIOException on a 1-Wire communication error with the adapter
	 * @throws OneWireException   on a setup error with the 1-Wire adapter
	 */
	@Override
	public native boolean canBreak() throws OneWireIOException, OneWireException;

	// --------
	// -------- Finding iButtons and 1-Wire devices
	// --------

	/**
	 * Returns <code>true</code> if the first iButton or 1-Wire device is found on
	 * the 1-Wire Network. If no devices are found, then <code>false</code> will be
	 * returned.
	 *
	 * @return <code>true</code> if an iButton or 1-Wire device is found.
	 *
	 * @throws OneWireIOException on a 1-Wire communication error
	 * @throws OneWireException   on a setup error with the 1-Wire adapter
	 */
	@Override
	public boolean findFirstDevice() throws OneWireIOException, OneWireException {

		// reset the internal rom buffer
		this.resetSearch = true;

		return this.findNextDevice();
	}

	/**
	 * Returns <code>true</code> if the next iButton or 1-Wire device is found. The
	 * previous 1-Wire device found is used as a starting point in the search. If no
	 * more devices are found then <code>false</code> will be returned.
	 *
	 * @return <code>true</code> if an iButton or 1-Wire device is found.
	 *
	 * @throws OneWireIOException on a 1-Wire communication error
	 * @throws OneWireException   on a setup error with the 1-Wire adapter
	 */
	@Override
	public boolean findNextDevice() throws OneWireIOException, OneWireException {
		boolean retval;

		while (true) {
			retval = this.romSearch_Native(this.skipResetOnSearch, this.resetSearch, this.doAlarmSearch, this.RomDta);

			if (!retval) {
				this.resetSearch = true;

				return false;
			}
			this.resetSearch = false;

			// check if this is an OK family type
			if (this.isValidFamily(this.RomDta)) {
				return true;
			}
		}
	}

	/**
	 * Copies the 'current' iButton address being used by the adapter into the
	 * array. This address is the last iButton or 1-Wire device found in a search
	 * (findNextDevice()...). This method copies into a user generated array to
	 * allow the reuse of the buffer. When searching many iButtons on the one wire
	 * network, this will reduce the memory burn rate.
	 *
	 * @param address An array to be filled with the current iButton address.
	 * @see com.dalsemi.onewire.utils.Address
	 */
	@Override
	public void getAddress(byte[] address) {
		System.arraycopy(this.RomDta, 0, address, 0, 8);
	}

	/**
	 * Verifies that the iButton or 1-Wire device specified is present on the 1-Wire
	 * Network. This does not affect the 'current' device state information used in
	 * searches (findNextDevice...).
	 *
	 * @param address device address to verify is present
	 *
	 * @return <code>true</code> if device is present else <code>false</code>.
	 *
	 * @throws OneWireIOException on a 1-Wire communication error
	 * @throws OneWireException   on a setup error with the 1-Wire adapter
	 *
	 * @see com.dalsemi.onewire.utils.Address
	 */
	@Override
	public native boolean isPresent(byte[] address) throws OneWireIOException, OneWireException;

	/**
	 * Verifies that the iButton or 1-Wire device specified is present on the 1-Wire
	 * Network and in an alarm state. This does not affect the 'current' device
	 * state information used in searches (findNextDevice...).
	 *
	 * @param address device address to verify is present and alarming
	 *
	 * @return <code>true</code> if device is present and alarming else
	 *         <code>false</code>.
	 *
	 * @throws OneWireIOException on a 1-Wire communication error
	 * @throws OneWireException   on a setup error with the 1-Wire adapter
	 *
	 * @see com.dalsemi.onewire.utils.Address
	 */
	@Override
	public native boolean isAlarming(byte[] address) throws OneWireIOException, OneWireException;

	/**
	 * Selects the specified iButton or 1-Wire device by broadcasting its address.
	 * This operation is referred to a 'MATCH ROM' operation in the iButton and
	 * 1-Wire device data sheets. This does not affect the 'current' device state
	 * information used in searches (findNextDevice...).
	 *
	 * Warning, this does not verify that the device is currently present on the
	 * 1-Wire Network (See isPresent).
	 *
	 * @param address iButton to select
	 *
	 * @return <code>true</code> if device address was sent,<code>false</code>
	 *         otherwise.
	 *
	 * @throws OneWireIOException on a 1-Wire communication error
	 * @throws OneWireException   on a setup error with the 1-Wire adapter
	 *
	 * @see com.dalsemi.onewire.adapter.DSPortAdapter#isPresent(byte[] address)
	 * @see com.dalsemi.onewire.utils.Address
	 */
	@Override
	public native boolean select(byte[] address) throws OneWireIOException, OneWireException;

	// --------
	// -------- Finding iButton/1-Wire device options
	// --------

	/**
	 * Set the 1-Wire Network search to find only iButtons and 1-Wire devices that
	 * are in an 'Alarm' state that signals a need for attention. Not all iButton
	 * types have this feature. Some that do: DS1994, DS1920, DS2407. This selective
	 * searching can be canceled with the 'setSearchAllDevices()' method.
	 *
	 * @see #setNoResetSearch
	 */
	@Override
	public void setSearchOnlyAlarmingDevices() {
		this.doAlarmSearch = true;
	}

	/**
	 * Set the 1-Wire Network search to not perform a 1-Wire reset before a search.
	 * This feature is chiefly used with the DS2409 1-Wire coupler. The normal reset
	 * before each search can be restored with the 'setSearchAllDevices()' method.
	 */
	@Override
	public void setNoResetSearch() {
		this.skipResetOnSearch = true;
	}

	/**
	 * Set the 1-Wire Network search to find all iButtons and 1-Wire devices whether
	 * they are in an 'Alarm' state or not and restores the default setting of
	 * providing a 1-Wire reset command before each search. (see setNoResetSearch()
	 * method).
	 *
	 * @see #setNoResetSearch
	 */
	@Override
	public void setSearchAllDevices() {
		this.doAlarmSearch = false;
		this.skipResetOnSearch = false;
	}

	// --------
	// -------- 1-Wire Network Semaphore methods
	// --------

	/**
	 * Gets exclusive use of the 1-Wire to communicate with an iButton or 1-Wire
	 * Device. This method should be used for critical sections of code where a
	 * sequence of commands must not be interrupted by communication of threads with
	 * other iButtons, and it is permissible to sustain a delay in the special case
	 * that another thread has already been granted exclusive access and this access
	 * has not yet been relinquished.
	 * <p>
	 *
	 * It can be called through the OneWireContainer class by the end application if
	 * they want to ensure exclusive use. If it is not called around several methods
	 * then it will be called inside each method.
	 *
	 * @param blocking <code>true</code> if want to block waiting for exclusive
	 *                 access to the adapter
	 * @return <code>true</code> if blocking was false and a exclusive session with
	 *         the adapter was acquired
	 *
	 * @throws OneWireException on a setup error with the 1-Wire adapter
	 */
	@Override
	public native boolean beginExclusive(boolean blocking) throws OneWireException;

	/**
	 * Relinquishes exclusive control of the 1-Wire Network. This command
	 * dynamically marks the end of a critical section and should be used when
	 * exclusive control is no longer needed.
	 */
	@Override
	public native void endExclusive();

	// --------
	// -------- Primitive 1-Wire Network data methods
	// --------

	/**
	 * Sends a bit to the 1-Wire Network.
	 *
	 * @param bitValue the bit value to send to the 1-Wire Network.
	 *
	 * @throws OneWireIOException on a 1-Wire communication error
	 * @throws OneWireException   on a setup error with the 1-Wire adapter
	 */
	@Override
	public void putBit(boolean bitValue) throws OneWireIOException, OneWireException {
		if (this.dataBit_Native(bitValue) != bitValue) {
			throw new OneWireIOException("Error during putBit()");
		}
	}

	/**
	 * Gets a bit from the 1-Wire Network.
	 *
	 * @return the bit value received from the the 1-Wire Network.
	 *
	 * @throws OneWireIOException on a 1-Wire communication error
	 * @throws OneWireException   on a setup error with the 1-Wire adapter
	 */
	@Override
	public boolean getBit() throws OneWireIOException, OneWireException {
		return this.dataBit_Native(true);
	}

	/**
	 * Sends a byte to the 1-Wire Network.
	 *
	 * @param byteValue the byte value to send to the 1-Wire Network.
	 *
	 * @throws OneWireIOException on a 1-Wire communication error
	 * @throws OneWireException   on a setup error with the 1-Wire adapter
	 */
	@Override
	public void putByte(int byteValue) throws OneWireIOException, OneWireException {
		if (this.dataByte_Native(byteValue & 0x00FF) != (0x00FF & byteValue)) {
			throw new OneWireIOException("Error during putByte(), echo was incorrect ");
		}
	}

	/**
	 * Gets a byte from the 1-Wire Network.
	 *
	 * @return the byte value received from the the 1-Wire Network.
	 *
	 * @throws OneWireIOException on a 1-Wire communication error
	 * @throws OneWireException   on a setup error with the 1-Wire adapter
	 */
	@Override
	public int getByte() throws OneWireIOException, OneWireException {
		return this.dataByte_Native(0x00FF);
	}

	/**
	 * Get a block of data from the 1-Wire Network.
	 *
	 * @param len length of data bytes to receive
	 *
	 * @return the data received from the 1-Wire Network.
	 *
	 * @throws OneWireIOException on a 1-Wire communication error
	 * @throws OneWireException   on a setup error with the 1-Wire adapter
	 */
	@Override
	public byte[] getBlock(int len) throws OneWireIOException, OneWireException {
		var barr = new byte[len];

		this.getBlock(barr, 0, len);

		return barr;
	}

	/**
	 * Get a block of data from the 1-Wire Network and write it into the provided
	 * array.
	 *
	 * @param arr array in which to write the received bytes
	 * @param len length of data bytes to receive
	 *
	 * @throws OneWireIOException on a 1-Wire communication error
	 * @throws OneWireException   on a setup error with the 1-Wire adapter
	 */
	@Override
	public void getBlock(byte[] arr, int len) throws OneWireIOException, OneWireException {
		this.getBlock(arr, 0, len);
	}

	/**
	 * Get a block of data from the 1-Wire Network and write it into the provided
	 * array.
	 *
	 * @param arr array in which to write the received bytes
	 * @param off offset into the array to start
	 * @param len length of data bytes to receive
	 *
	 * @throws OneWireIOException on a 1-Wire communication error
	 * @throws OneWireException   on a setup error with the 1-Wire adapter
	 */
	@Override
	public native void getBlock(byte[] arr, int off, int len) throws OneWireIOException, OneWireException;

	/**
	 * Sends a block of data and returns the data received in the same array. This
	 * method is used when sending a block that contains reads and writes. The
	 * 'read' portions of the data block need to be pre-loaded with 0xFF's. It
	 * starts sending data from the index at offset 'off' for length 'len'.
	 *
	 * @param dataBlock array of data to transfer to and from the 1-Wire Network.
	 * @param off       offset into the array of data to start
	 * @param len       length of data to send / receive starting at 'off'
	 *
	 * @throws OneWireIOException on a 1-Wire communication error
	 * @throws OneWireException   on a setup error with the 1-Wire adapter
	 */
	@Override
	public native void dataBlock(byte dataBlock[], int off, int len) throws OneWireIOException, OneWireException;

	/**
	 * Sends a Reset to the 1-Wire Network.
	 *
	 * @return the result of the reset. Potential results are:
	 *         <ul>
	 *         <li>0 (RESET_NOPRESENCE) no devices present on the 1-Wire Network.
	 *         <li>1 (RESET_PRESENCE) normal presence pulse detected on the 1-Wire
	 *         Network indicating there is a device present.
	 *         <li>2 (RESET_ALARM) alarming presence pulse detected on the 1-Wire
	 *         Network indicating there is a device present and it is in the alarm
	 *         condition. This is only provided by the DS1994/DS2404 devices.
	 *         <li>3 (RESET_SHORT) inticates 1-Wire appears shorted. This can be
	 *         transient conditions in a 1-Wire Network. Not all adapter types can
	 *         detect this condition.
	 *         </ul>
	 *
	 * @throws OneWireIOException on a 1-Wire communication error
	 * @throws OneWireException   on a setup error with the 1-Wire adapter
	 */
	@Override
	public native int reset() throws OneWireIOException, OneWireException;

	// --------
	// -------- 1-Wire Network power methods
	// --------

	/**
	 * Sets the duration to supply power to the 1-Wire Network. This method takes a
	 * time parameter that indicates the program pulse length when the method
	 * startPowerDelivery().
	 * <p>
	 *
	 * Note: to avoid getting an exception, use the canDeliverPower() and
	 * canDeliverSmartPower() method to check it's availability.
	 * <p>
	 *
	 * @param timeFactor
	 *                   <ul>
	 *                   <li>0 (DELIVERY_HALF_SECOND) provide power for 1/2 second.
	 *                   <li>1 (DELIVERY_ONE_SECOND) provide power for 1 second.
	 *                   <li>2 (DELIVERY_TWO_SECONDS) provide power for 2 seconds.
	 *                   <li>3 (DELIVERY_FOUR_SECONDS) provide power for 4 seconds.
	 *                   <li>4 (DELIVERY_SMART_DONE) provide power until the the
	 *                   device is no longer drawing significant power.
	 *                   <li>5 (DELIVERY_INFINITE) provide power until the
	 *                   setPowerNormal() method is called.
	 *                   </ul>
	 *
	 * @throws OneWireIOException on a 1-Wire communication error
	 * @throws OneWireException   on a setup error with the 1-Wire adapter
	 */
	@Override
	public void setPowerDuration(int timeFactor) throws OneWireIOException, OneWireException {

		// Right now we only support infinite pull up.
		if (timeFactor != DELIVERY_INFINITE) {
			throw new OneWireException("No support for other than infinite power duration");
		}
	}

	/**
	 * Sets the 1-Wire Network voltage to supply power to an iButton device. This
	 * method takes a time parameter that indicates whether the power delivery
	 * should be done immediately, or after certain conditions have been met.
	 * <p>
	 *
	 * Note: to avoid getting an exception, use the canDeliverPower() and
	 * canDeliverSmartPower() method to check it's availability.
	 * <p>
	 *
	 * @param changeCondition
	 *                        <ul>
	 *                        <li>0 (CONDITION_NOW) operation should occur
	 *                        immediately.
	 *                        <li>1 (CONDITION_AFTER_BIT) operation should be
	 *                        pending execution immediately after the next bit is
	 *                        sent.
	 *                        <li>2 (CONDITION_AFTER_BYTE) operation should be
	 *                        pending execution immediately after next byte is sent.
	 *                        </ul>
	 *
	 * @return <code>true</code> if the voltage change was successful,
	 *         <code>false</code> otherwise.
	 *
	 * @throws OneWireIOException on a 1-Wire communication error
	 * @throws OneWireException   on a setup error with the 1-Wire adapter
	 */
	@Override
	public native boolean startPowerDelivery(int changeCondition) throws OneWireIOException, OneWireException;

	/**
	 * Sets the duration for providing a program pulse on the 1-Wire Network. This
	 * method takes a time parameter that indicates the program pulse length when
	 * the method startProgramPulse().
	 * <p>
	 *
	 * Note: to avoid getting an exception, use the canDeliverPower() method to
	 * check it's availability.
	 * <p>
	 *
	 * @param timeFactor
	 *                   <ul>
	 *                   <li>6 (DELIVERY_EPROM) provide program pulse for 480
	 *                   microseconds
	 *                   <li>5 (DELIVERY_INFINITE) provide power until the
	 *                   setPowerNormal() method is called.
	 *                   </ul>
	 *
	 * @throws OneWireIOException on a 1-Wire communication error
	 * @throws OneWireException   on a setup error with the 1-Wire adapter
	 */
	@Override
	public void setProgramPulseDuration(int timeFactor) throws OneWireIOException, OneWireException {
		if (timeFactor != DELIVERY_EPROM) {
			throw new OneWireException("Only support EPROM length program pulse duration");
		}
	}

	/**
	 * Sets the 1-Wire Network voltage to eprom programming level. This method takes
	 * a time parameter that indicates whether the power delivery should be done
	 * immediately, or after certain conditions have been met.
	 * <p>
	 *
	 * Note: to avoid getting an exception, use the canProgram() method to check
	 * it's availability.
	 * <p>
	 *
	 * @param changeCondition
	 *                        <ul>
	 *                        <li>0 (CONDITION_NOW) operation should occur
	 *                        immediately.
	 *                        <li>1 (CONDITION_AFTER_BIT) operation should be
	 *                        pending execution immediately after the next bit is
	 *                        sent.
	 *                        <li>2 (CONDITION_AFTER_BYTE) operation should be
	 *                        pending execution immediately after next byte is sent.
	 *                        </ul>
	 *
	 * @return <code>true</code> if the voltage change was successful,
	 *         <code>false</code> otherwise.
	 *
	 * @throws OneWireIOException on a 1-Wire communication error
	 * @throws OneWireException   on a setup error with the 1-Wire adapter or the
	 *                            adapter does not support this operation
	 */
	@Override
	public native boolean startProgramPulse(int changeCondition) throws OneWireIOException, OneWireException;

	/**
	 * Sets the 1-Wire Network voltage to 0 volts. This method is used rob all
	 * 1-Wire Network devices of parasite power delivery to force them into a hard
	 * reset.
	 *
	 * @throws OneWireIOException on a 1-Wire communication error
	 * @throws OneWireException   on a setup error with the 1-Wire adapter or the
	 *                            adapter does not support this operation
	 */
	@Override
	public native void startBreak() throws OneWireIOException, OneWireException;

	/**
	 * Sets the 1-Wire Network voltage to normal level. This method is used to
	 * disable 1-Wire conditions created by startPowerDelivery and
	 * startProgramPulse. This method will automatically be called if a
	 * communication method is called while an outstanding power command is taking
	 * place.
	 *
	 * @throws OneWireIOException on a 1-Wire communication error
	 * @throws OneWireException   on a setup error with the 1-Wire adapter or the
	 *                            adapter does not support this operation
	 */
	@Override
	public native void setPowerNormal() throws OneWireIOException, OneWireException;

	// --------
	// -------- 1-Wire Network speed methods
	// --------

	/**
	 * This method takes an int representing the new speed of data transfer on the
	 * 1-Wire Network.
	 * <p>
	 *
	 * @param speed
	 *                     <ul>
	 *                     <li>0 (SPEED_REGULAR) set to normal communication speed
	 *                     <li>1 (SPEED_FLEX) set to flexible communication speed
	 *                     used for long lines
	 *                     <li>2 (SPEED_OVERDRIVE) set to normal communication speed
	 *                     to overdrive
	 *                     <li>3 (SPEED_HYPERDRIVE) set to normal communication
	 *                     speed to hyperdrive
	 *                     <li>>3 future speeds
	 *                     </ul>
	 *
	 * @param desiredSpeed
	 *
	 * @throws OneWireIOException on a 1-Wire communication error
	 * @throws OneWireException   on a setup error with the 1-Wire adapter or the
	 *                            adapter does not support this operation
	 */
	@Override
	public native void setSpeed(int desiredSpeed) throws OneWireIOException, OneWireException;

	/**
	 * This method returns the current data transfer speed through a port to a
	 * 1-Wire Network.
	 * <p>
	 *
	 * @return
	 *         <ul>
	 *         <li>0 (SPEED_REGULAR) set to normal communication speed
	 *         <li>1 (SPEED_FLEX) set to flexible communication speed used for long
	 *         lines
	 *         <li>2 (SPEED_OVERDRIVE) set to normal communication speed to
	 *         overdrive
	 *         <li>3 (SPEED_HYPERDRIVE) set to normal communication speed to
	 *         hyperdrive
	 *         <li>>3 future speeds
	 *         </ul>
	 */
	@Override
	public native int getSpeed();

	// --------
	// -------- Misc
	// --------

	/**
	 * Select the TMEX specified port type (0 to 15) Use this method if the
	 * constructor with the PortType cannot be used.
	 *
	 *
	 * @param newPortType
	 * @return true if port type valid. Instance is only usable if this returns
	 *         false.
	 */
	public boolean setTMEXPortType(int newPortType) {

		// set default port type
		this.portType = newPortType;

		// attempt to set the portType, return result
		return this.setPortType_Native(this.portType);
	}

	// --------
	// -------- Additional Native Methods
	// --------

	/**
	 * CleanUp the native state for classes owned by the provided thread.
	 */
	public static native void CleanUpByThread(Thread thread);

	/**
	 * Get the default Adapter Name.
	 *
	 * @return String containing the name of the default adapter
	 */
	public static native String getDefaultAdapterName();

	/**
	 * Get the default Adapter Port name.
	 *
	 * @return String containing the name of the default adapter port
	 */
	public static native String getDefaultPortName();

	/**
	 * Get the default Adapter Type number.
	 *
	 * @return int, the default adapter type
	 */
	private static native int getDefaultTypeNumber();

	/**
	 * Attempt to set the desired TMEX Port type. This native call will attempt to
	 * get a session handle to verify that the portType exists.
	 *
	 * @return true if portType exists, false if not
	 */
	private native boolean setPortType_Native(int portType);

	/**
	 * Perform a 1-Wire bit operation
	 *
	 * @param bitValue boolean bit value, true=1, false=0 to send to 1-Wire net
	 *
	 * @return boolean true for 1 return , false for 0 return
	 *
	 * @throws OneWireIOException on a 1-Wire communication error
	 * @throws OneWireException   on a setup error with the 1-Wire adapter
	 */
	private native boolean dataBit_Native(boolean bitValue) throws OneWireIOException, OneWireException;

	/**
	 * Perform a 1-Wire byte operation
	 *
	 * @param byteValue integer with ls byte containing the 8 bits value to send to
	 *                  the 1-Wire net
	 *
	 * @return int containing the 1-Wire return 8 bits in the ls byte.
	 *
	 * @throws OneWireIOException on a 1-Wire communication error
	 * @throws OneWireException   on a setup error with the 1-Wire adapter
	 */
	private native int dataByte_Native(int byteValue) throws OneWireIOException, OneWireException;

	/**
	 * Get the TMEX main and porttype version strings concatenated
	 *
	 * @return string containing the TMEX version
	 */
	private native String getVersion_Native();

	/**
	 * Perform a search
	 *
	 * @param skipResetOnSearch boolean, true to skip 1-Wire reset on search
	 * @param resetSearch       boolean, true to reset search (First)
	 * @param doAlarmSearch     boolean, true if only want to find alarming
	 * @param RomDta            byte array to hold ROM of device found
	 *
	 * @return boolean, true if search found a device else false
	 *
	 * @throws OneWireIOException on a 1-Wire communication error
	 * @throws OneWireException   on a setup error with the 1-Wire adapter
	 */
	private native boolean romSearch_Native(boolean skipResetOnSearch, boolean resetSearch, boolean doAlarmSearch,
			byte[] RomDta) throws OneWireIOException, OneWireException;

	/**
	 * Return the port name header (taken from porttype version)
	 *
	 * @return String containing the port name header
	 */
	private native String getPortNameHeader_Native();

	/**
	 * Cleanup native (called on finalize of this instance)
	 */
	private native void cleanup_Native();

	// --------
	// -------- Native driver loading
	// --------

	/**
	 * Static method called before instance is created. Attempt verify native
	 * driver's installed and to load the native TMEX interface code: ibtmjava.dll
	 * for x86 and ibtmjava64.dll for x64.
	 */
	static {
		driverLoaded = false;
		var nativeTMEXDLLName = "IBFS32.DLL";
		var nativeDLLName = "ibtmjava.dll";
		var loadLibraryString = "ibtmjava";
		if (System.getProperty("os.arch").indexOf("64") != -1) {
			nativeTMEXDLLName = "IBFS64.DLL";
			nativeDLLName = "ibtmjava64.dll";
			loadLibraryString = "ibtmjava64";
		}

		// check if on OS that can have native TMEX drivers
		if (System.getProperty("os.name").indexOf("Windows") != -1) {

			// check if TMEX native drivers installed
			int index = 0, last_index = 0;
			var search_path = System.getProperty("java.library.path");
			String path;
			File file;
			var tmex_loaded = false;

			System.out.println(search_path);
			// check for a path to search
			if (search_path != null) {
				// loop to look through the library search path
				do {
					index = search_path.indexOf(File.pathSeparatorChar, last_index);

					if (index > -1) {
						path = search_path.substring(last_index, index);

						// look to see if main TMEX dll is in this path
						file = new File(path + File.separator + nativeTMEXDLLName);

						if (file.exists()) {
							tmex_loaded = true;

							break;
						}
					}

					last_index = index + 1;
				} while (index > -1);
			}
			// jdk must not support "java.library.path" so assume it is loaded
			else {
				tmex_loaded = true;
			}

			if (tmex_loaded) {
				try {
					System.loadLibrary(loadLibraryString);
					driverLoaded = true;
				} catch (UnsatisfiedLinkError e) {
					if (search_path != null) {
						System.err.println("Could not load Java to TMEX-native bridge driver: " + nativeDLLName);
					} else {
						System.err.println("Native drivers not found, download 1-Wire Drivers from www.ibutton.com");
					}
				}
			} else {
				System.err.println("Native drivers not found, download 1-Wire Drivers from www.ibutton.com");
			}
		}
	}
}
// CHECKSTYLE:ON
