# Copyright 2013 New Dream Network, LLC (DreamHost)
#
#    Licensed 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.

import itertools
from six import moves

from neutron.plugins.common import constants as qconstants
from neutron.services.loadbalancer import constants


PROTOCOL_MAP = {
    constants.PROTOCOL_TCP: 'tcp',
    constants.PROTOCOL_HTTP: 'http',
    constants.PROTOCOL_HTTPS: 'tcp',
}

BALANCE_MAP = {
    constants.LB_METHOD_ROUND_ROBIN: 'roundrobin',
    constants.LB_METHOD_LEAST_CONNECTIONS: 'leastconn',
    constants.LB_METHOD_SOURCE_IP: 'source'
}

STATS_MAP = {
    constants.STATS_ACTIVE_CONNECTIONS: 'scur',
    constants.STATS_TOTAL_CONNECTIONS: 'stot',
    constants.STATS_IN_BYTES: 'bin',
    constants.STATS_OUT_BYTES: 'bout',
}

ACTIVE_PENDING_STATUSES = qconstants.ACTIVE_PENDING_STATUSES
INACTIVE = qconstants.INACTIVE


def save_config(cfg_data, socket_path=None,
                user_group='nogroup'):
    """Convert a logical configuration to the HAProxy version."""
    data = []
    data.extend(_build_global(socket_path=socket_path,
                              user_group=user_group))
    data.extend(_build_defaults())
    data.extend(cfg_data)
    return '\n'.join(data)


def get_config_data(logical_config):
    data = []
    data.extend(_build_frontend(logical_config))
    data.extend(_build_listeners(logical_config))
    data.extend(_build_backend(logical_config))
    return '\n'.join(data) 


def _build_global(socket_path=None, user_group='nogroup'):
    opts = [
        'daemon',
        'user nobody',
        'group %s' % user_group,
        'log /dev/log local3 info',
        'log /dev/log local3 notice'
    ]

    if socket_path:
        opts.append('stats socket %s mode 0666 level user' % socket_path)

    return itertools.chain(['global'], ('\t' + o for o in opts))


def _build_defaults():
    opts = [
        'log global',
        'retries 3',
        'option splice-auto',
        'option redispatch',
        'timeout connect 5000',
        'timeout client 50000',
        'timeout server 50000',
    ]

    return itertools.chain(['defaults'], ('\t' + o for o in opts))


def _build_frontend(config):
    protocol = config['vip']['protocol']

    opts = [
        'option splice-auto',
        'option tcplog',
        'bind %s:%d transparent' % (
            _get_first_ip_from_port(config['vip']['port']),
            config['vip']['protocol_port']
        ),
        'mode %s' % PROTOCOL_MAP[protocol],
        'default_backend %s' % config['pool']['id'],
    ]

    if config['vip']['connection_limit'] >= 0:
        opts.append('maxconn %s' % config['vip']['connection_limit'])

    if protocol == constants.PROTOCOL_HTTP:
        opts.append('option forwardfor')

    return itertools.chain(
        ['frontend %s' % config['vip']['id']],
        ('\t' + o for o in opts)
    )

def _build_listeners(config):
    # add the listeners
    opts = []
    for listener in config['listeners']:
        protocol = listener['protocol']
        listen_opts = [
        'frontend %s-%s' % (config['vip']['id'],listener['protocol_port']),
        '\toption splice-auto',
        '\toption tcplog',
        '\tbind %s:%d' % (
            _get_first_ip_from_port(config['vip']['port']),
            listener['protocol_port']
        ),
        '\tmode %s' % PROTOCOL_MAP[protocol],
        '\tdefault_backend %s' % config['pool']['id']
        ]
        opts.extend(listen_opts)
    return itertools.chain([],(o for o in opts))

def _build_backend(config):
    protocol = config['pool']['protocol']
    lb_method = config['pool']['lb_method']

    opts = [
        'mode %s' % PROTOCOL_MAP[protocol],
        'balance %s' % BALANCE_MAP.get(lb_method, 'roundrobin'),
        'option splice-auto'
    ]

    if protocol == constants.PROTOCOL_HTTP:
        opts.append('option forwardfor')

    # add the first health_monitor (if available)
    server_addon, health_opts = _get_server_health_option(config)
    opts.extend(health_opts)

    # add session persistence (if available)
    persist_opts = _get_session_persistence(config)
    opts.extend(persist_opts)

    # add the members
    for member in config['members']:
        if ((member['status'] in ACTIVE_PENDING_STATUSES or