package org.bson;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import org.jruby.Ruby;
import org.jruby.RubyModule;
import org.jruby.RubyInteger;
import org.jruby.RubyString;
import org.jruby.anno.JRubyMethod;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.load.BasicLibraryService;

/**
 * The native implementation of various extensions.
 *
 * @since 2.0.0
 */
public class NativeService implements BasicLibraryService {

  /**
   * Constant for the BSON module name.
   *
   * @since 2.0.0
   */
  private final String BSON = "BSON".intern();

  /**
   * Loads the native extension into the JRuby runtime.
   *
   * @param runtime The Ruby runtime.
   *
   * @return Always returns true if no exception.
   *
   * @since 2.0.0
   */
  public boolean basicLoad(final Ruby runtime) throws IOException {
    RubyModule bson = runtime.fastGetModule(BSON);
    IntegerExtension.extend(bson);
    return true;
  }

  /**
   * Provides native extensions around integer operations.
   *
   * @since 2.0.0
   */
  public static class IntegerExtension {

    /**
     * Constant for the Integer module name.
     *
     * @since 2.0.0
     */
    private static final String INTEGER = "Integer".intern();

    /**
     * Load the method definitions into the integer module.
     *
     * @param bson The bson module to define the methods under.
     *
     * @since 2.0.0.
     */
    public static void extend(final RubyModule bson) {
      RubyModule integer = bson.defineOrGetModuleUnder(INTEGER);
      integer.defineAnnotatedMethods(IntegerExtension.class);
    }

    /**
     * Encodes the integer to the raw BSON bytes.
     *
     * @param integer The instance of the integer object.
     *
     * @return The encoded bytes.
     *
     * @since 2.0.0.
     */
    @JRubyMethod(name = "to_bson")
    public static IRubyObject toBson(final IRubyObject integer) {
      final long value = ((RubyInteger) integer).getLongValue();
      return RubyString.newString(integer.getRuntime(), toBsonInt32(value));
    }

    /**
     * Encodes the integer to the raw BSON bytes.
     *
     * @param integer The instance of the integer object.
     * @param bytes The bytes to encode to.
     *
     * @return The encoded bytes.
     *
     * @since 2.0.0.
     */
    @JRubyMethod(name = "to_bson")
    public static IRubyObject toBson(final IRubyObject integer, final IRubyObject bytes) {
      final long value = ((RubyInteger) integer).getLongValue();
      final RubyString encoded =
        RubyString.newString(integer.getRuntime(), toBsonInt32(value));
      return ((RubyString) bytes).append(encoded);
    }

    /**
     * Take the 32bit value and convert it to it's little endian bytes.
     *
     * @param value The value to encode.
     *
     * @return The byte array.
     *
     * @since 2.0.0.
     */
    private static byte[] toBsonInt32(final long value) {
      final ByteBuffer buffer = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
      buffer.putInt((int) value);
      return buffer.array();
    }

    /**
     * Take the 64bit value and convert it to it's little endian bytes.
     *
     * @param value The value to encode.
     *
     * @return The byte array.
     *
     * @since 2.0.0.
     */
    private static byte[] toBsonInt64(final long value) {
      final ByteBuffer buffer = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN);
      buffer.putLong(value);
      return buffer.array();
    }
  }
}

@@ -1998,36 +1998,27 @@
  return 
-RubyString.newString
+toBsonInt32
 (integer
@@ -2032,35 +2032,22 @@
 time(), 
-toBsonInt32(
 value)
-)
 ;%0A    %7D%0A
@@ -2488,22 +2488,25 @@
 ;%0A      
-final 
+return ((
 RubyStri
@@ -2511,139 +2511,64 @@
 ring
- encoded =%0A        RubyString.newString(integer.getRuntime(), toBsonInt32(value));%0A      return ((RubyString) bytes).append(encoded
+) bytes).append(toBsonInt32(integer.getRuntime(), value)
 );%0A 
@@ -2652,32 +2652,73 @@
 n bytes.%0A     *%0A
+     * @param runtime The JRuby runtime.%0A
      * @param va
@@ -2827,38 +2827,42 @@
  private static 
-byte%5B%5D
+RubyString
  toBsonInt32(fin
@@ -2860,24 +2860,44 @@
 Int32(final 
+Ruby runtime, final 
 long value) 
@@ -3023,32 +3023,62 @@
 e);%0A      return
+ RubyString.newString(runtime,
  buffer.array();
@@ -3068,32 +3068,33 @@
 , buffer.array()
+)
 ;%0A    %7D%0A%0A    /**
@@ -3165,32 +3165,73 @@
 n bytes.%0A     *%0A
+     * @param runtime The JRuby runtime.%0A
      * @param va
@@ -3352,14 +3352,18 @@
 tic 
-byte%5B%5D
+RubyString
  toB
@@ -3373,24 +3373,44 @@
 Int64(final 
+Ruby runtime, final 
 long value) 
@@ -3539,16 +3539,46 @@
   return
+ RubyString.newString(runtime,
  buffer.
@@ -3584,16 +3584,17 @@
 .array()
+)
 ;%0A    %7D%0A

Return RubyString from toBsonIntXX methods