/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2007-2015 Broad Institute
 *
 * 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 THE
 * AUTHORS OR COPYRIGHT HOLDERS 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.
 */

package org.broad.igv.bbfile;

import htsjdk.samtools.seekablestream.SeekableStream;
import org.apache.log4j.Logger;
import org.broad.igv.util.LittleEndianInputStream;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;

/**
 * Created by IntelliJ IDEA.
 * User: martind
 * Date: Jan 14, 2010
 * Time: 3:59:55 PM
 * To change this template use File | Settings | File Templates.
 */
public class RPTreeHeader {

    private static Logger log = Logger.getLogger(RPTreeHeader.class);

    public final int RPTREE_HEADER_SIZE = 48;

    public final int RPTREE_MAGIC_LTH = 0x2468ACE0;
    public final int RPTREE_MAGIC_HTL = 0xE0AC6824;

    // defines the R+ Tree access

    private long rpTreeOffset;         // BBFile file offset for mChromosome region tree
    private boolean headerOK;          // R+ Tree header read OK

    // R+ Tree header - Table K
    private int magic;             // magic number identifies it as B+ header
    private int blockSize;         // number of children per block
    private long itemCount;        // number of chromosomes/contigs in B+ tree
    private int startChromID;      // ID of the first mChromosome in item
    private int startBase;         // Position of first base in item
    private int endChromID;        // ID of the first mChromosome in item
    private int endBase;           // Position of first base in item
    private long endFileOffset;    // file position marking mEndBase of data
    private int itemsPerSlot;      // number of items per leaf
    private long reserved;         // Currently 0

    // constructor   - reads from file input stream
    /*
    *   Constructor
    *
    *   Parameters:
    *       fis - file input stream handle
    *       fileOffset - file offset to the RP tree header
    *       isLowToHigh - if true, indicates low to high byte order, else high to low
    * */
    public RPTreeHeader(SeekableStream fis, long fileOffset, boolean isLowToHigh) {

        long itemsCount;

       rpTreeOffset =  fileOffset;

       // Note: a bad R+ Tree header will result in false returned
       headerOK =  readHeader(fis, rpTreeOffset, isLowToHigh);

    }

    public boolean isHeaderOK() {
        return headerOK;
    }

    public int getHeaderSize() {
        return RPTREE_HEADER_SIZE;
    }

    public long getTreeOffset() {
        return rpTreeOffset;
    }

    public int getMagic() {
        return magic;
    }

    public int getBlockSize() {
        return blockSize;
    }

    public long getItemCount() {
        return itemCount;
    }

    public int getStartChromID() {
        return startChromID;
    }

    public int getStartBase() {
        return startBase;
    }

    public int getEndChromID() {
        return endChromID;
    }

    public int getEndBase() {
        return endBase;
    }

    public long getMEndFileOffset() {
        return endFileOffset;
    }

    public int getItemsPerSlot() {
        return itemsPerSlot;
    }

    public long getReserved() {
        return reserved;
    }

// prints out the B+ Tree Header
public void print() {

   // note if read successfully
   if(headerOK){
       log.debug("R+ tree header has " + RPTREE_HEADER_SIZE + " bytes.");
       log.debug("R+ tree header magic = " + magic);
   }
   else {
       log.debug("R+ Tree header is unrecognized type, header magic = " + magic);
       return;
   }

   // Table E - Chromosome B+ Tree  Header
   log.debug("R+ Tree file offset = " + rpTreeOffset);
   log.debug("magic = " + magic);
   log.debug("Block size = " + blockSize);
   log.debug("ItemCount = " + itemCount);
   log.debug("StartChromID = " + startChromID);
   log.debug("StartBase = " + startBase);
   log.debug("EndChromID = " + endChromID);
   log.debug("EndBase = " + endBase);
   log.debug("EndFileOffset = " + endFileOffset);
   log.debug("ItemsPerSlot = " + itemsPerSlot);
   log.debug("Reserved = " + reserved);
   }

  /*
  * Reads in the R+ Tree Header.
  *
  * Returns status of for tree header read; true if read, false if not.
  * */
   private boolean readHeader(SeekableStream fis, long fileOffset, boolean isLowToHigh){

   LittleEndianInputStream lbdis;
   DataInputStream bdis;

    byte[] buffer = new byte[RPTREE_HEADER_SIZE];

   try {
       // Read R+ tree header into a buffer
       fis.seek(fileOffset);
       fis.readFully(buffer);

       // decode header
       if(isLowToHigh){
           lbdis = new LittleEndianInputStream(new ByteArrayInputStream(buffer));
           magic = lbdis.readInt();

           // check for a valid B+ Tree Header
           if(magic != RPTREE_MAGIC_LTH)
               return false;

           // Get mChromosome B+ header information
           blockSize = lbdis.readInt();
           itemCount = lbdis.readLong();
           startChromID = lbdis.readInt();
           startBase = lbdis.readInt();
           endChromID = lbdis.readInt();
           endBase = lbdis.readInt();
           endFileOffset = lbdis.readLong();
           itemsPerSlot = lbdis.readInt();
           reserved = lbdis.readInt();
       }
       else {
           bdis = new DataInputStream(new ByteArrayInputStream(buffer));

           // check for a valid B+ Tree Header
           magic = bdis.readInt();

           if(magic != RPTREE_MAGIC_HTL)
               return false;

           // Get mChromosome B+ header information
           blockSize = bdis.readInt();
           itemCount = bdis.readLong();
           startChromID = bdis.readInt();
           startBase = bdis.readInt();
           endChromID = bdis.readInt();
           endBase = bdis.readInt();
           endFileOffset = bdis.readLong();
           itemsPerSlot = bdis.readInt();
           reserved = bdis.readInt();
       }

   }catch(IOException ex) {
           log.error("Error reading R+ tree header " + ex);
           throw new RuntimeException("Error reading R+ tree header ", ex);
       }

   // success
    return true;
   }


}
