package com.invertor.modbus.data;

import com.invertor.modbus.Modbus;
import com.invertor.modbus.exception.IllegalDataAddressException;
import com.invertor.modbus.exception.IllegalDataValueException;
import com.invertor.modbus.utils.DataUtils;

import java.util.Arrays;

/*
 * Copyright (C) 2016 "Invertor" Factory", JSC
 * [http://www.sbp-invertor.ru]
 *
 * This file is part of JLibModbus.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Authors: Vladislav Y. Kochedykov, software engineer.
 * email: vladislav.kochedykov@gmail.com
 */

/**
 * since 1.2.8.4 it extends Observable to notify observers if register values was changed.
 *
 * @see java.util.Observable
 * @see java.util.Observer
 */
public class ModbusHoldingRegisters extends ModbusValues<Integer> {

    private int[] registers = new int[0];

    public ModbusHoldingRegisters(int size) {
        registers = new int[(Modbus.checkEndAddress(size) ? size : Modbus.MAX_START_ADDRESS)];
    }

    public ModbusHoldingRegisters() {

    }

    void setSize(int size) {
        synchronized (this) {
            if (registers.length != size) {
                registers = Arrays.copyOf(registers, size);
            }
        }
    }

    protected void checkOffsetAndValue(int offset, int value) throws IllegalDataValueException, IllegalDataAddressException {
        checkAddress(offset);
        checkValue(value);
    }

    @Override
    public void setImpl(int offset, Integer value) throws IllegalDataAddressException, IllegalDataValueException {
        checkAddress(offset);
        checkValue(value);
        synchronized (this) {
            registers[offset] = value;
        }
    }

    public void setRange(int offset, int[] range) throws IllegalDataAddressException, IllegalDataValueException {
        checkRange(offset, range.length);
        if (!Modbus.checkWriteRegisterCount(range.length))
            throw new IllegalDataAddressException(offset);
        synchronized (this) {
            for (int i = 0; i < range.length; i++) {
                set(i, range[i]);
            }
        }
    }

    public void setInt8At(int offset, int i8) throws IllegalDataAddressException, IllegalDataValueException {
        checkOffsetAndValue(offset, i8);
        set(offset, (int)DataUtils.byteLow(i8));
    }

    public void setInt16At(int offset, int i16) throws IllegalDataAddressException, IllegalDataValueException {
        checkOffsetAndValue(offset, i16);
        set(offset, i16);
    }

    public void setInt32At(int offset, int i32) throws IllegalDataAddressException, IllegalDataValueException {
        setInt16At(offset, DataUtils.wordLow(i32));
        setInt16At(offset + 1, DataUtils.wordHigh(i32));
    }

    public void setInt64At(int offset, long i64) throws IllegalDataAddressException, IllegalDataValueException {
        setInt32At(offset, (int) (i64 & 0xffffffff));
        setInt32At(offset + 2, (int) ((i64 >> 32) & 0xffffffff));
    }

    public void setFloat32At(int offset, float f32) throws IllegalDataAddressException, IllegalDataValueException {
        setInt32At(offset, Float.floatToRawIntBits(f32));
    }

    public void setFloat64At(int offset, double f64) throws IllegalDataAddressException, IllegalDataValueException {
        setInt64At(offset, Double.doubleToRawLongBits(f64));
    }

    public int getInt8At(int offset) throws IllegalDataAddressException {
        checkAddress(offset);
        return DataUtils.byteLow(get(offset));
    }

    public int getInt16At(int offset) throws IllegalDataAddressException {
        return get(offset);
    }

    public int getInt32At(int offset) throws IllegalDataAddressException {
        return getInt16At(offset) & (getInt16At(offset + 1) << 16);
    }

    public long getInt64At(int offset) throws IllegalDataAddressException {
        return (long)getInt32At(offset) & ((long)getInt32At(offset + 2) << 32);
    }

    public float getFloat32At(int offset) throws IllegalDataAddressException {
        return Float.intBitsToFloat(getInt32At(offset));
    }

    public double getFloat64At(int offset) throws IllegalDataAddressException {
        return Double.longBitsToDouble(getInt64At(offset));
    }

    public int[] getRegisters() {
        return Arrays.copyOf(registers, getQuantity());
    }

    public void setRegisters(int[] registers) {
        this.registers = Arrays.copyOf(registers, registers.length);
    }

    @Override
    synchronized public int getByteCount() {
        return getQuantity() * 2;
    }

    @Override
    public byte[] getBytes() {
        return DataUtils.toByteArray(registers);
    }

    @Override
    public void setBytesBe(byte[] bytes) {
        registers = DataUtils.BeToIntArray(bytes);
    }

    public void setBytesLe(byte[] bytes) {
        this.registers = DataUtils.LeToIntArray(bytes);
    }

    @Override
    synchronized public int getQuantity() {
        return registers.length;
    }

    @Override
    public Integer get(int offset) throws IllegalDataAddressException {
        checkAddress(offset);
        synchronized (this) {
            return registers[offset];
        }
    }

    public int[] getRange(int offset, int quantity) throws IllegalDataAddressException {
        checkRange(offset, quantity);
        if (!Modbus.checkReadRegisterCount(quantity))
            throw new IllegalDataAddressException(offset);
        synchronized (this) {
            return Arrays.copyOfRange(registers, offset, offset + quantity);
        }
    }

    private void checkRange(int offset, int quantity) throws IllegalDataAddressException {
        if (offset + quantity > getQuantity())
            throw new IllegalDataAddressException(offset);
    }

    private void checkAddress(int offset) throws IllegalDataAddressException {
        if (offset > (getQuantity() - 1))
            throw new IllegalDataAddressException(offset);
    }

    private void checkValue(int value) throws IllegalDataValueException {
        if (!Modbus.checkRegisterValue(value))
            throw new IllegalDataValueException();
    }
}

@@ -4192,17 +4192,17 @@
 offset) 
-&
+%7C
  (getInt
@@ -4326,24 +4326,25 @@
 eturn (long)
+ 
 getInt32At(o
@@ -4354,17 +4354,17 @@
 et) 
-&
+%7C
  ((long)
 getI
@@ -4359,16 +4359,17 @@
  ((long)
+ 
 getInt32

Fix errors in data conversion operations.