/*
 *
 * 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.qpid.server.configuration;

import org.apache.qpid.url.AMQBindingURL;
import org.apache.qpid.url.URLSyntaxException;
import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.queue.QueueRegistry;
import org.apache.qpid.server.registry.ApplicationRegistry;
import org.apache.qpid.server.exchange.Exchange;
import org.apache.qpid.server.exchange.ExchangeRegistry;
import org.apache.qpid.server.store.MessageStore;
import org.apache.qpid.AMQException;
import org.apache.log4j.Logger;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.XMLConfiguration;


import java.util.Collection;

public class VirtualHostConfiguration
{
    private static final Logger _logger = Logger.getLogger(VirtualHostConfiguration.class);

    XMLConfiguration _config;

    private static final String XML_VIRTUALHOST = "virtualhost";
    private static final String XML_PATH = "path";
    private static final String XML_BIND = "bind";
    private static final String XML_VIRTUALHOST_PATH = "virtualhost.path";
    private static final String XML_VIRTUALHOST_BIND = "virtualhost.bind";


    public VirtualHostConfiguration(String configFile) throws ConfigurationException
    {
        _logger.info("Loading Config file:" + configFile);

        _config = new XMLConfiguration(configFile);

        if (_config.getProperty(XML_VIRTUALHOST_PATH) == null)
        {
            throw new ConfigurationException(
                    "Virtualhost Configuration document does not contain a valid virtualhost.");
        }
    }

    public void performBindings() throws AMQException, ConfigurationException, URLSyntaxException
    {
        Object prop = _config.getProperty(XML_VIRTUALHOST_PATH);

        if (prop instanceof Collection)
        {
            _logger.debug("Number of VirtualHosts: " + ((Collection) prop).size());

            int virtualhosts = ((Collection) prop).size();
            for (int vhost = 0; vhost < virtualhosts; vhost++)
            {
                loadVirtualHost(vhost);
            }
        }
        else
        {
            loadVirtualHost(-1);
        }
    }

    private void loadVirtualHost(int index) throws AMQException, ConfigurationException, URLSyntaxException
    {
        String path = XML_VIRTUALHOST;

        if (index != -1)
        {
            path = path + "(" + index + ")";
        }

        Object prop = _config.getProperty(path + "." + XML_PATH);

        if (prop == null)
        {
            prop = _config.getProperty(path + "." + XML_BIND);
            String error = "Virtual Host not defined for binding";

            if (prop != null)
            {
                if (prop instanceof Collection)
                {
                    error += "s";
                }

                error += ": " + prop;
            }

            throw new ConfigurationException(error);
        }

        _logger.info("VirtualHost:'" + prop + "'");

        prop = _config.getProperty(path + "." + XML_BIND);
        if (prop instanceof Collection)
        {
            int bindings = ((Collection) prop).size();
            _logger.debug("Number of Bindings: " + bindings);
            for (int dest = 0; dest < bindings; dest++)
            {
                loadBinding(path, dest);
            }
        }
        else
        {
            loadBinding(path, -1);
        }
    }

    private void loadBinding(String rootpath, int index) throws AMQException, ConfigurationException, URLSyntaxException
    {
        String path = rootpath + "." + XML_BIND;
        if (index != -1)
        {
            path = path + "(" + index + ")";
        }

        String bindingString = _config.getString(path);

        AMQBindingURL binding = new AMQBindingURL(bindingString);

        _logger.debug("Loaded Binding:" + binding);

        try
        {
            bind(binding);
        }
        catch (AMQException amqe)
        {
            _logger.info("Unable to bind url: " + binding);
            throw amqe;
        }
    }

    private void bind(AMQBindingURL binding) throws AMQException, ConfigurationException
    {

        String queueName = binding.getQueueName();

        // This will occur if the URL is a Topic
        if (queueName == null)
        {
            //todo register valid topic
            ///queueName = binding.getDestinationName();
            throw new AMQException("Topics cannot be bound. TODO Register valid topic");
        }

        //Get references to Broker Registries
        QueueRegistry queueRegistry = ApplicationRegistry.getInstance().getQueueRegistry();
        MessageStore messageStore = ApplicationRegistry.getInstance().getMessageStore();
        ExchangeRegistry exchangeRegistry = ApplicationRegistry.getInstance().getExchangeRegistry();

        synchronized (queueRegistry)
        {
            AMQQueue queue = queueRegistry.getQueue(queueName);

            if (queue == null)
            {
                _logger.info("Queue '" + binding.getQueueName() + "' does not exists. Creating.");

                queue = new AMQQueue(queueName,
                        Boolean.parseBoolean(binding.getOption(AMQBindingURL.OPTION_DURABLE)),
                        null /* These queues will have no owner */,
                        false /* Therefore autodelete makes no sence */, queueRegistry);

                if (queue.isDurable())
                {
                    messageStore.createQueue(queue);
                }

                queueRegistry.registerQueue(queue);
            }
            else
            {
                _logger.info("Queue '" + binding.getQueueName() + "' already exists not creating.");
            }

            Exchange defaultExchange = exchangeRegistry.getExchange(binding.getExchangeName());
            synchronized (defaultExchange)
            {
                if (defaultExchange == null)
                {
                    throw new ConfigurationException("Attempt to bind queue to unknown exchange:" + binding);
                }

                defaultExchange.registerQueue(queue.getName(), queue, null);

                if (binding.getRoutingKey() == null || binding.getRoutingKey().equals(""))
                {
                    throw new ConfigurationException("Unknown binding not specified on url:" + binding);
                }

                queue.bind(binding.getRoutingKey(), defaultExchange);
            }
            _logger.info("Queue '" + queue.getName() + "' bound to exchange:" + binding.getExchangeName() + " RK:'" + binding.getRoutingKey() + "'");
        }
    }
}
