

package org.juniversal.translator.cplusplus;

import org.eclipse.jdt.core.dom.*;
import org.juniversal.translator.core.ASTUtil;

import java.util.List;

public class SourceTypeDeclarationWriter extends CPlusPlusASTNodeWriter<TypeDeclaration> {
    private boolean outputSomething;

    @SuppressWarnings("unchecked")
    public SourceTypeDeclarationWriter(CPlusPlusSourceFileWriter sourceFileWriter) {
        super(sourceFileWriter);
    }

    @Override
    public void write(TypeDeclaration typeDeclaration) {
        outputSomething = false;

        // Write the static fields, if any
        for (Object bodyDeclaration : typeDeclaration.bodyDeclarations()) {
            if (bodyDeclaration instanceof FieldDeclaration) {
                FieldDeclaration fieldDeclaration = (FieldDeclaration) bodyDeclaration;

                if (!ASTUtil.containsStatic(fieldDeclaration.modifiers()))
                    continue;

                // Skip any Javadoc comments for the field; field comments are just output in the
                // header
                setPositionToStartOfNode(fieldDeclaration);

                writeNode(fieldDeclaration);
                writeln();
                outputSomething = true;
            }
        }

        for (BodyDeclaration bodyDeclaration : (List<BodyDeclaration>) typeDeclaration.bodyDeclarations()) {
            if (bodyDeclaration instanceof MethodDeclaration) {
                MethodDeclaration methodDeclaration = (MethodDeclaration) bodyDeclaration;
                if (methodDeclaration.getBody() == null)
                    continue;

                writeMethod(methodDeclaration);
            } else if (bodyDeclaration instanceof TypeDeclaration) {
                TypeDeclaration nestedTypeDeclaration = (TypeDeclaration) bodyDeclaration;

                writeNestedType(nestedTypeDeclaration);
            }
        }

        setPosition(ASTUtil.getEndPosition(typeDeclaration));
    }

    private void writeNestedType(TypeDeclaration nestedTypeDeclaration) {
        if (outputSomething) {
            getSourceFileWriter().writeln();
            getSourceFileWriter().writeln();
        }

        writeln("/**");
        writeln(" *    " + nestedTypeDeclaration.getName());
        writeln(" */");
        writeln();

        setPositionToStartOfNode(nestedTypeDeclaration);
        writeNode(nestedTypeDeclaration);
        outputSomething = true;
    }

    private void writeMethod(MethodDeclaration methodDeclaration) {
        // We assume that the first non-whitespace text on the first line of the method
        // isn't indented at all--there's nothing in the method left of it. Unindent the
        // whole method by that amount, since methods aren't indented in the C++ source.
        CompilationUnit compilationUnit = getSourceFileWriter().getCompilationUnit();
        int methodLine = compilationUnit.getLineNumber(methodDeclaration.getStartPosition());
        int methodLineStartPosition = compilationUnit.getPosition(methodLine, 0);
        setPosition(methodLineStartPosition);
        skipSpacesAndTabs();
        int additionalIndent = getSourceLogicalColumn();

        int previousIndent = getTargetWriter().setAdditionalIndentation(-1 * additionalIndent);

        getContext().setWritingMethodImplementation(true);

        // Skip back to the beginning of the comments, ignoring any comments associated with
        // the previous node
        setPositionToStartOfNodeSpaceAndComments(methodDeclaration);

        // If we haven't output anything yet, don't include the separator blank lines
        if (!outputSomething)
            skipBlankLines();

        copySpaceAndComments();
        writeNode(methodDeclaration);

        getContext().setWritingMethodImplementation(false);
        getTargetWriter().setAdditionalIndentation(previousIndent);

        copySpaceAndCommentsUntilEOL();
        writeln();

        outputSomething = true;
    }
}
