package de.aaschmid.gradle.plugins.cpd.internal.worker;

import de.aaschmid.gradle.plugins.cpd.internal.worker.CpdReportConfiguration.CpdCsvReport;
import de.aaschmid.gradle.plugins.cpd.internal.worker.CpdReportConfiguration.CpdTextReport;
import de.aaschmid.gradle.plugins.cpd.internal.worker.CpdReportConfiguration.CpdXmlReport;
import net.sourceforge.pmd.cpd.CSVRenderer;
import net.sourceforge.pmd.cpd.FileReporter;
import net.sourceforge.pmd.cpd.Match;
import net.sourceforge.pmd.cpd.Renderer;
import net.sourceforge.pmd.cpd.ReportException;
import net.sourceforge.pmd.cpd.SimpleRenderer;
import net.sourceforge.pmd.cpd.XMLRenderer;
import org.gradle.api.GradleException;
import org.gradle.api.logging.Logger;
import org.gradle.api.logging.Logging;

import java.lang.reflect.Field;
import java.util.List;

class CpdReporter {

    private static final Logger logger = Logging.getLogger(CpdReporter.class);

    private final List<CpdReportConfiguration> reports;

    CpdReporter(List<CpdReportConfiguration> reports) {
        this.reports = reports;
    }

    void generate(List<Match> matches) {
        if (logger.isInfoEnabled()) {
            logger.info("Generating reports");
        }
        for (CpdReportConfiguration report : reports) {

            Renderer renderer = createRendererFor(report);
            String renderedMatches;

            ClassLoader previousContextClassLoader = Thread.currentThread().getContextClassLoader();
            try {
                // Workaround for Gradle Worker API uses special classloaders which Xerces dynamic implementation loading does not like
                Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());

                renderedMatches = renderer.render(matches.iterator());
            } finally {
                Thread.currentThread().setContextClassLoader(previousContextClassLoader);
            }

            FileReporter reporter = new FileReporter(report.getDestination(), report.getEncoding());
            try {
                reporter.report(renderedMatches);

            } catch (ReportException e) {
                throw new GradleException(e.getMessage(), e);
            }
        }
    }

    /**
     * @param report the configured reports used
     * @return a full configured {@link Renderer} to generate a CPD single file reports.
     */
    private Renderer createRendererFor(CpdReportConfiguration report) { // TODO CpdRenderer exists since 6.1.0 <- update minimal version!!
        if (report instanceof CpdCsvReport) {
            char separator = ((CpdCsvReport) report).getSeparator();

            if (logger.isDebugEnabled()) {
                logger.debug("Creating renderer to generate CSV file separated by '{}'.", separator);
            }
            return new CSVRenderer(separator);

        } else if (report instanceof CpdTextReport) {
            String lineSeparator = ((CpdTextReport) report).getLineSeparator();
            boolean trimLeadingCommonSourceWhitespaces = ((CpdTextReport) report).getTrimLeadingCommonSourceWhitespaces();

            if (logger.isDebugEnabled()) {
                logger.debug("Creating renderer to generate simple text file separated by '{}' and trimmed '{}'.", lineSeparator, trimLeadingCommonSourceWhitespaces);
            }
            Renderer result = new SimpleRenderer(lineSeparator);
            setTrimLeadingWhitespacesByReflection(result, trimLeadingCommonSourceWhitespaces);
            return result;

        } else if (report instanceof CpdXmlReport) {
            String encoding = report.getEncoding();
            if (logger.isDebugEnabled()) {
                logger.debug("Creating XML renderer to generate with encoding '{}'.", encoding);
            }
            return new XMLRenderer(encoding);
        }
        throw new GradleException(String.format("Cannot create reports for unsupported %s", report.getClass().getCanonicalName()));
    }

    /**
     * Also set second field to trim leading whitespaces.
     * <p/>
     * <i>Information:</i> Use reflection because neither proper constructor for setting both fields nor setter are
     * available.
     */
    private void setTrimLeadingWhitespacesByReflection(Renderer result, boolean trimLeadingCommonSourceWhitespaces) {
        String fieldName = "trimLeadingWhitespace";
        if (logger.isDebugEnabled()) {
            logger.debug("Try setting '{}' field to '{}' for '{}' by reflection.", fieldName, trimLeadingCommonSourceWhitespaces, result);
        }
        try {
            Field field = SimpleRenderer.class.getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(result, trimLeadingCommonSourceWhitespaces);

        } catch (Exception e) {
            if (logger.isWarnEnabled()) { // TODO test if it is really logged?
                logger.warn(String.format("Could not set field '%s' on created SimpleRenderer by reflection due to:", fieldName), e);
            }
        }
    }
}

@@ -403,147 +403,13 @@
 cpd.
-FileReporter;%0Aimport net.sourceforge.pmd.cpd.Match;%0Aimport net.sourceforge.pmd.cpd.Renderer;%0Aimport net.sourceforge.pmd.cpd.ReportException
+Match
 ;%0Aim
@@ -493,24 +493,77 @@
 MLRenderer;%0A
+import net.sourceforge.pmd.cpd.renderer.CPDRenderer;%0A
 import org.g
@@ -667,16 +667,71 @@
 gging;%0A%0A
+import java.io.FileWriter;%0Aimport java.io.IOException;%0A
 import j
@@ -1228,32 +1228,35 @@
  %7B%0A%0A            
+CPD
 Renderer rendere
@@ -1302,31 +1302,78 @@
     
-S
 tr
-ing renderedMatches;
+y (FileWriter fileWriter = new FileWriter(report.getDestination()))%7B
 %0A%0A  
@@ -1382,16 +1382,20 @@
         
+ 
+   
 ClassLoa
@@ -1479,38 +1479,46 @@
 ();%0A            
+    
 try %7B%0A
+    
                 
@@ -1645,32 +1645,36 @@
                 
+ 
+   
 Thread.currentTh
@@ -1758,25 +1758,11 @@
     
-renderedMatches =
+   
  ren
@@ -1792,19 +1792,35 @@
 erator()
-);%0A
+, fileWriter);%0A    
         
@@ -1831,24 +1831,28 @@
 %7D finally %7B%0A
+    
             
@@ -1945,180 +1945,13 @@
     
-%7D%0A%0A
     
-        FileReporter reporter = new FileReporter(report.getDestination(), report.getEncoding());%0A            try %7B%0A                reporter.report(renderedMatches);%0A
+%7D
 %0A   
@@ -1968,22 +1968,18 @@
  catch (
-Report
+IO
 Exceptio
@@ -2175,16 +2175,19 @@
  %7B@link 
+CPD
 Renderer
@@ -2247,16 +2247,19 @@
 private 
+CPD
 Renderer
@@ -2313,75 +2313,8 @@
 t) %7B
- // TODO CpdRenderer exists since 6.1.0 %3C- update minimal version!!
 %0A   
@@ -3127,16 +3127,22 @@
         
+Simple
 Renderer
@@ -4038,16 +4038,19 @@
 lection(
+CPD
 Renderer

remove deprecated API