/*
 * Copyright 2021 Juan Cruz Viotti
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {
  strict as assert
} from 'assert'

import fromExponential from 'from-exponential'
import ResizableBuffer from '../resizable-buffer'

import {
  zigzagEncode
} from '../integer/zigzag'

import {
  varintEncode
} from '../integer/varint'

import {
  JSONNumber
} from '../../json'

import {
  NoOptions
} from './options'

import {
  EncodingContext
} from '../context'

const stringPrefixCount = (value: string, prefix: string): number => {
  let count: number = 0

  for (const character of value) {
    if (character === prefix) {
      count += 1
    } else {
      break
    }
  }

  return count
}

// TODO: More efficient way to get the integral
// const toIntegral = (value, multiplier = 1) => {
//   const result = value * multiplier
//   if (Number.isInteger(result)) return result
//   return toIntegral(value, multiplier * 10)
// }

export const DOUBLE_VARINT_TUPLE = (
  buffer: ResizableBuffer, offset: number, value: JSONNumber,
  _options: NoOptions, _context: EncodingContext
): number => {
  const valueString: string = fromExponential(value)
  const pointIndex: number = valueString.startsWith('-')
    ? valueString.indexOf('.') - 1
    : valueString.indexOf('.')
  const point: number = pointIndex > 0 ? pointIndex : 0
  assert(point >= 0)
  const integralString: string = valueString.replace('.', '')
  const zeroPrefix: number = stringPrefixCount(integralString.startsWith('-')
    ? integralString.slice(1) : integralString, '0')
  assert(zeroPrefix >= 0)

  const integralBytes: number =
    varintEncode(buffer, offset, zigzagEncode(BigInt(integralString)))
  const pointValue: bigint = zeroPrefix === 0 || zeroPrefix === integralString.length
    ? zigzagEncode(BigInt(point))
    : zigzagEncode(BigInt(-zeroPrefix - (valueString.startsWith('-') ? 1 : 0)))
  return integralBytes + varintEncode(buffer, offset + integralBytes, pointValue)
}
