// CHECKSTYLE:OFF

/*---------------------------------------------------------------------------
 * Copyright (C) 1999,2000 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.container;

import com.dalsemi.onewire.OneWireException;
// imports
import com.dalsemi.onewire.adapter.DSPortAdapter;
import com.dalsemi.onewire.adapter.OneWireIOException;

/**
 * <P>
 * 1-Wire&reg; container for a Single Addressable Switch, DS2405. This container
 * encapsulates the functionality of the 1-Wire family type <B>05</B> (hex)
 * </P>
 *
 * <H3>Features</H3>
 * <UL>
 * <LI>Open drain PIO pin controlled through 1-Wire communication
 * <LI>Logic level sensing of the PIO pin can be sensed
 * <LI>Operating temperature range from -40 to +85
 * <li>One channel with level sensing abilities
 * <li>Does not support activity sensing or 'Smart On' capabilities
 * </UL>
 *
 * <H3>Memory</H3>
 *
 * <P>
 * The DS2405 has no memory beyond its 64-bit registration number.
 * </P>
 *
 *
 * <H3>Usage</H3>
 *
 * <p>
 * The DS2405 supports level sensing, but not activity sensing. The code below
 * reads the state of the DS2405, extracting the latch state and the sensed
 * level of the PIO pin, then in the loop it toggles the latch state.
 * </p>
 *
 * <pre>
 * // "ID" is a byte array of size 8 with an address of a part we
 * // have already found with family code 05 hex
 * // "access" is a DSPortAdapter
 *
 * int i = 0;
 * OneWireContainer05 ds2405 = (OneWireContainer05) access.getDeviceContainer(ID);
 * ds2405.setupContainer(access, ID);
 *
 * byte[] state = ds2405.readDevice();
 *
 * // I know that the 2405 only has one channel (one switch)
 * // and it doesn't support 'Smart On'
 *
 * boolean latch_state = ds2405.getLatchState(0, state);
 * System.out.println("Current state of switch: " + latch_state);
 * System.out.println("Current output level:    " + ds2405.getLevel(0, state));
 * while (++i &lt; 100) {
 * 	System.out.println("Toggling switch");
 * 	ds2405.setLatchState(0, !latch_state, false, state);
 * 	ds2405.writeDevice(state);
 * 	state = ds2405.readDevice();
 * 	latch_state = ds2405.getLatchState(0, state);
 * 	System.out.println("Current state of switch: " + latch_state);
 * 	System.out.println("Current output level:    " + ds2405.getLevel(0, state));
 * 	Thread.sleep(500);
 * }
 *
 * </pre>
 *
 * <p>
 * Also see the usage example in the
 * {@link com.dalsemi.onewire.container.SwitchContainer SwithContainer}
 * interface.
 * </p>
 *
 * <H3>DataSheet</H3>
 * <DL>
 * <DD><A HREF="http://pdfserv.maxim-ic.com/arpdf/DS2405.pdf">
 * http://pdfserv.maxim-ic.com/arpdf/DS2405.pdf</A>
 * </DL>
 *
 * Also see the {@link com.dalsemi.onewire.container.OneWireContainer12 DS2406},
 * a dual addressable switch (OneWireContainer12).
 *
 * @see com.dalsemi.onewire.container.OneWireSensor
 * @see com.dalsemi.onewire.container.SwitchContainer
 * @see com.dalsemi.onewire.container.OneWireContainer12
 *
 * @version 0.00, 28 Aug 2000
 * @author KLA,DSS
 */
public class OneWireContainer05 extends OneWireContainer implements SwitchContainer {

	// --------
	// -------- Constructors
	// --------

	/**
	 * Creates a new <code>OneWireContainer</code> for communication with a DS2405.
	 * Note that the method
	 * <code>setupContainer(com.dalsemi.onewire.adapter.DSPortAdapter,byte[])</code>
	 * must be called to set the correct <code>DSPortAdapter</code> device address.
	 *
	 * @see com.dalsemi.onewire.container.OneWireContainer#setupContainer(com.dalsemi.onewire.adapter.DSPortAdapter,byte[])
	 *      setupContainer(DSPortAdapter,byte[])
	 * @see #OneWireContainer05(com.dalsemi.onewire.adapter.DSPortAdapter,byte[])
	 *      OneWireContainer05(DSPortAdapter,byte[])
	 * @see #OneWireContainer05(com.dalsemi.onewire.adapter.DSPortAdapter,long)
	 *      OneWireContainer05(DSPortAdapter,long)
	 * @see #OneWireContainer05(com.dalsemi.onewire.adapter.DSPortAdapter,java.lang.String)
	 *      OneWireContainer05(DSPortAdapter,String)
	 */
	public OneWireContainer05() {
	}

