/*
 This file is part of GNU Taler
 (C) 2020 Taler Systems S.A.
 GNU Taler is free software; you can redistribute it and/or modify it under the
 terms of the GNU General Public License as published by the Free Software
 Foundation; either version 3, or (at your option) any later version.
 GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 You should have received a copy of the GNU General Public License along with
 GNU Taler; see the file COPYING.  If not, see 
 */
/**
 * Utilities to handle Taler-style configuration files.
 *
 * @author Florian Dold 
 */
/**
 * Imports
 */
import { AmountJson } from "./amounts";
import * as Amounts from "./amounts";
export class ConfigError extends Error {
  constructor(message: string) {
    super();
    Object.setPrototypeOf(this, ConfigError.prototype);
    this.name = "ConfigError";
    this.message = message;
  }
}
type OptionMap = { [optionName: string]: string };
type SectionMap = { [sectionName: string]: OptionMap };
export class ConfigValue {
  constructor(
    private sectionName: string,
    private optionName: string,
    private val: string | undefined,
    private converter: (x: string) => T,
  ) {}
  required(): T {
    if (!this.val) {
      throw new ConfigError(
        `required option [${this.sectionName}]/${this.optionName} not found`,
      );
    }
    return this.converter(this.val);
  }
}
export class Configuration {
  private sectionMap: SectionMap = {};
  loadFromString(s: string): void {
    const reComment = /^\s*#.*$/;
    const reSection = /^\s*\[\s*([^\]]*)\s*\]\s*$/;
    const reParam = /^\s*([^=]+?)\s*=\s*(.*?)\s*$/;
    const reEmptyLine = /^\s*$/;
    let currentSection: string | undefined = undefined;
    const lines = s.split("\n");
    for (const line of lines) {
      console.log("parsing line", JSON.stringify(line));
      if (reEmptyLine.test(line)) {
        continue;
      }
      if (reComment.test(line)) {
        continue;
      }
      const secMatch = line.match(reSection);
      if (secMatch) {
        currentSection = secMatch[1];
        console.log("setting section to", currentSection);
        continue;
      }
      if (currentSection === undefined) {
        throw Error("invalid configuration, expected section header");
      }
      const paramMatch = line.match(reParam);
      if (paramMatch) {
        const optName = paramMatch[1];
        let val = paramMatch[2];
        if (val.startsWith('"') && val.endsWith('"')) {
          val = val.slice(1, val.length - 1);
        }
        const sec = this.sectionMap[currentSection] ?? {};
        this.sectionMap[currentSection] = Object.assign(sec, {
          [optName]: val,
        });
        continue;
      }
      throw Error(
        "invalid configuration, expected section header or option assignment",
      );
    }
    console.log("parsed config", JSON.stringify(this.sectionMap, undefined, 2));
  }
  getString(section: string, option: string): ConfigValue {
    const val = (this.sectionMap[section] ?? {})[option];
    return new ConfigValue(section, option, val, (x) => x);
  }
  getAmount(section: string, option: string): ConfigValue {
    const val = (this.sectionMap[section] ?? {})[option];
    return new ConfigValue(section, option, val, (x) =>
      Amounts.parseOrThrow(x),
    );
  }
}