/*******************************************************************************
 * Copyright (c) 2010, 2018 Red Hat Inc. and others.
 * 
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *******************************************************************************/
package org.eclipse.linuxtools.changelog.core.tests;

import static org.junit.Assert.*;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Calendar;
import java.util.GregorianCalendar;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.ui.IEditorPart;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.eclipse.linuxtools.changelog.core.IFormatterChangeLogContrib;
import org.eclipse.linuxtools.changelog.tests.fixtures.ChangeLogTestProject;
import org.eclipse.linuxtools.changelog.tests.helpers.EditorHelper;
import org.eclipse.linuxtools.internal.changelog.core.ChangeLogWriter;
import org.eclipse.linuxtools.internal.changelog.core.formatters.GNUFormat;

public class ChangeLogWriterTest {

    // The instance of the class under test
    private ChangeLogWriter clogWriter;
    // A fake project
    private ChangeLogTestProject project;
    // the path elements to the ChangeLog file, absolute to the project root
    private final String CHANGELOG_FILE_PATH = "/project-name/src/org/";
    private final String CHANGELOG_FILE_NAME = "ChangeLog";
    private String changelogFilePath;
    // IFile handle to '/path/changelog/ChangeLog'
    private IFile changelogFile;
    // Current content of the ChangeLog file
    private final String changeLogContent = "2009-10-14  Some Author  <some.author@example.com>\n\n" +
            "\t* this/file/does/not/really/exist/SpringRoll.java: new file\n\n";


    @Before
    public void setUp() throws Exception {
        clogWriter = new ChangeLogWriter();
        // create a testproject and add a file to it
        project = new ChangeLogTestProject("changelogWriterProject");
        // Generate full path to ChangeLog file
        changelogFilePath = CHANGELOG_FILE_PATH + CHANGELOG_FILE_NAME;
        // add a ChangeLog file to the project at the path specified by
        // CHANGELOG_FILE_PATH_SEGMENTS
        InputStream newFileInputStream = new ByteArrayInputStream(
                changeLogContent.getBytes());
        changelogFile = project.addFileToProject(CHANGELOG_FILE_PATH + CHANGELOG_FILE_NAME, CHANGELOG_FILE_NAME,
                newFileInputStream);
    }

    @After
    public void tearDown() throws CoreException {
        // dispose testproject
        project.getTestProject().delete(true, null);
    }

    /**
     * Test for setting and getting the content of the ChangeLog
     * file.
     */
    @Test
    public void testGetSetChangelog() {
        // Open the ChangeLog file and get the IEdiorPart
        final IEditorPart currentContent = EditorHelper.openEditor(changelogFile);
        clogWriter.setChangelog(currentContent);
        assertEquals(currentContent, clogWriter.getChangelog());
        EditorHelper.closeEditor(currentContent);
    }

    @Test
    public void testGetSetChangelogLocation() {
        final String path = "/path/to/changelog/file/ChangeLog";
        clogWriter.setChangelogLocation(path);
        assertEquals(path, clogWriter.getChangelogLocation());
    }

    @Test
    public void testGetSetDateLine() {
        String authorName = "Test Author";
        String email = "spongebob@commedycentral.com";
        clogWriter.setDateLine(new GNUFormat().formatDateLine(
                authorName, email));

        // Today's date in ISO format
        Calendar c = new GregorianCalendar();
        String isoDate = String.format("%1$tY-%1$tm-%1$td", c);

        // expected date/author line
        String expectedDateLine = isoDate + "  " + authorName + "  <" + email + ">\n\n";
        assertEquals(expectedDateLine, clogWriter.getDateLine());
    }

    @Test
    public void testGetSetEntryFilePath() {
        final String entryFilePath = "/some/path/to/some/File.java";
        clogWriter.setEntryFilePath(entryFilePath);
        assertEquals(entryFilePath, clogWriter.getEntryFilePath());
    }

    @Test
    public void testGetSetFormatter() {
        IFormatterChangeLogContrib formatter = new GNUFormat();
        clogWriter.setFormatter(formatter);
        assertEquals(formatter, clogWriter.getFormatter());
    }

    @Test
    public void testGetSetGuessedFName() {
        final String guessedFunctionName = "getInstance";
        clogWriter.setGuessedFName(guessedFunctionName);
        assertEquals(guessedFunctionName, clogWriter.getGuessedFName());
    }