	/**
	 * Creates a new <code>OneWireContainer</code> for communication with a DS2405.
	 *
	 * @param sourceAdapter adapter object required to communicate with this 1-Wire
	 *                      device
	 * @param newAddress    address of this DS2405
	 *
	 * @see #OneWireContainer05()
	 * @see #OneWireContainer05(com.dalsemi.onewire.adapter.DSPortAdapter,long)
	 *      OneWireContainer05(DSPortAdapter,long)
	 * @see #OneWireContainer05(com.dalsemi.onewire.adapter.DSPortAdapter,java.lang.String)
	 *      OneWireContainer05(DSPortAdapter,String)
	 */
	public OneWireContainer05(DSPortAdapter sourceAdapter, byte[] newAddress) {
		super(sourceAdapter, newAddress);
	}

	/**
	 * Creates a new <code>OneWireContainer</code> for communication with a DS2405.
	 *
	 * @param sourceAdapter adapter object required to communicate with this 1-Wire
	 *                      device
	 * @param newAddress    address of this DS2405
	 *
	 * @see #OneWireContainer05()
	 * @see #OneWireContainer05(com.dalsemi.onewire.adapter.DSPortAdapter,byte[])
	 *      OneWireContainer05(DSPortAdapter,byte[])
	 * @see #OneWireContainer05(com.dalsemi.onewire.adapter.DSPortAdapter,java.lang.String)
	 *      OneWireContainer05(DSPortAdapter,String)
	 */
	public OneWireContainer05(DSPortAdapter sourceAdapter, long newAddress) {
		super(sourceAdapter, newAddress);
	}

	/**
	 * Creates a new <code>OneWireContainer</code> for communication with a DS2405.
	 *
	 * @param sourceAdapter adapter object required to communicate with this 1-Wire
	 *                      device
	 * @param newAddress    address of this DS2405
	 *
	 * @see #OneWireContainer05()
	 * @see #OneWireContainer05(com.dalsemi.onewire.adapter.DSPortAdapter,byte[])
	 *      OneWireContainer05(DSPortAdapter,byte[])
	 * @see #OneWireContainer05(com.dalsemi.onewire.adapter.DSPortAdapter,long)
	 *      OneWireContainer05(DSPortAdapter,long)
	 */
	public OneWireContainer05(DSPortAdapter sourceAdapter, String newAddress) {
		super(sourceAdapter, newAddress);
	}

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

	/**
	 * Gets the Maxim Integrated Products part number of the iButton or 1-Wire
	 * Device as a <code>java.lang.String</code>. For example "DS1992".
	 *
	 * @return iButton or 1-Wire device name
	 */
	@Override
	public String getName() {
		return "DS2405";
	}

	/**
	 * Retrieves the alternate Maxim Integrated Products part numbers or names. A
	 * 'family' of MicroLAN devices may have more than one part number depending on
	 * packaging. There can also be nicknames such as "Crypto iButton".
	 *
	 * @return the alternate names for this iButton or 1-Wire device
	 */
	@Override
	public String getAlternateNames() {
		return "Addressable Switch";
	}

	/**
	 * Gets a short description of the function of this iButton or 1-Wire Device
	 * type.
	 *
	 * @return device description
	 */
	@Override
	public String getDescription() {
		return """
				Addressable Switch with controlled open drain PIO \
				pin. PIO pin sink capability is greater than 4mA \
				at 0.4V.""";
	}

	// --------
	// -------- Switch Feature methods
	// --------

	/**
	 * Gets the number of channels supported by this switch. Channel specific
	 * methods will use a channel number specified by an integer from [0 to
	 * (<code>getNumberChannels(byte[])</code> - 1)]. Note that all devices of the
	 * same family will not necessarily have the same number of channels.
	 *
	 * @param state current state of the device returned from
	 *              <code>readDevice()</code>
	 *
	 * @return the number of channels for this device
	 *
	 * @see com.dalsemi.onewire.container.OneWireSensor#readDevice()
	 */
	@Override
	public int getNumberChannels(byte[] state) {
		// we ignore the state, DS2405 can only have one channel
		return 1;
	}

	/**
	 * Checks if the channels of this switch are 'high side' switches. This
	 * indicates that when 'on' or <code>true</code>, the switch output is connect
	 * to the 1-Wire data. If this method returns <code>false</code> then when the
	 * switch is 'on' or <code>true</code>, the switch is connected to ground.
	 *
	 * @return <code>true</code> if the switch is a 'high side' switch,
	 *         <code>false</code> if the switch is a 'low side' switch
	 *
	 * @see #getLatchState(int,byte[])
	 */
	@Override
	public boolean isHighSideSwitch() {
		return false;
	}

