/*
 * Copyright (c) 2018 Mol* contributors, licensed under MIT, See LICENSE file for more info.
 *
 * @author David Sehnal <david.sehnal@gmail.com>
 */

import Expression from './expression'

const { isLiteral, isSymbol, isArgumentsArray } = Expression;

class Writer {
    private value: string[] = [];
    private currentLineLength = 0;
    private prefixLength = 0;
    private _prefix: string = '';
    private localPrefix: string = '';

    private setLocal() {
        this.localPrefix = '  ';
    }

    newline() {
        this.value.push(`\n${this._prefix}${this.localPrefix}`);
        this.currentLineLength = 0;
    }

    push() {
        this.value.push('(');
        this.currentLineLength = 0;
        this.localPrefix = '';
        this.prefixLength += 2;
        this._prefix = new Array(this.prefixLength + 1).join(' ');
    }

    pop() {
        this.value.push(')');
        this.prefixLength -= 2;
        this._prefix = new Array(this.prefixLength + 1).join(' ');
    }

    append(str: string) {
        if (!this.currentLineLength) {
            this.value.push(str);
            this.currentLineLength = str.length;
        } else if (this.currentLineLength + this.prefixLength + this.localPrefix.length + str.length < 80) {
            this.value.push(str);
            this.currentLineLength += str.length;
        } else {
            this.setLocal();
            this.newline();
            this.value.push(str);
            this.currentLineLength = str.length;
        }
    }

    whitespace() {
        if (this.currentLineLength + this.prefixLength + this.localPrefix.length + 1 < 80) {
            this.value.push(' ');
        }
    }

    getStr() {
        return this.value.join('');
    }
}

function _format(e: Expression, writer: Writer) {
    if (isLiteral(e)) {
        if (typeof e === 'string' && (/\s/.test(e) || !e.length)) writer.append(`\`${e}\``);
        else writer.append(`${e}`);
        return;
    }
    if (isSymbol(e)) {
        writer.append(`${e.name}`);
        return;
    }

    writer.push();
    _format(e.head, writer);

    if (!e.args) {
        writer.pop();
        return;
    }

    if (isArgumentsArray(e.args)) {
        let prevLiteral = true;
        for (const a of e.args) {
            if (isLiteral(a)) {
                if (prevLiteral) writer.whitespace();
                else writer.newline();
                prevLiteral = true;
            } else {
                prevLiteral = false;
                writer.newline();
            }
            _format(a, writer);
        }
        writer.pop();
        return;
    }

    const keys = Object.keys(e.args);
    if (!keys.length) {
        writer.pop();
        return;
    }

    if (keys.length === 1 && isLiteral(e.args[keys[0]])) {
        writer.whitespace()
        writer.append(`:${keys[0]}`);
        writer.whitespace();
        _format(e.args[keys[0]], writer);
        writer.pop();
        return;
    }

    for (const a of keys) {
        writer.newline();
        writer.append(`:${a}`);
        writer.whitespace();
        _format(e.args[a], writer);
    }
    writer.pop();
}

export function formatMolScript(e: Expression) {
    const writer = new Writer();
    _format(e, writer);
    return writer.getStr();
}
