/*
 * Copyright 2012-2018 Chronicle Map Contributors
 *
 * 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.
 */

package net.openhft.lang.values;

import net.openhft.chronicle.bytes.Byteable;
import net.openhft.chronicle.bytes.BytesStore;
import net.openhft.chronicle.bytes.DynamicallySized;
import net.openhft.chronicle.values.Copyable;

/**
 * Created by peter.lawrey on 23/04/2015.
 */
public class DoubleArray implements Byteable, Copyable<DoubleArray>, DynamicallySized {
    static boolean HACK = true;
    private static final int CAPACITY = 0; // assume a 32-bit size.
    private static final int LENGTH = CAPACITY + 4; // assume a 32-bit size.
    private static final int BASE = LENGTH + 4;

    private final int capacity;
    private BytesStore bs;
    private long offset;

    public DoubleArray(int capacity) {
        bs = BytesStore.nativeStoreWithFixedCapacity(BASE + capacity * 8L);
        bs.writeInt(CAPACITY, capacity);
        offset = 0;
        this.capacity = capacity;
    }

    @Override
    public void bytesStore(BytesStore bytes, long offset, long maxSize) {
        this.bs = bytes;
        this.offset = offset;
    }

    @Override
    public BytesStore bytesStore() {
        return bs;
    }

    @Override
    public long offset() {
        return offset;
    }

    @Override
    public long maxSize() {
        return BASE + capacity * 8;
    }

    public int length() {
        return HACK && bs == null ? 6 * 8 : bs.readInt(LENGTH + offset);
    }

    public int capacity() {
        return bs.readInt(CAPACITY + offset);
    }

    public double getDataAt(int index) {
        if (index < 0 || index >= length()) throw new ArrayIndexOutOfBoundsException();
        return bs.readDouble(BASE + offset + index * 8L);
    }

    public void setDataAt(int index, double d) {
        if (index < 0 || index >= capacity()) throw new ArrayIndexOutOfBoundsException();
        if (length() <= index)
            setLength(index + 1);
        bs.writeDouble(BASE + offset + index * 8L, d);
    }

    public void setLength(int length) {
        if (length < 0 || length >= capacity()) throw new IllegalArgumentException();
        bs.writeInt(LENGTH + offset, length);
    }

    public void addData(double d) {
        int index = length();
        if (index >= capacity()) throw new IllegalStateException();
        bs.writeInt(LENGTH + offset, index + 1);
        bs.writeDouble(BASE + offset + index * 8L, d);
    }

    public void setData(double[] doubles) {
        if (doubles.length > capacity()) throw new IllegalArgumentException();
        bs.writeInt(LENGTH + offset, doubles.length);
        for (int index = 0; index < doubles.length; index++)
            bs.writeDouble(BASE + offset + index * 8L, doubles[index]);
    }

    public int getDataUsing(double[] doubles) {
        int length = Math.min(length(), doubles.length);
        for (int index = 0; index < length; index++)
            doubles[index] = bs.readDouble(BASE + offset + index * 8L);
        return length;
    }

    @Override
    public void copyFrom(DoubleArray doubleArray) {
        int length = length();
        // set first so we check the length will fit.
        doubleArray.setLength(length);
        for (int i = 0; i < length; i++)
            doubleArray.setDataAt(i, getDataAt(i));
    }

    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("[ ");
        String sep = "";
        for (int i = 0, len = length(); i < len; i++) {
            sb.append(sep).append(getDataAt(i));
            sep = ", ";
        }
        return sb.append(" ]").toString();
    }
}