	/**
	 * Checks if the channels of this switch support activity sensing. If this
	 * method returns <code>true</code> then the method
	 * <code>getSensedActivity(int,byte[])</code> can be used.
	 *
	 * @return <code>true</code> if channels support activity sensing
	 *
	 * @see #getSensedActivity(int,byte[])
	 * @see #clearActivity()
	 */
	@Override
	public boolean hasActivitySensing() {
		return false;
	}

	/**
	 * Checks if the channels of this switch support level sensing. If this method
	 * returns <code>true</code> then the method <code>getLevel(int,byte[])</code>
	 * can be used.
	 *
	 * @return <code>true</code> if channels support level sensing
	 *
	 * @see #getLevel(int,byte[])
	 */
	@Override
	public boolean hasLevelSensing() {
		return true;
	}

	/**
	 * Checks if the channels of this switch support 'smart on'. Smart on is the
	 * ability to turn on a channel such that only 1-Wire device on this channel are
	 * awake and ready to do an operation. This greatly reduces the time to discover
	 * the device down a branch. If this method returns <code>true</code> then the
	 * method <code>setLatchState(int,boolean,boolean,byte[])</code> can be used
	 * with the <code>doSmart</code> parameter <code>true</code>.
	 *
	 * @return <code>true</code> if channels support 'smart on'
	 *
	 * @see #setLatchState(int,boolean,boolean,byte[])
	 */
	@Override
	public boolean hasSmartOn() {
		return false;
	}

	/**
	 * Checks if the channels of this switch require that only one channel is on at
	 * any one time. If this method returns <code>true</code> then the method
	 * <code>setLatchState(int,boolean,boolean,byte[])</code> will not only affect
	 * the state of the given channel but may affect the state of the other channels
	 * as well to insure that only one channel is on at a time.
	 *
	 * @return <code>true</code> if only one channel can be on at a time.
	 *
	 * @see #setLatchState(int,boolean,boolean,byte[])
	 */
	@Override
	public boolean onlySingleChannelOn() {
		return true;
	}

	// --------
	// -------- Switch 'get' Methods
	// --------

	/**
	 * Checks the sensed level on the indicated channel. To avoid an exception,
	 * verify that this switch has level sensing with the
	 * <code>hasLevelSensing()</code>. Level sensing means that the device can sense
	 * the logic level on its PIO pin.
	 *
	 * @param channel channel to execute this operation, in the range [0 to
	 *                (<code>getNumberChannels(byte[])</code> - 1)]
	 * @param state   current state of the device returned from
	 *                <code>readDevice()</code>
	 *
	 * @return <code>true</code> if level sensed is 'high' and <code>false</code> if
	 *         level sensed is 'low'
	 *
	 * @see com.dalsemi.onewire.container.OneWireSensor#readDevice()
	 * @see #hasLevelSensing()
	 */
	@Override
	public boolean getLevel(int channel, byte[] state) {
		return (state[0] & 0x02) == 0x02;
	}

	/**
	 * Checks the latch state of the indicated channel.
	 *
	 * @param channel channel to execute this operation, in the range [0 to
	 *                (<code>getNumberChannels(byte[])</code> - 1)]
	 * @param state   current state of the device returned from
	 *                <code>readDevice()</code>
	 *
	 * @return <code>true</code> if channel latch is 'on' or conducting and
	 *         <code>false</code> if channel latch is 'off' and not conducting. Note
	 *         that the actual output when the latch is 'on' is returned from the
	 *         <code>isHighSideSwitch()</code> method.
	 *
	 * @see com.dalsemi.onewire.container.OneWireSensor#readDevice()
	 * @see #isHighSideSwitch()
	 * @see #setLatchState(int,boolean,boolean,byte[])
	 */
	@Override
	public boolean getLatchState(int channel, byte[] state) {
		return (state[0] & 0x01) == 0x01;
	}

	/**
	 * Checks if the indicated channel has experienced activity. This occurs when
	 * the level on the PIO pins changes. To clear the activity that is reported,
	 * call <code>clearActivity()</code>. To avoid an exception, verify that this
	 * device supports activity sensing by calling the method
	 * <code>hasActivitySensing()</code>.
	 *
	 * @param channel channel to execute this operation, in the range [0 to
	 *                (<code>getNumberChannels(byte[])</code> - 1)]
	 * @param state   current state of the device returned from
	 *                <code>readDevice()</code>
	 *
	 * @return <code>true</code> if activity was detected and <code>false</code> if
	 *         no activity was detected
	 *
	 * @throws OneWireException if this device does not have activity sensing
	 *
	 * @see #hasActivitySensing()
	 * @see #clearActivity()
	 */
	@Override
	public boolean getSensedActivity(int channel, byte[] state) throws OneWireException {

		// i don't do this
		throw new OneWireException("Sense Activity not supported");
	}

