/*
 * Copyright 2020, 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 viewerConfig from "ProtoLogJsonSafePath/services.core.protolog.json"

import { nanos_to_string } from './transform.js'

const PROTOLOG_VERSION = "1.0.0"

class FormatStringMismatchError extends Error {
  constructor(message) {
    super(message);
  }
}

function get_param(arr, idx) {
  if (arr.length <= idx) {
    throw new FormatStringMismatchError('No param for format string conversion');
  }
  return arr[idx];
}

function format_text(messageFormat, data) {
  let out = ""
  const strParams = data.strParams;
  let strParamsIdx = 0;
  const sint64Params = data.sint64Params;
  let sint64ParamsIdx = 0;
  const doubleParams = data.doubleParams;
  let doubleParamsIdx = 0;
  const booleanParams = data.booleanParams;
  let booleanParamsIdx = 0;
  for (let i = 0; i < messageFormat.length;) {
    if (messageFormat[i] == '%') {
      if (i + 1 >= messageFormat.length) {
        // Should never happen - protologtool checks for that
        throw new Error("Invalid format string")
      }
      switch (messageFormat[i + 1]) {
        case '%':
          out += '%';
          break;
        case 'd':
          out += get_param(sint64Params, sint64ParamsIdx++).toString(10);
          break;
        case 'o':
          out += get_param(sint64Params, sint64ParamsIdx++).toString(8);
          break;
        case 'x':
          out += get_param(sint64Params, sint64ParamsIdx++).toString(16);
          break;
        case 'f':
          out += get_param(doubleParams, doubleParamsIdx++).toFixed(6);
          break;
        case 'e':
          out += get_param(doubleParams, doubleParamsIdx++).toExponential();
          break;
        case 'g':
          out += get_param(doubleParams, doubleParamsIdx++).toString();
          break;
        case 's':
          out += get_param(strParams, strParamsIdx++);
          break;
        case 'b':
          out += get_param(booleanParams, booleanParamsIdx++).toString();
          break;
        default:
          // Should never happen - protologtool checks for that
          throw new Error("Invalid format string conversion: " + messageFormat[i + 1]);
      }
      i += 2;
    } else {
      out += messageFormat[i];
      i += 1;
    }
  }
  return out;
}

function transform_unformatted(entry) {
  return {
    text: (entry.messageHash.toString() + ' - [' + entry.strParams.toString() +
      '] [' + entry.sint64Params.toString() + '] [' + entry.doubleParams.toString() +
      '] [' + entry.booleanParams.toString() + ']'),
    time: nanos_to_string(entry.elapsedRealtimeNanos),
    tag: "INVALID",
    at: "",
    timestamp: entry.elapsedRealtimeNanos,
  };
}

function transform_formatted(entry, message) {
  return {
    text: format_text(message.message, entry),
    time: nanos_to_string(entry.elapsedRealtimeNanos),
    tag: viewerConfig.groups[message.group].tag,
    at: message.at,
    timestamp: entry.elapsedRealtimeNanos,
  };
}

function transform_message(entry) {
  let message = viewerConfig.messages[entry.messageHash]
  if (message === undefined) {
    return transform_unformatted(entry);
  } else {
    try {
      return transform_formatted(entry, message);
    } catch (err) {
      if (err instanceof FormatStringMismatchError) {
        return transform_unformatted(entry);
      }
      throw err;
    }
  }
}

function transform_protolog(log) {
  if (log.version !== PROTOLOG_VERSION) {
    throw new Error('Unsupported log version');
  }
  if (viewerConfig.version !== PROTOLOG_VERSION) {
    throw new Error('Unsupported viewer config version');
  }

  let data = log.log.map(entry => (transform_message(entry)))
  data.sort(function(a, b) { return a.timestamp - b.timestamp })
  let transformed = {
    children: data
  }
  return transformed
}

export { transform_protolog };
