/*
 * Copyright (c) 2004-2017, by Alex Leigh.
 * All rights reserved.
 *
 * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE
 * The copyright notice above does not evidence any
 * actual or intended publication of such source code.
 */

package lc.mecha.io;

/**
 * The <code>AlignmentCalculator</code> assists in performing block alignment calculations for logical storage.
 *
 * @author Alex Leigh
 */
public class AlignmentCalculator {
    private final int blockSz;
    private int firstBlockIndex;
    private int lastBlockIndex;
    private int physicalOffset;
    private int physicalLength;

    public AlignmentCalculator(int blockSz) {
        this.blockSz = blockSz;
    }

    public void align(int logicalOffset, int logicalLength) {
        firstBlockIndex = logicalOffset / blockSz;
        lastBlockIndex = ((logicalOffset + logicalLength - 1) / blockSz);
        physicalOffset = firstBlockIndex * blockSz;
        physicalLength = (lastBlockIndex + 1) * blockSz - firstBlockIndex * blockSz;
    }

    /**
     * Return the configured block size in bytes.
     *
     * @return
     */
    public int getBlockSz() {
        return blockSz;
    }

    /**
     * Return the zero-index of the first block satisfying the alignment.
     *
     * @return
     */
    public int getFirstBlockIndex() {
        return firstBlockIndex;
    }

    /**
     * Return the zero-index of the last block satisfying the alignment.
     *
     * @return
     */
    public int getLastBlockIndex() {
        return lastBlockIndex;
    }

    /**
     * Return the physical offset in bytes of the first byte of the first block satisfying the alignment.
     *
     * @return
     */
    public int getPhysicalOffset() {
        return physicalOffset;
    }

    /**
     * Return the physical length in bytes from the first byte of the first block to the last byte of the last block
     * satisfying the alignment.
     *
     * @return
     */
    public int getPhysicalLength() {
        return physicalLength;
    }

    @Override
    public String toString() {
        return "AlignmentCalculator{" +
                "blockSz=" + blockSz +
                ", firstBlockIndex=" + firstBlockIndex +
                ", lastBlockIndex=" + lastBlockIndex +
                ", physicalOffset=" + physicalOffset +
                ", physicalLength=" + physicalLength +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        AlignmentCalculator that = (AlignmentCalculator) o;

        if (blockSz != that.blockSz) return false;
        if (firstBlockIndex != that.firstBlockIndex) return false;
        if (lastBlockIndex != that.lastBlockIndex) return false;
        if (physicalOffset != that.physicalOffset) return false;
        return physicalLength == that.physicalLength;
    }

    @Override
    public int hashCode() {
        int result = blockSz;
        result = 31 * result + firstBlockIndex;
        result = 31 * result + lastBlockIndex;
        result = 31 * result + physicalOffset;
        result = 31 * result + physicalLength;
        return result;
    }
}
