/*   Copyright 2004 The Apache Software Foundation
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package misc.detailed;

import org.apache.tools.ant.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

import java.io.File;
import java.io.FilenameFilter;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;

/**
 * Date: Feb 8, 2005
 * Time: 11:30:24 AM
 */
public class SampleRunner {

    private List<File> samples;
    private String XMLBEANS_HOME;
    private BuildFileTest runSampleTest;


    @BeforeEach
    public void setUp() throws Exception {
        Project proj = new Project();
        proj.setName("Samples Task Tests");
        XMLBEANS_HOME = new File(proj.getBaseDir(), "build").getAbsolutePath();
        samples = new ArrayList<>();
        runSampleTest = new BuildFileTest();
    }

    @Test
    @Disabled
    public void testSamples() throws Exception {
        loadSampleDirs(new File("./samples"));
        List<Object> exceptions = new ArrayList<>();
        for (File sample : samples) {
            runSampleTest.call_samples_task(sample.getAbsolutePath(), "test", XMLBEANS_HOME);
            BuildException e;
            if ((e = runSampleTest.getAnyExceptions()) != null) {
                exceptions.add(sample.getAbsolutePath());
                exceptions.add(e.getCause());
            }
        }
        if (!exceptions.isEmpty()) {
            throw new RuntimeException(getMessageFromExceptions(exceptions));
        }

    }

    private String getMessageFromExceptions(List<Object> ex) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < ex.size(); i += 2) {
            sb.append("\n\nFILE:").append(ex.get(i));
            sb.append("\n **Error: ").append(((BuildException) ex.get(i + 1)).getMessage());
        }
        return sb.toString();
    }

    private void loadSampleDirs(File dir) {
        assert dir != null && dir.exists();
        File[] files = dir.listFiles(new BuildFilter());
        assert (files != null && files.length == 1);
        samples.add(files[0]);

    }

    private static class BuildFilter
        implements FilenameFilter {
        public boolean accept(File file, String name) {
            return name.equals("build.xml");
        }
    }

    private static class BuildFileTest {

        protected Project project;

        private StringBuilder logBuffer;
        private StringBuilder fullLogBuffer;
        private StringBuilder outBuffer;
        private StringBuilder errBuffer;
        private BuildException buildException;

        protected String getOutput() {
            return cleanBuffer(outBuffer);
        }

        protected String getError() {
            return cleanBuffer(errBuffer);
        }

        protected BuildException getBuildException() {
            return buildException;
        }

        public void call_samples_task(String projectPath, String taskName, String xmlbeansHome) {
            configureProject(projectPath);
            Project proj = getProject();
            proj.setProperty("xmlbeans.home", xmlbeansHome);
            executeTarget(proj.getDefaultTarget());
        }

        public BuildException getAnyExceptions() {
            return this.getBuildException();
        }

        private String cleanBuffer(StringBuilder buffer) {
            StringBuilder cleanedBuffer = new StringBuilder();
            boolean cr = false;
            for (int i = 0; i < buffer.length(); i++) {
                char ch = buffer.charAt(i);
                if (ch == '\r') {
                    cr = true;
                    continue;
                }

                if (!cr) {
                    cleanedBuffer.append(ch);
                } else {
                    if (ch == '\n') {
                        cleanedBuffer.append(ch);
                    } else {
                        cleanedBuffer.append('\r').append(ch);
                    }
                }
            }
            return cleanedBuffer.toString();
        }

        /**
         * set up to run the named project
         *
         * @param filename name of project file to run
         */
        protected void configureProject(String filename) throws BuildException {
            logBuffer = new StringBuilder();
            fullLogBuffer = new StringBuilder();
            project = new Project();
            project.init();
            project.setUserProperty("ant.file", new File(filename).getAbsolutePath());
            project.addBuildListener(new BuildFileTest.AntTestListener());
            //ProjectHelper.configureProject(project, new File(filename));
            ProjectHelper.getProjectHelper().parse(project, new File(filename));
        }

        /**
         * execute a target we have set up.
         * configureProject needs to be called before
         *
         * @param targetName target to run
         */
        protected void executeTarget(String targetName) {
            PrintStream sysOut = System.out;
            PrintStream sysErr = System.err;
            try {
                sysOut.flush();
                sysErr.flush();
                outBuffer = new StringBuilder();
                PrintStream out = new PrintStream(new BuildFileTest.AntOutputStream());
                System.setOut(out);
                errBuffer = new StringBuilder();
                PrintStream err = new PrintStream(new BuildFileTest.AntOutputStream());
                System.setErr(err);
                logBuffer = new StringBuilder();
                fullLogBuffer = new StringBuilder();
                buildException = null;
                project.executeTarget(targetName);
            } finally {
                System.setOut(sysOut);
                System.setErr(sysErr);
                // rajus: 2004/04/07
                System.out.println("STDOUT+STDERR:\n" + getOutput() + getError());
                System.out.println("END STDOUT+STDERR:");
            }

        }

        /**
         * Get the project which has been configured for a test.
         *
         * @return the Project instance for this test.
         */
        protected Project getProject() {
            return project;
        }

        /**
         * an output stream which saves stuff to our buffer.
         */
        private class AntOutputStream extends java.io.OutputStream {
            public void write(int b) {
                outBuffer.append((char) b);
            }
        }

        /**
         * our own personal build listener
         */
        private class AntTestListener implements BuildListener {
            /**
             * Fired before any targets are started.
             */
            public void buildStarted(BuildEvent event) {
            }

            /**
             * Fired after the last target has finished. This event
             * will still be thrown if an error occured during the build.
             *
             * @see BuildEvent#getException()
             */
            public void buildFinished(BuildEvent event) {
            }

            /**
             * Fired when a target is started.
             *
             * @see BuildEvent#getTarget()
             */
            public void targetStarted(BuildEvent event) {
                //System.out.println("targetStarted " + event.getTarget().getName());
            }

            /**
             * Fired when a target has finished. This event will
             * still be thrown if an error occured during the build.
             *
             * @see BuildEvent#getException()
             */
            public void targetFinished(BuildEvent event) {
                //System.out.println("targetFinished " + event.getTarget().getName());
            }

            /**
             * Fired when a task is started.
             *
             * @see BuildEvent#getTask()
             */
            public void taskStarted(BuildEvent event) {
                //System.out.println("taskStarted " + event.getTask().getTaskName());
            }

            /**
             * Fired when a task has finished. This event will still
             * be throw if an error occured during the build.
             *
             * @see BuildEvent#getException()
             */
            public void taskFinished(BuildEvent event) {
                //System.out.println("taskFinished " + event.getTask().getTaskName());
            }

            /**
             * Fired whenever a message is logged.
             *
             * @see BuildEvent#getMessage()
             * @see BuildEvent#getPriority()
             */
            public void messageLogged(BuildEvent event) {
                if (event.getPriority() == Project.MSG_INFO ||
                    event.getPriority() == Project.MSG_WARN ||
                    event.getPriority() == Project.MSG_ERR) {
                    logBuffer.append(event.getMessage());
                }
                fullLogBuffer.append(event.getMessage());

            }
        }


    }
}
