/*
 * 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 com.cloud.network.resource;

import java.util.Map;
import java.util.UUID;

import javax.naming.ConfigurationException;
import javax.ws.rs.core.MultivaluedMap;

import org.apache.log4j.Logger;
import org.libvirt.LibvirtException;
import org.midonet.client.MidonetApi;
import org.midonet.client.resource.Bridge;
import org.midonet.client.resource.BridgePort;
import org.midonet.client.resource.Host;

import com.sun.jersey.core.util.MultivaluedMapImpl;

import com.cloud.agent.api.to.NicTO;
import com.cloud.exception.InternalErrorException;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef;
import com.cloud.hypervisor.kvm.resource.VifDriverBase;
import com.cloud.network.Networks;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.script.OutputInterpreter;
import com.cloud.utils.script.Script;

public class MidoNetVifDriver extends VifDriverBase {

    private static final Logger s_logger = Logger.getLogger(MidoNetVifDriver.class);
    private int _timeout;
    private String _midoApiLocation = "http://localhost:8081/";
    private static final String midoPostfix = "mnet";

    @Override
    public void configure(Map<String, Object> params) throws ConfigurationException {

        super.configure(params);

        String value = (String)params.get("scripts.timeout");
        _timeout = NumbersUtil.parseInt(value, 30 * 60) * 1000;

        // Load Midonet API server location
        String midoLoc = (String)params.get("midonet.apiserver.address");
        if (midoLoc != null) {
            _midoApiLocation = midoLoc;
        }
    }

    /*
     * Grab our host id in a file written by Midonet, then
     * return a Host.
     */
    private Host getMyHost(MidonetApi api) {
        Script command = new Script("/bin/bash", _timeout);
        command.add("-c");
        command.add("awk -F'=' '{if ($1~/host/) print $2}' /etc/midolman/host_uuid.properties");

        OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser();
        command.execute(parser);
        String host_uuid = parser.getLines().split("\\n")[0];
        for (Host host : api.getHosts()) {
            if (host.getId().toString().equals(host_uuid)) {
                return host;
            }
        }
        return null;
    }

    /*
     * simple script to add the tap to the host and bring it up.
     */
    private String addTap() {
        OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser();
        Script command = new Script("/bin/bash", _timeout);
        command.add("-c");
        command.add("ip tuntap add mode tap dev '%d" + midoPostfix + "' && ip link | grep " + midoPostfix + " | sort -n | tail -1 | awk -F': ' '{print $2}'");
        command.execute(parser);
        String tapName = parser.getLines().split("\\n")[0];
        command = new Script("/bin/bash", _timeout);
        command.add("-c");
        command.add("ip link set " + tapName + " up");
        command.execute();
        return tapName;
    }

    @Override
    public LibvirtVMDef.InterfaceDef plug(NicTO nic, String guestOsType, String nicAdapter) throws InternalErrorException, LibvirtException {

        if (s_logger.isDebugEnabled()) {
            s_logger.debug("nic=" + nic);
        }

        LibvirtVMDef.InterfaceDef intf = new LibvirtVMDef.InterfaceDef();

        String trafficLabel = nic.getName();

        if (nic.getBroadcastType() == Networks.BroadcastDomainType.Mido && (nic.getType() == Networks.TrafficType.Guest || nic.getType() == Networks.TrafficType.Public)) {
            /*
            * create the tap.
            */
            String tapName = addTap();

            /*
            * grab the tenant id and the network id from the Broadcast URI.
            * We need to pluck the values out of the String. The string
            * should look like "mido://[tenant_id].[bridge_name]"
            */
            MultivaluedMap qNet = new MultivaluedMapImpl();
            String nicAuthority = nic.getBroadcastUri().getAuthority();
            String tenantId = nicAuthority.split("\\.")[0];
            qNet.add("tenant_id", tenantId);
            String url = nicAuthority.split("\\.")[1];
            String netName = url.split(":")[0];

            MidonetApi api = new MidonetApi(_midoApiLocation);
            api.enableLogging();

            for (Bridge b : api.getBridges(qNet)) {
                if (b.getName().equals(netName)) {
                    for (BridgePort p : b.getPorts()) {
                        UUID pvif = p.getVifId();
                        if (pvif != null && p.getVifId().toString().equals(nic.getUuid())) {
                            getMyHost(api).addHostInterfacePort().interfaceName(tapName).portId(p.getId()).create();
                            break;
                        }
                    }
                }
            }

            intf.defEthernet(tapName, nic.getMac(), getGuestNicModel(guestOsType, nicAdapter), "");

        } else {
            throw new InternalErrorException("Only NICs of BroadcastDomain type Mido are supported by the MidoNetVifDriver");
        }

        return intf;
    }

    @Override
    public void unplug(LibvirtVMDef.InterfaceDef iface) {
        String netName = iface.getBrName();

        if (netName != null && netName.contains(midoPostfix)) {
            Script command = new Script("/bin/bash", _timeout);
            command.add("-c");
            command.add("ip tuntap del " + iface.getBrName() + " mode tap");
            command.execute();
        }
    }
}
