package com.redhat.ceylon.eclipse.code.style;

import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.formatter.FormattingContextProperties;
import org.eclipse.jface.text.formatter.IContentFormatter;
import org.eclipse.jface.text.formatter.IContentFormatterExtension;
import org.eclipse.jface.text.formatter.IFormattingContext;
import org.eclipse.jface.text.formatter.IFormattingStrategy;
import org.eclipse.jface.text.source.ISourceViewer;

import ceylon.file.Writer;
import ceylon.file.Writer$impl;
import ceylon.formatter.format_;

import com.redhat.ceylon.compiler.typechecker.parser.CeylonLexer;
import com.redhat.ceylon.compiler.typechecker.parser.CeylonParser;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.eclipse.code.editor.CeylonSourceViewer;
import com.redhat.ceylon.eclipse.ui.CeylonPlugin;

public class CeylonContentFormatter implements IContentFormatter,
        IContentFormatterExtension {

    private CeylonSourceViewer viewer;

    public CeylonContentFormatter(ISourceViewer sourceViewer) {
        if (sourceViewer instanceof CeylonSourceViewer) {
            this.viewer = (CeylonSourceViewer) sourceViewer;
        }
    }

    @Override
    public void format(IDocument document, IRegion region) {
        // TODO Do we need this?
    }

    @Override
    public IFormattingStrategy getFormattingStrategy(String s) {
        return new CeylonFormattingStrategy(s);
    }

    @Override
    public void format(IDocument document, IFormattingContext formattingContext) {
        CeylonLexer lexer = new CeylonLexer(new ANTLRStringStream(
                document.get()));
        try {
            Tree.CompilationUnit cu = new CeylonParser(new CommonTokenStream(
                    lexer)).compilationUnit();
            lexer.reset();
            final StringBuilder builder = new StringBuilder(
                    document.getLength());
            format_.format(
                    cu,
                    ((FormatterPreferences) formattingContext
                            .getProperty(FormattingContextProperties.CONTEXT_PREFERENCES))
                            .getOptions(), new StringBuilderWriter(builder));
            document.set(builder.toString());
        } catch (RecognitionException re) {
            CeylonPlugin
                    .getInstance()
                    .getLog()
                    .log(new StatusInfo(IStatus.WARNING,
                            "Error formatting sample code, should not have happened"));
        }
    }

    private static class StringBuilderWriter implements Writer {
        private final StringBuilder builder;

        public StringBuilderWriter(StringBuilder builder) {
            this.builder = builder;
        }

        @Override
        public Object write(String string) {
            builder.append(string);
            return null; // void
        }

        // the rest is boring

        @Override
        public Writer$impl $ceylon$file$Writer$impl() {
            return new Writer$impl(this);
        }

        @Override
        public Object close() {
            return null; // void
        }

        @Override
        public Object destroy(Throwable arg0) {
            return null; // void
        }

        @Override
        public Object flush() {
            return null; // void
        }

        @Override
        public Object writeLine() {
            // unused; ceylon.formatter has its own newline handling
            throw new UnsupportedOperationException();
        }

        @Override
        public Object writeLine(String line) {
            // unused; ceylon.formatter has its own newline handling
            throw new UnsupportedOperationException();
        }

        @Override
        public String writeLine$line() {
            return ""; // default value for "line" parameter
        }

        @Override
        public Object writeBytes(
                ceylon.language.Iterable<? extends ceylon.language.Byte, ? extends Object> bytes) {
            // unused; ceylon.formatter never writes bytes
            throw new UnsupportedOperationException();
        }
    }
}

@@ -670,67 +670,8 @@
 r;%0A%0A
-import ceylon.file.Writer;%0Aimport ceylon.file.Writer$impl;%0A
 impo
@@ -699,16 +699,16 @@
 ormat_;%0A
+
 %0Aimport 
@@ -1004,16 +1004,75 @@
 nPlugin;
+%0Aimport com.redhat.ceylon.eclipse.util.StringBuilderWriter;
 %0A%0Apublic
@@ -2646,16 +2646,16 @@
 ARNING,%0A
+
         
@@ -2753,1627 +2753,7 @@
    %7D
-%0A%0A    private static class StringBuilderWriter implements Writer %7B%0A        private final StringBuilder builder;%0A%0A        public StringBuilderWriter(StringBuilder builder) %7B%0A            this.builder = builder;%0A        %7D%0A%0A        @Override%0A        public Object write(String string) %7B%0A            builder.append(string);%0A            return null; // void%0A        %7D%0A%0A        // the rest is boring%0A%0A        @Override%0A        public Writer$impl $ceylon$file$Writer$impl() %7B%0A            return new Writer$impl(this);%0A        %7D%0A%0A        @Override%0A        public Object close() %7B%0A            return null; // void%0A        %7D%0A%0A        @Override%0A        public Object destroy(Throwable arg0) %7B%0A            return null; // void%0A        %7D%0A%0A        @Override%0A        public Object flush() %7B%0A            return null; // void%0A        %7D%0A%0A        @Override%0A        public Object writeLine() %7B%0A            // unused; ceylon.formatter has its own newline handling%0A            throw new UnsupportedOperationException();%0A        %7D%0A%0A        @Override%0A        public Object writeLine(String line) %7B%0A            // unused; ceylon.formatter has its own newline handling%0A            throw new UnsupportedOperationException();%0A        %7D%0A%0A        @Override%0A        public String writeLine$line() %7B%0A            return %22%22; // default value for %22line%22 parameter%0A        %7D%0A%0A        @Override%0A        public Object writeBytes(%0A                ceylon.language.Iterable%3C? extends ceylon.language.Byte, ? extends Object%3E bytes) %7B%0A            // unused; ceylon.formatter never writes bytes%0A            throw new UnsupportedOperationException();%0A        %7D%0A    %7D
 %0A%7D%0A

Use public StringBuilderWriter