/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.felix.bundleplugin.baseline;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Map;
import java.util.Map.Entry;

import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;

/**
 * BND Baseline check between two bundles.
 *
 * @goal baseline
 * @phase verify
 * @threadSafe true
 * @since 2.4.1
 */
public final class BaselinePlugin
    extends AbstractBaselinePlugin
{

    private static final String TABLE_PATTERN = "%s %-50s %-10s %-10s %-10s %-10s %-10s";

    /**
     * An XML output file to render to <code>${project.build.directory}/baseline.xml</code>.
     *
     * @parameter expression="${project.build.directory}/baseline.xml"
     */
    private File xmlOutputFile;

    /**
     * Whether to log the results to the console or not, true by default.
     *
     * @parameter expression="${logResults}" default-value="true"
     */
    private boolean logResults;

    private FileWriter xmlFileWriter;

    private PrettyPrintXMLWriter xmlWriter;

    protected void init()
    {
        if ( xmlOutputFile != null )
        {
            xmlOutputFile.getParentFile().mkdirs();
            try
            {
                xmlFileWriter = new FileWriter( xmlOutputFile );
                xmlWriter = new PrettyPrintXMLWriter( xmlFileWriter );
            }
            catch ( IOException e )
            {
                getLog().warn( "No XML report will be produced, cannot write data to " + xmlOutputFile, e );
            }
        }
    }

    protected void startBaseline( String generationDate,
                                  String bundleName,
                                  String currentVersion,
                                  String previousVersion )
    {
        if ( isLoggingResults() )
        {
            log( "Baseline Report - Generated by Apache Felix Maven Bundle Plugin on %s based on Bnd - see http://www.aqute.biz/Bnd/Bnd",
                 generationDate );
            log( "Comparing bundle %s version %s to version %s", bundleName, currentVersion, previousVersion );
            log( "" );
            log( TABLE_PATTERN,
                 " ",
                 "PACKAGE_NAME",
                 "DELTA",
                 "CUR_VER",
                 "BASE_VER",
                 "REC_VER",
                 "WARNINGS",
                 "ATTRIBUTES" );
            log( TABLE_PATTERN,
                 "=",
                 "==================================================",
                 "==========",
                 "==========",
                 "==========",
                 "==========",
                 "==========",
                 "==========" );
        }

        if ( isProducingXml() )
        {
            xmlWriter.startElement( "baseline" );
            xmlWriter.addAttribute( "version", "1.0.0" );
            xmlWriter.addAttribute( "vendor", "The Apache Software Foundation" );
            xmlWriter.addAttribute( "vendorURL", "http://www.apache.org/" );
            xmlWriter.addAttribute( "generator", "Apache Felix Maven Bundle Plugin" );
            xmlWriter.addAttribute( "generatorURL", "http://felix.apache.org/site/apache-felix-maven-bundle-plugin-bnd.html" );
            xmlWriter.addAttribute( "analyzer", "Bnd" );
            xmlWriter.addAttribute( "analyzerURL", "http://www.aqute.biz/Bnd/Bnd" );
            xmlWriter.addAttribute( "generatedOn", generationDate );
            xmlWriter.addAttribute( "bundleName", bundleName );
            xmlWriter.addAttribute( "currentVersion", currentVersion );
            xmlWriter.addAttribute( "previousVersion", previousVersion );
        }
    }

    protected void startPackage( boolean mismatch,
                                 String name,
                                 String shortDelta,
                                 String delta,
                                 String newerVersion,
                                 String olderVersion,
                                 String suggestedVersion,
                                 DiffMessage diffMessage,
                                 Map<String,String> attributes )
    {
        if ( isLoggingResults() )
        {
            log( TABLE_PATTERN,
                 mismatch ? '*' : shortDelta,
                 name,
                 delta,
                 newerVersion,
                 olderVersion,
                 suggestedVersion,
                 diffMessage != null ? diffMessage : '-',
                 attributes );
        }

        if ( isProducingXml() )
        {
            xmlWriter.startElement( "package" );
            xmlWriter.addAttribute( "name", name );
            xmlWriter.addAttribute( "delta", delta );
            simpleElement( "mismatch", String.valueOf( mismatch ) );
            simpleElement( "newerVersion", newerVersion );
            simpleElement( "olderVersion", olderVersion );
            simpleElement( "suggestedVersion", suggestedVersion );

            if ( diffMessage != null )
            {
                simpleElement( diffMessage.getType().name(), diffMessage.getMessage() );
            }

            xmlWriter.startElement( "attributes" );
            if (attributes != null)
            {
                for (Entry<String, String> attribute : attributes.entrySet())
                {
                    String attributeName = attribute.getKey();
                    if (':' == attributeName.charAt(attributeName.length() - 1))
                    {
                        attributeName = attributeName.substring(0, attributeName.length() - 1);
                    }
                    String attributeValue = attribute.getValue();

                    xmlWriter.startElement(attributeName);
                    xmlWriter.writeText(attributeValue);
                    xmlWriter.endElement();
                }
            }
            xmlWriter.endElement();
        }
    }

    protected void startDiff( int depth, String type, String name, String delta, String shortDelta )
    {
        if ( isLoggingResults() )
        {
            log( "%-" + (depth * 4) + "s %s %s %s",
                 "",
                 shortDelta,
                 type,
                 name );
        }

        if ( isProducingXml() )
        {
            xmlWriter.startElement( type );
            xmlWriter.addAttribute( "name", name );
            xmlWriter.addAttribute( "delta", delta );
        }
    }

    protected void endDiff( int depth )
    {
        if ( isProducingXml() )
        {
            xmlWriter.endElement();
        }
    }

    protected void endPackage()
    {
        if ( isLoggingResults() )
        {
            log( "-----------------------------------------------------------------------------------------------------------" );
        }

        if ( isProducingXml() )
        {
            xmlWriter.endElement();
        }
    }

    protected void endBaseline()
    {
        if ( xmlWriter != null )
        {
            xmlWriter.endElement();
            IOUtil.close( xmlFileWriter );
        }
    }

    private boolean isProducingXml()
    {
        return xmlFileWriter!= null && xmlWriter != null;
    }

    private boolean isLoggingResults()
    {
        return logResults && getLog().isInfoEnabled();
    }

    private void log( String format, Object...args )
    {
        getLog().info( String.format( format, args ) );
    }

    private void simpleElement( String name, String value )
    {
        xmlWriter.startElement( name );
        xmlWriter.writeText( value );
        xmlWriter.endElement();
    }

}
