// Copyright (C) 2018 The Android Open Source Project
//
// 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 {assertTrue} from '../base/logging';

const EPSILON = 0.0000000001;

// TODO(hjd): Combine with timeToCode.
export function timeToString(sec: number) {
  const units = ['s', 'ms', 'us', 'ns'];
  const sign = Math.sign(sec);
  let n = Math.abs(sec);
  let u = 0;
  while (n < 1 && n !== 0 && u < units.length - 1) {
    n *= 1000;
    u++;
  }
  return `${sign < 0 ? '-' : ''}${Math.round(n * 10) / 10} ${units[u]}`;
}

export function fromNs(ns: number) {
  return ns / 1e9;
}

export function toNsFloor(seconds: number) {
  return Math.floor(seconds * 1e9);
}

export function toNsCeil(seconds: number) {
  return Math.ceil(seconds * 1e9);
}

export function toNs(seconds: number) {
  return Math.round(seconds * 1e9);
}

// 1000000023ns -> "1.000 000 023"
export function formatTimestamp(sec: number) {
  const parts = sec.toFixed(9).split('.');
  parts[1] = parts[1].replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
  return parts.join('.');
}

// TODO(hjd): Rename to formatTimestampWithUnits
// 1000000023ns -> "1s 23ns"
export function timeToCode(sec: number): string {
  let result = '';
  let ns = Math.round(sec * 1e9);
  if (ns < 1) return '0s';
  const unitAndValue = [
    ['m', 60000000000],
    ['s', 1000000000],
    ['ms', 1000000],
    ['us', 1000],
    ['ns', 1],
  ];
  unitAndValue.forEach((pair) => {
    const unit = pair[0] as string;
    const val = pair[1] as number;
    if (ns >= val) {
      const i = Math.floor(ns / val);
      ns -= i * val;
      result += i.toLocaleString() + unit + ' ';
    }
  });
  return result.slice(0, -1);
}

export function currentDateHourAndMinute(): string {
  const date = new Date();
  return `${date.toISOString().substr(0, 10)}-${date.getHours()}-${
      date.getMinutes()}`;
}

export class TimeSpan {
  readonly start: number;
  readonly end: number;

  constructor(start: number, end: number) {
    assertTrue(start <= end);
    this.start = start;
    this.end = end;
  }

  clone() {
    return new TimeSpan(this.start, this.end);
  }

  equals(other: TimeSpan): boolean {
    return Math.abs(this.start - other.start) < EPSILON &&
        Math.abs(this.end - other.end) < EPSILON;
  }

  get duration() {
    return this.end - this.start;
  }

  isInBounds(sec: number) {
    return this.start <= sec && sec <= this.end;
  }

  add(sec: number): TimeSpan {
    return new TimeSpan(this.start + sec, this.end + sec);
  }

  contains(other: TimeSpan) {
    return this.start <= other.start && other.end <= this.end;
  }
}
