/*
 * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */

package org.opendaylight.controller.netconf.util.messages;

import com.google.common.base.Preconditions;

import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;

import org.opendaylight.controller.netconf.api.NetconfSession;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;

public final class SendErrorExceptionUtil {
    private static final Logger logger = LoggerFactory.getLogger(SendErrorExceptionUtil.class);

    private SendErrorExceptionUtil() {}

    public static void sendErrorMessage(final NetconfSession session,
            final NetconfDocumentedException sendErrorException) {
        logger.trace("Sending error {}", sendErrorException.getMessage(), sendErrorException);
        final Document errorDocument = createDocument(sendErrorException);
        ChannelFuture f = session.sendMessage(new NetconfMessage(errorDocument));
        f.addListener(new SendErrorVerifyingListener(sendErrorException));
    }

    public static void sendErrorMessage(Channel channel, NetconfDocumentedException sendErrorException) {
        logger.trace("Sending error {}", sendErrorException.getMessage(), sendErrorException);
        final Document errorDocument = createDocument(sendErrorException);
        ChannelFuture f = channel.writeAndFlush(new NetconfMessage(errorDocument));
        f.addListener(new SendErrorVerifyingListener(sendErrorException));
    }

    public static void sendErrorMessage(NetconfSession session, NetconfDocumentedException sendErrorException,
            NetconfMessage incommingMessage) {
        final Document errorDocument = createDocument(sendErrorException);
        logger.trace("Sending error {}", XmlUtil.toString(errorDocument));
        tryToCopyAttributes(incommingMessage.getDocument(), errorDocument, sendErrorException);
        ChannelFuture f = session.sendMessage(new NetconfMessage(errorDocument));
        f.addListener(new SendErrorVerifyingListener(sendErrorException));
    }

    private static void tryToCopyAttributes(final Document incommingDocument, final Document errorDocument,
            final NetconfDocumentedException sendErrorException) {
        try {
            final Element incommingRpc = incommingDocument.getDocumentElement();
            Preconditions.checkState(incommingRpc.getTagName().equals(XmlNetconfConstants.RPC_KEY), "Missing "
                    + XmlNetconfConstants.RPC_KEY + " " + "element");

            final Element rpcReply = errorDocument.getDocumentElement();
            Preconditions.checkState(rpcReply.getTagName().equals(XmlNetconfConstants.RPC_REPLY_KEY), "Missing "
                    + XmlNetconfConstants.RPC_REPLY_KEY + " element");

            final NamedNodeMap incomingAttributes = incommingRpc.getAttributes();
            for (int i = 0; i < incomingAttributes.getLength(); i++) {
                final Attr attr = (Attr) incomingAttributes.item(i);
                // skip namespace
                if (attr.getNodeName().equals(XmlUtil.XMLNS_ATTRIBUTE_KEY)) {
                    continue;
                }
                rpcReply.setAttributeNode((Attr) errorDocument.importNode(attr, true));
            }
        } catch (final Exception e) {
            logger.warn("Unable to copy incomming attributes to {}, returned rpc-error might be invalid for client",
                    sendErrorException, e);
        }
    }

    private static Document createDocument(final NetconfDocumentedException sendErrorException) {
        return sendErrorException.toXMLDocument();
    }

    /**
     * Checks if netconf error was sent successfully.
     */
    private static final class SendErrorVerifyingListener implements ChannelFutureListener {
        private final NetconfDocumentedException sendErrorException;

        public SendErrorVerifyingListener(final NetconfDocumentedException sendErrorException) {
            this.sendErrorException = sendErrorException;
        }

        @Override
        public void operationComplete(final ChannelFuture channelFuture) throws Exception {
            Preconditions.checkState(channelFuture.isSuccess(), "Unable to send exception {}", sendErrorException,
                    channelFuture.cause());
        }
    }
}

@@ -3069,33 +3069,44 @@
 _KEY), %22Missing 
-%22
+%25s element%22,
 %0A               
@@ -3101,34 +3101,32 @@
                 
- +
  XmlNetconfConst
@@ -3141,26 +3141,8 @@
 _KEY
- + %22 %22 + %22element%22
 );%0A%0A
@@ -3325,17 +3325,28 @@
 Missing 
-%22
+%25s element%22,
 %0A       
@@ -3357,18 +3357,16 @@
         
- +
  XmlNetc
@@ -3395,21 +3395,8 @@
 _KEY
- + %22 element%22
 );%0A%0A
@@ -4850,18 +4850,18 @@
 ception 
-%7B%7D
+%25s
 %22, sendE

Fix precondition formatting