	/**
	 * Clears the activity latches the next time possible. For example, on a
	 * DS2406/07, this happens the next time the status is read with
	 * <code>readDevice()</code>.
	 *
	 * @throws OneWireException if this device does not support activity sensing
	 *
	 * @see com.dalsemi.onewire.container.OneWireSensor#readDevice()
	 * @see #getSensedActivity(int,byte[])
	 */
	@Override
	public void clearActivity() throws OneWireException {

		// i don't do this
		throw new OneWireException("Sense Activity not supported");
	}

	// --------
	// -------- Switch 'set' Methods
	// --------

	/**
	 * Sets the latch state of the indicated channel. The method
	 * <code>writeDevice()</code> must be called to finalize changes to the device.
	 * Note that multiple 'set' methods can be called before one call to
	 * <code>writeDevice()</code>.
	 *
	 * @param channel    channel to execute this operation, in the range [0 to
	 *                   (<code>getNumberChannels(byte[])</code> - 1)]
	 * @param latchState <code>true</code> to set the channel latch 'on'
	 *                   (conducting) and <code>false</code> to set the channel
	 *                   latch 'off' (not conducting). Note that the actual output
	 *                   when the latch is 'on' is returned from the
	 *                   <code>isHighSideSwitch()</code> method.
	 * @param doSmart    If latchState is 'on'/<code>true</code> then doSmart
	 *                   indicates if a 'smart on' is to be done. To avoid an
	 *                   exception check the capabilities of this device using the
	 *                   <code>hasSmartOn()</code> method.
	 * @param state      current state of the device returned from
	 *                   <code>readDevice()</code>
	 *
	 * @see #hasSmartOn()
	 * @see #getLatchState(int,byte[])
	 * @see com.dalsemi.onewire.container.OneWireSensor#writeDevice(byte[])
	 */
	@Override
	public void setLatchState(int channel, boolean latchState, boolean doSmart, byte[] state) {
		if (latchState) {
			state[0] = (byte) (state[0] | 0x01);
		} else {
			state[0] = (byte) (state[0] & 0xfe);
		}
	}

	/**
	 * Retrieves the 1-Wire device sensor state. This state is returned as a byte
	 * array. Pass this byte array to the 'get' and 'set' methods. If the device
	 * state needs to be changed then call the 'writeDevice' to finalize the
	 * changes.
	 *
	 * @return 1-Wire device sensor state
	 *
	 * @throws OneWireIOException on a 1-Wire communication error such as reading an
	 *                            incorrect CRC from a 1-Wire device. 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 byte[] readDevice() throws OneWireIOException, OneWireException {

		// first let's make sure we can talk to the part
		// speed is not critical with the 2405 so i'll just call doSpeed()
		this.doSpeed();

		// this ain't a hard part--it's only gonna take 1 byte
		var state = new byte[1];

		// here's the 'bitmap'
		// bit 0 : switch state (0 for conducting, 1 for non-conducting)
		// bit 1 : sensed level (0 for low, 1 for high)
		state[0] = (byte) 0;

		if (!this.isPresent()) {
			throw new OneWireIOException("Device not present");
		}
		if (this.isAlarming()) {
			state[0] = 1;
		}

		if (!this.isPresent()) {
			throw new OneWireIOException("Device not present");
		}
		// Byte after 'search' indicates level
		if (this.adapter.getByte() != 0) {
			state[0] = (byte) (state[0] | 0x02);
		}

		return state;
	}

	/**
	 * Writes the 1-Wire device sensor state that have been changed by 'set'
	 * methods. Only the state registers that changed are updated. This is done by
	 * referencing a field information appended to the state data.
	 *
	 * @param state 1-Wire device sensor state
	 *
	 * @throws OneWireIOException on a 1-Wire communication error such as reading an
	 *                            incorrect CRC from a 1-Wire device. 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 void writeDevice(byte[] state) throws OneWireIOException, OneWireException {
		this.doSpeed();

		var value = (state[0] & 0x01) == 0x01;
		var compare = this.isAlarming();

		// check to see if already in the correct state
		if (compare == value) {
			return;
		}
		if (this.adapter.select(this.address)) {

			// verify
			compare = this.isAlarming();

			if (compare == value) {
				return;
			}
		}

		throw new OneWireIOException("Failure to change DS2405 latch state");
	}
}
// CHECKSTYLE:ON
