import * as Lint from "tslint/lib";
import * as ts from "typescript";

const OPTION_CATCH = "check-catch";
const OPTION_ELSE = "check-else";

export class Rule extends Lint.Rules.AbstractRule {
    public static CATCH_FAILURE_STRING = "'catch' should not be on the same line as the preceeding block's curly brace";
    public static ELSE_FAILURE_STRING = "'else' should not be on the same line as the preceeding block's curly brace";

    public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
        return this.applyWithWalker(new NextLineWalker(sourceFile, this.getOptions()));
    }
}

class NextLineWalker extends Lint.RuleWalker {
    public visitIfStatement(node: ts.IfStatement) {
        const sourceFile = node.getSourceFile();
        const thenStatement = node.thenStatement;

        const elseStatement = node.elseStatement;
        if (!!elseStatement) {
            // find the else keyword
            const elseKeyword = getFirstChildOfKind(node, ts.SyntaxKind.ElseKeyword);
            if (this.hasOption(OPTION_ELSE) && !!elseKeyword) {
                const thenStatementEndLoc = sourceFile.getLineAndCharacterOfPosition(thenStatement.getEnd());
                const elseKeywordLoc = sourceFile.getLineAndCharacterOfPosition(elseKeyword.getStart(sourceFile));
                if (thenStatementEndLoc.line === elseKeywordLoc.line) {
                    const failure = this.createFailure(elseKeyword.getStart(sourceFile), elseKeyword.getWidth(sourceFile), Rule.ELSE_FAILURE_STRING);
                    this.addFailure(failure);
                }
            }
        }

        super.visitIfStatement(node);
    }

    public visitTryStatement(node: ts.TryStatement) {
        const sourceFile = node.getSourceFile();
        const catchClause = node.catchClause;

        // "visit" try block
        const tryBlock = node.tryBlock;

        if (this.hasOption(OPTION_CATCH) && !!catchClause) {
            const tryClosingBrace = tryBlock.getLastToken(sourceFile);
            const catchKeyword = catchClause.getFirstToken(sourceFile);
            const tryClosingBraceLoc = sourceFile.getLineAndCharacterOfPosition(tryClosingBrace.getEnd());
            const catchKeywordLoc = sourceFile.getLineAndCharacterOfPosition(catchKeyword.getStart(sourceFile));
            if (tryClosingBraceLoc.line === catchKeywordLoc.line) {
                const failure = this.createFailure(catchKeyword.getStart(sourceFile), catchKeyword.getWidth(sourceFile), Rule.CATCH_FAILURE_STRING);
                this.addFailure(failure);
            }
        }
        super.visitTryStatement(node);
    }
}

function getFirstChildOfKind(node: ts.Node, kind: ts.SyntaxKind) {
    return node.getChildren().filter((child) => child.kind === kind)[0];
}
