// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include 'src/builtins/builtins-string-gen.h'

namespace string {

extern transitioning builtin
StringSubstring(implicit context: Context)(String, intptr, intptr): String;

const kStringPadStart: constexpr int31 = 0;
const kStringPadEnd: constexpr int31 = 1;

transitioning macro StringPad(implicit context: Context)(
    receiver: JSAny, arguments: Arguments, methodName: constexpr string,
    variant: constexpr int31): String {
  const receiverString: String = ToThisString(receiver, methodName);
  const stringLength: Smi = receiverString.length_smi;

  if (arguments.length == 0) {
    return receiverString;
  }
  const maxLength: Number = ToLength_Inline(arguments[0]);
  assert(IsNumberNormalized(maxLength));

  typeswitch (maxLength) {
    case (smiMaxLength: Smi): {
      if (smiMaxLength <= stringLength) {
        return receiverString;
      }
    }
    case (Number): {
    }
  }

  let fillString: String = ' ';
  let fillLength: intptr = 1;

  if (arguments.length != 1) {
    const fill = arguments[1];
    if (fill != Undefined) {
      fillString = ToString_Inline(fill);
      fillLength = fillString.length_intptr;
      if (fillLength == 0) {
        return receiverString;
      }
    }
  }

  // Pad.
  assert(fillLength > 0);
  // Throw if max_length is greater than String::kMaxLength.
  if (!TaggedIsSmi(maxLength)) {
    ThrowInvalidStringLength(context);
  }

  const smiMaxLength: Smi = UnsafeCast<Smi>(maxLength);
  if (smiMaxLength > SmiConstant(kStringMaxLength)) {
    ThrowInvalidStringLength(context);
  }
  assert(smiMaxLength > stringLength);
  const padLength: Smi = smiMaxLength - stringLength;

  let padding: String;
  if (fillLength == 1) {
    // Single char fill.
    // Fast path for a single character fill.  No need to calculate number of
    // repetitions or remainder.
    padding = StringRepeat(context, fillString, padLength);
  } else {
    // Multi char fill.
    const fillLengthWord32: int32 = TruncateIntPtrToInt32(fillLength);
    const padLengthWord32: int32 = Convert<int32>(padLength);
    const repetitionsWord32: int32 = padLengthWord32 / fillLengthWord32;
    const remainingWord32: int32 = padLengthWord32 % fillLengthWord32;
    padding =
        StringRepeat(context, fillString, Convert<Smi>(repetitionsWord32));

    if (remainingWord32 != 0) {
      const remainderString =
          StringSubstring(fillString, 0, Convert<intptr>(remainingWord32));
      padding = padding + remainderString;
    }
  }

  // Return result.
  assert(padLength == padding.length_smi);
  if (variant == kStringPadStart) {
    return padding + receiverString;
  }
  assert(variant == kStringPadEnd);
  return receiverString + padding;
}

// ES6 #sec-string.prototype.padstart
transitioning javascript builtin
StringPrototypePadStart(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): String {
  const methodName: constexpr string = 'String.prototype.padStart';
  return StringPad(receiver, arguments, methodName, kStringPadStart);
}

// ES6 #sec-string.prototype.padend
transitioning javascript builtin
StringPrototypePadEnd(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): String {
  const methodName: constexpr string = 'String.prototype.padEnd';
  return StringPad(receiver, arguments, methodName, kStringPadEnd);
}
}