package com.sleepycat.je.utilint;
import java.util.Arrays;
import com.sleepycat.je.log.FileManager;
import com.sleepycat.je.log.LogReadable;
import com.sleepycat.je.tree.TreeUtils;
import de.ovgu.cide.jakutil.*;
/** 
 * DbLsn is a class that operates on Log Sequence Numbers (LSNs). An LSN is a
 * long comprised of a file number (32b) and offset within that file (32b)
 * which references a unique record in the database environment log.  While
 * LSNs are represented as long's, we operate on them using an abstraction and
 * return longs from these methods so that we don't have to worry about the
 * lack of unsigned quantities.
 */
public class DbLsn {
  static final long INT_MASK=0xFFFFFFFFL;
  public static final long MAX_FILE_OFFSET=0xFFFFFFFFL;
  public static final long NULL_LSN=-1;
  private DbLsn(){
  }
  public static long makeLsn(  long fileNumber,  long fileOffset){
    return fileOffset & INT_MASK | ((fileNumber & INT_MASK) << 32);
  }
  public static long longToLsn(  Long lsn){
    if (lsn == null) {
      return NULL_LSN;
    }
    return lsn.longValue();
  }
  /** 
 * Return the file number for this DbLsn.
 * @return the number for this DbLsn.
 */
  public static long getFileNumber(  long lsn){
    return (lsn >> 32) & INT_MASK;
  }
  /** 
 * Return the file offset for this DbLsn.
 * @return the offset for this DbLsn.
 */
  public static long getFileOffset(  long lsn){
    return (lsn & INT_MASK);
  }
  private static int compareLong(  long l1,  long l2){
    if (l1 < l2) {
      return -1;
    }
 else     if (l1 > l2) {
      return 1;
    }
 else {
      return 0;
    }
  }
  public static int compareTo(  long lsn1,  long lsn2){
    if (lsn1 == NULL_LSN || lsn2 == NULL_LSN) {
      throw new NullPointerException();
    }
    long fileNumber1=getFileNumber(lsn1);
    long fileNumber2=getFileNumber(lsn2);
    if (fileNumber1 == fileNumber2) {
      return compareLong(getFileOffset(lsn1),getFileOffset(lsn2));
    }
 else {
      return compareLong(fileNumber1,fileNumber2);
    }
  }
  public static String toString(  long lsn){
    return "<DbLsn val=\"0x" + Long.toHexString(getFileNumber(lsn)) + "/0x"+ Long.toHexString(getFileOffset(lsn))+ "\"/>";
  }
  public static String getNoFormatString(  long lsn){
    return "0x" + Long.toHexString(getFileNumber(lsn)) + "/0x"+ Long.toHexString(getFileOffset(lsn));
  }
  public static String dumpString(  long lsn,  int nSpaces){
    StringBuffer sb=new StringBuffer();
    sb.append(TreeUtils.indent(nSpaces));
    sb.append(toString(lsn));
    return sb.toString();
  }
  /** 
 * Return the logsize in bytes between these two LSNs. This is an
 * approximation; the logs might actually be a little more or less in
 * size. This assumes that no log files have been cleaned.
 */
  public static long getNoCleaningDistance(  long thisLsn,  long otherLsn,  long logFileSize){
    long diff=0;
    assert thisLsn != NULL_LSN;
    long myFile=getFileNumber(thisLsn);
    if (otherLsn == NULL_LSN) {
      otherLsn=0;
    }
    long otherFile=getFileNumber(otherLsn);
    if (myFile == otherFile) {
      diff=Math.abs(getFileOffset(thisLsn) - getFileOffset(otherLsn));
    }
 else     if (myFile > otherFile) {
      diff=calcDiff(myFile - otherFile,logFileSize,thisLsn,otherLsn);
    }
 else {
      diff=calcDiff(otherFile - myFile,logFileSize,otherLsn,thisLsn);
    }
    return diff;
  }
  /** 
 * Return the logsize in bytes between these two LSNs. This is an
 * approximation; the logs might actually be a little more or less in
 * size. This assumes that log files might have been cleaned.
 */
  public static long getWithCleaningDistance(  long thisLsn,  FileManager fileManager,  long otherLsn,  long logFileSize){
    long diff=0;
    assert thisLsn != NULL_LSN;
    long myFile=getFileNumber(thisLsn);
    if (otherLsn == NULL_LSN) {
      otherLsn=0;
    }
    long otherFile=getFileNumber(otherLsn);
    if (myFile == otherFile) {
      diff=Math.abs(getFileOffset(thisLsn) - getFileOffset(otherLsn));
    }
 else {
      Long[] fileNums=fileManager.getAllFileNumbers();
      int myFileIdx=Arrays.binarySearch(fileNums,new Long(myFile));
      int otherFileIdx=Arrays.binarySearch(fileNums,new Long(otherFile));
      if (myFileIdx > otherFileIdx) {
        diff=calcDiff(myFileIdx - otherFileIdx,logFileSize,thisLsn,otherLsn);
      }
 else {
        diff=calcDiff(otherFileIdx - myFileIdx,logFileSize,otherLsn,thisLsn);
      }
    }
    return diff;
  }
  private static long calcDiff(  long fileDistance,  long logFileSize,  long laterLsn,  long earlierLsn){
    long diff=fileDistance * logFileSize;
    diff+=getFileOffset(laterLsn);
    diff-=getFileOffset(earlierLsn);
    return diff;
  }
  /** 
 * @see LogReadable#logEntryIsTransactional.
 */
  public boolean logEntryIsTransactionalX(){
    return false;
  }
  /** 
 * @see LogReadable#getTransactionId
 */
  public long getTransactionIdX(){
    return 0;
  }
}