    /**
     * Note that there can be several Changelogs inside a directory tree.
     * The {@link ChangeLogWriter#writeChangeLog()} code assumes that the full path to
     * the ChangeLog file and the full path to the file for which to generate a ChangeLog
     * entry have the same ancestor (with some potential overlap).
     *
     * Consider the following example:
     *
     * 1. The ChangeLog file is <project-root>/src/ChangeLog
     * 2. The currently open editor contains code of <project-root>/src/org/eclipse/example/Test.java
     *
     * In the above case entries in <project-root>/src/ChangeLog *should* be of the form:
     *
     * <code>
     *
     * YYYY-MM-DD  Author Name  <email@example.com>
     *
     *    * org/eclipse/example/Test.java: new File
     *
     * </code>
     *
     * Similarly, if the ChangeLog file is in <project-root>/ChangeLog and the currently open
     * file is <project-root>/src/org/eclipse/example/Sun.java, generated entries in
     * <project-root>/ChangeLog would look like (note the "src" path is added in this case):
     *
     * <code>
     *
     * YYYY-MM-DD  Author Name  <email@example.com>
     *
     *    * src/org/eclipse/example/Sun.java: new File
     *
     * </code>
     *
     * Test for method {@link org.eclipse.linuxtools.internal.changelog.core.ChangeLogWriter#writeChangeLog()}
     */
    @Test
    public void testWriteChangeLog() throws Exception {
        // We want paths up to the ChangeLog file to overlap

        final String pathRelativeToChangeLog = "eclipse/example/test/NewCoffeeMaker.java";
        clogWriter.setEntryFilePath( CHANGELOG_FILE_PATH + pathRelativeToChangeLog );

        // Will show up surrounded by "(" and ")" in ChangeLog
        String guessedFunctionName = "bazinga";
        clogWriter.setGuessedFName(guessedFunctionName);

        // set GNU formatter
        clogWriter.setFormatter(new GNUFormat());

        // Open a document and get the IEditorPart
        IEditorPart editorContent = EditorHelper.openEditor(changelogFile);
        clogWriter.setChangelog(editorContent);

        // set date/author line
        String authorName = "Test Author";
        String email = "test@example.com";
        clogWriter.setDateLine(clogWriter.getFormatter().formatDateLine(
                authorName, email));

        // full absolute path to ChangeLog file (relative to project root)
        clogWriter.setChangelogLocation(changelogFilePath);

        // Write changelog to buffer - need to save for persistence
        clogWriter.writeChangeLog();

        // above written content is not persistent yet; save it to make it persistent
        clogWriter.getChangelog().doSave(null);

        // Today's date in ISO format
        Calendar c = new GregorianCalendar();
        String isoDate = String.format("%1$tY-%1$tm-%1$td", c);

        // Construct the changelog entry by hand and match it with what has been written
        String expectedChangeLogEntry = isoDate + "  " + authorName + "  <" + email + ">\n\n";
        expectedChangeLogEntry += "\t* " + pathRelativeToChangeLog + " (" + guessedFunctionName + "): \n\n";

        String expectedContent = expectedChangeLogEntry + changeLogContent;

        // Read in content written to file
        StringBuilder actualContent = new StringBuilder();
        try (BufferedReader br = new BufferedReader(new InputStreamReader(
                new FileInputStream(changelogFile.getLocation().toFile())))) {
            String line;
            while ((line = br.readLine()) != null) {
                actualContent.append(line + "\n");
            }
        }
        // Assert proper content has been added
        assertEquals(expectedContent, actualContent.toString());
        EditorHelper.closeEditor(editorContent);
    }

    @Test
    public void testGetSetDefaultContent() {
        final String defaultContent = "DISCLAIMER: default ChangeLog content for new files?";
        clogWriter.setDefaultContent(defaultContent);
        assertEquals(defaultContent, clogWriter.getDefaultContent());
    }


    /**
     * Test the use of default text.
     *
     * @throws Exception
     */
    @Test
    public void canWriteChangeLogToEmptyChangeLogButWithSomeDefaultContent() throws Exception {
        // set GNU formatter
        clogWriter.setFormatter(new GNUFormat());

        // Open up a new ChangeLog file at newPathToChangeLog with empty content
        // and get the IEditorPart
        InputStream newFileInputStream = new ByteArrayInputStream(
                "".getBytes()); // no content
        String destinationPath = "/this/is/some/random/path";
        IFile emptyChangeLogFile = project.addFileToProject(destinationPath, CHANGELOG_FILE_NAME,
                newFileInputStream);
        IEditorPart editorContent = EditorHelper.openEditor(emptyChangeLogFile);
        clogWriter.setChangelog(editorContent);

        String authorName = "Test Author";
        String email = "test@example.com";
        clogWriter.setDateLine(clogWriter.getFormatter().formatDateLine(
                authorName, email));
        clogWriter.setChangelogLocation(destinationPath + "/" + CHANGELOG_FILE_NAME);

        // Set some default content
        String defaultContent = "Removed.";
        clogWriter.setDefaultContent(defaultContent);

        String relativePathOfChangedFile = "path/to/file/for/new/entry/test.c";
        clogWriter.setEntryFilePath( destinationPath + "/" + relativePathOfChangedFile  );

        clogWriter.setGuessedFName("");

        // Write changelog to buffer - need to save for persistence
        clogWriter.writeChangeLog();

        // above written content is not persistent yet; save it to make it persistent
        clogWriter.getChangelog().doSave(null);

        // Construct the changelog entry by hand and match it with what has been written
        String expectedChangeLogEntry = new GNUFormat().formatDateLine(authorName, email);
        expectedChangeLogEntry += "\t* " + relativePathOfChangedFile + ": "
            + defaultContent + "\n";

        // Read in content written to file
        StringBuilder actualContent = new StringBuilder();
        try (BufferedReader br = new BufferedReader(new InputStreamReader(
                new FileInputStream(emptyChangeLogFile.getLocation().toFile())))) {
            String line;
            while ((line = br.readLine()) != null) {
                actualContent.append(line + "\n");
            }
        }
        // Assert proper content has been added
        assertEquals(expectedChangeLogEntry, actualContent.toString());
        EditorHelper.closeEditor(editorContent);
    }
}
