package railo.runtime.crypt;

public final class BinConverter {

  /**
    * gets bytes from an array into a long
    * @param buffer where to get the bytes
    * @param nStartIndex index from where to read the data
    * @return the 64bit integer
    */
  public static long byteArrayToLong(byte[] buffer,int nStartIndex) {

    return (((long)buffer[nStartIndex]) << 56) |
           ((buffer[nStartIndex + 1] & 0x0ffL) << 48) |
           ((buffer[nStartIndex + 2] & 0x0ffL) << 40) |
           ((buffer[nStartIndex + 3] & 0x0ffL) << 32) |
           ((buffer[nStartIndex + 4] & 0x0ffL) << 24) |
           ((buffer[nStartIndex + 5] & 0x0ffL) << 16) |
           ((buffer[nStartIndex + 6] & 0x0ffL) << 8) |
           ((long)buffer[nStartIndex + 7] & 0x0ff);

  }

  /**
    * converts a long o bytes which are put into a given array
    * @param lValue the 64bit integer to convert
    * @param buffer the target buffer
    * @param nStartIndex where to place the bytes in the buffer
    */
  public static void longToByteArray(long lValue,byte[] buffer,int nStartIndex) {
    buffer[nStartIndex] = (byte) (lValue >>> 56);
    buffer[nStartIndex + 1] = (byte) ((lValue >>> 48) & 0x0ff);
    buffer[nStartIndex + 2] = (byte) ((lValue >>> 40) & 0x0ff);
    buffer[nStartIndex + 3] = (byte) ((lValue >>> 32) & 0x0ff);
    buffer[nStartIndex + 4] = (byte) ((lValue >>> 24) & 0x0ff);
    buffer[nStartIndex + 5] = (byte) ((lValue >>> 16) & 0x0ff);
    buffer[nStartIndex + 6] = (byte) ((lValue >>> 8) & 0x0ff);
    buffer[nStartIndex + 7] = (byte) lValue;
  }

  /**
    * converts values from an integer array to a long
    * @param buffer where to get the bytes
    * @param nStartIndex index from where to read the data
    * @return the 64bit integer
    */
  public static long intArrayToLong(int[] buffer,int nStartIndex) {
    return (((long) buffer[nStartIndex]) << 32) | (( buffer[nStartIndex + 1]) & 0x0ffffffffL);
  }

  /**
    * converts a long to integers which are put into a given array
    * @param lValue the 64bit integer to convert
    * @param buffer the target buffer
    * @param nStartIndex where to place the bytes in the buffer
    */
  public static void longToIntArray(long lValue,int[] buffer,int nStartIndex) {
    buffer[nStartIndex]     = (int) (lValue >>> 32);
    buffer[nStartIndex + 1] = (int) lValue;
  }

  /**
    * makes a long from two integers (treated unsigned)
    * @param nLo lower 32bits
    * @param nHi higher 32bits
    * @return the built long
    */
  public static long makeLong(int nLo,int nHi) {
    return (((long)nHi << 32) | (nLo & 0x00000000ffffffffL));
  }

  /**
    * gets the lower 32 bits of a long
    * @param lVal the long integer
    * @return lower 32 bits
    */
  public static int longLo32(long lVal) {
    return (int)lVal;
  }

  /**
    * gets the higher 32 bits of a long
    * @param lVal the long integer
    * @return higher 32 bits
    */
  public static int longHi32(long lVal) {
    return (int)((lVal >>> 32));
  }

  final static char[] HEXTAB = { '0', '1', '2', '3', '4', '5', '6', '7',
                                 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

  /**
    * converts a byte array to a binhex string
    * @param data the byte array
    * @return the binhex string
    */
  public static String bytesToBinHex(byte[] data) {
    return bytesToBinHex(data, 0, data.length);
  }

  /**
    * converts a byte array to a binhex string
    * @param data the byte array
    * @param nStartPos start index where to get the bytes
    * @param nNumOfBytes number of bytes to convert
    * @return the binhex string
    */
  public static String bytesToBinHex(byte[] data,int nStartPos,int nNumOfBytes) {
    StringBuffer sbuf = new StringBuffer();
    sbuf.setLength(nNumOfBytes << 1);
    int nPos = 0;
    for (int nI = 0; nI < nNumOfBytes; nI++) {
      sbuf.setCharAt(nPos++, HEXTAB[(data[nI + nStartPos] >> 4) & 0x0f]);
      sbuf.setCharAt(nPos++, HEXTAB[data[nI + nStartPos] & 0x0f]);
    }
    return sbuf.toString();
  }

  /**
    * converts a binhex string back into a byte array (invalid codes will be skipped)
    * @param sBinHex binhex string
    * @param data the target array
    * @param nSrcPos from which character in the string the conversion should begin,
    *                remember that (nSrcPos modulo 2) should equals 0 normally
    * @param nDstPos to store the bytes from which position in the array
    * @param nNumOfBytes number of bytes to extract
    * @return number of extracted bytes
    */
  public static int binHexToBytes(String sBinHex,byte[] data,int nSrcPos,int nDstPos,int nNumOfBytes) {
    // check for correct ranges
    int nStrLen = sBinHex.length();
    int nAvailBytes = (nStrLen - nSrcPos) >> 1;
    if (nAvailBytes < nNumOfBytes)
      nNumOfBytes = nAvailBytes;
    int nOutputCapacity = data.length - nDstPos;
    if (nNumOfBytes > nOutputCapacity)
      nNumOfBytes = nOutputCapacity;
    // convert now
    int nResult = 0;
    for (int nI = 0; nI < nNumOfBytes; nI++) {
      byte bActByte = 0;
      boolean blConvertOK = true;
      for (int nJ = 0; nJ < 2; nJ++) {
        bActByte <<= 4;
        char cActChar = sBinHex.charAt(nSrcPos++);
        if ((cActChar >= 'a') && (cActChar <= 'f'))
          bActByte |= (byte)(cActChar - 'a') + 10;
        else if ((cActChar >= '0') && (cActChar <= '9'))
            bActByte |= (byte)(cActChar - '0');
          else
            blConvertOK = false;
      }
      if (blConvertOK) {
        data[nDstPos++] = bActByte;
        nResult++;
      }
    }
    return nResult;
  }

  /**
    * converts a byte array into an UNICODE string
    * @param data the byte array
    * @param nStartPos where to begin the conversion
    * @param nNumOfBytes number of bytes to handle
    * @return the string
    */
  public static String byteArrayToUNCString(byte[] data,int nStartPos,int nNumOfBytes) {
    // we need two bytes for every character
    nNumOfBytes &= ~1;
    // enough bytes in the buffer?
    int nAvailCapacity = data.length - nStartPos;
    if (nAvailCapacity < nNumOfBytes)
      nNumOfBytes = nAvailCapacity;
    StringBuffer sbuf = new StringBuffer();
    sbuf.setLength(nNumOfBytes >> 1);
    int nSBufPos = 0;
    while (nNumOfBytes > 0) {
      sbuf.setCharAt(nSBufPos++,
                     (char)((data[nStartPos] << 8) | (data[nStartPos + 1] & 0x0ff)));
      nStartPos += 2;
      nNumOfBytes -= 2;
    }
    return sbuf.toString();
  }
}