# -*- coding: utf8 -*-
#
#    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.

from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from horizon import messages
from horizon import tables
from horizon.utils import memoized

from tuskar_ui import api


class DeleteNode(tables.BatchAction):
    name = "delete"
    action_present = _("Delete")
    action_past = _("Deleting")
    data_type_singular = _("Node")
    data_type_plural = _("Nodes")
    classes = ('btn-danger',)

    def allowed(self, request, obj=None):
        if not obj:
            # this is necessary because table actions use this function
            # with obj=None
            return True
        return (getattr(obj, 'instance_uuid', None) is None and
                obj.power_state not in api.node.POWER_ON_STATES)

    def action(self, request, obj_id):
        if obj_id is None:
            messages.error(request, _("Select some nodes to delete."))
            return
        api.node.Node.delete(request, obj_id)


class ActivateNode(tables.BatchAction):
    name = "activate"
    action_present = _("Activate")
    action_past = _("Activated")
    data_type_singular = _("Node")
    data_type_plural = _("Nodes")

    def allowed(self, request, obj=None):
        if not obj:
            # this is necessary because table actions use this function
            # with obj=None
            return True
        return (obj.cpus and obj.memory_mb and obj.local_gb and
                obj.cpu_arch)

    def action(self, request, obj_id):
        if obj_id is None:
            messages.error(request, _("Select some nodes to activate."))
            return
        api.node.Node.set_maintenance(request, obj_id, False)
        api.node.Node.set_power_state(request, obj_id, 'off')


class SetPowerStateOn(tables.BatchAction):
    name = "set_power_state_on"
    action_present = _("Power On")
    action_past = _("Powering On")
    data_type_singular = _("Node")
    data_type_plural = _("Nodes")

    def allowed(self, request, obj=None):
        if not obj:
            # this is necessary because table actions use this function
            # with obj=None
            return True
        return obj.power_state not in api.node.POWER_ON_STATES

    def action(self, request, obj_id):
        if obj_id is None:
            messages.error(request, _("Select some nodes to power on."))
            return
        api.node.Node.set_power_state(request, obj_id, 'on')


class SetPowerStateOff(tables.BatchAction):
    name = "set_power_state_off"
    action_present = _("Power Off")
    action_past = _("Powering Off")
    data_type_singular = _("Node")
    data_type_plural = _("Nodes")

    def allowed(self, request, obj=None):
        if not obj:
            # this is necessary because table actions use this function
            # with obj=None
            return True
        return (
            obj.power_state in api.node.POWER_ON_STATES and
            getattr(obj, 'instance_uuid', None) is None
        )

    def action(self, request, obj_id):
        if obj_id is None:
            messages.error(request, _("Select some nodes to power off."))
            return
        api.node.Node.set_power_state(request, obj_id, 'off')


class NodeFilterAction(tables.FilterAction):
    def filter(self, table, nodes, filter_string):
        """Really naive case-insensitive search."""
        q = filter_string.lower()

        def comp(node):
            return any(q in unicode(value).lower() for value in (
                node.ip_address,
                node.cpus,
                node.memory_mb,
                node.local_gb,
            ))

        return filter(comp, nodes)


class DiscoverNode(tables.BatchAction):
    name = "discover_nodes"
    action_present = _("Discover")
    action_past = _("Discovered")
    data_type_singular = _("Node")
    data_type_plural = _("Nodes")

    def allowed(self, request, obj=None):
        if not obj:
            # this is necessary because table actions use this function
            # with obj=None
            return True
        return obj.state == api.node.MAINTENANCE_STATE

    def action(self, request, obj_id):
        if obj_id is None:
            messages.error(request, _("Select some nodes to discover."))
            return
        api.node.Node.discover(request, [obj_id])


@memoized.memoized
def _get_role_link(role_id):
    if role_id:
        return reverse('horizon:infrastructure:roles:detail',
                       kwargs={'role_id': role_id})


def get_role_link(datum):
    return _get_role_link(getattr(datum, 'role_id', None))


def get_power_state_with_transition(node):
    if node.target_power_state and (
            node.power_state != node.target_power_state):
        return "{0} -> {1}".format(
            node.power_state, node.target_power_state)
    return node.power_state


def get_state_string(node):
    state_dict = {
        api.node.DISCOVERING_STATE: _('Discovering'),
        api.node.DISCOVERED_STATE: _('Discovered'),
        api.node.PROVISIONED_STATE: _('Provisioned'),
        api.node.PROVISIONING_FAILED_STATE: _('Provisioning Failed'),
        api.node.PROVISIONING_STATE: _('Provisioning'),
        api.node.FREE_STATE: _('Free'),
    }

    node_state = node.state
    return state_dict.get(node_state, node_state)


class BaseNodesTable(tables.DataTable):
    node = tables.Column('uuid',
                         link="horizon:infrastructure:nodes:node_detail",
                         verbose_name=_("Node Name"))
    role_name = tables.Column('role_name',
                              link=get_role_link,
                              verbose_name=_("Deployment Role"))
    cpus = tables.Column('cpus',
                         verbose_name=_("CPU (cores)"))
    memory_mb = tables.Column('memory_mb',
                              verbose_name=_("Memory (MB)"))
    local_gb = tables.Column('local_gb',
                             verbose_name=_("Disk (GB)"))
    power_status = tables.Column(get_power_state_with_transition,
                                 verbose_name=_("Power Status"))
    state = tables.Column(get_state_string,
                          verbose_name=_("Status"))

    class Meta(object):
        name = "nodes_table"
        verbose_name = _("Nodes")
        table_actions = (NodeFilterAction, SetPowerStateOn, SetPowerStateOff,
                         DeleteNode)
        row_actions = (SetPowerStateOn, SetPowerStateOff, DeleteNode)
        template = "horizon/common/_enhanced_data_table.html"

    def get_object_id(self, datum):
        return datum.uuid

    def get_object_display(self, datum):
        return datum.uuid


class AllNodesTable(BaseNodesTable):

    class Meta(object):
        name = "all_nodes_table"
        verbose_name = _("All")
        hidden_title = False
        columns = ('node', 'cpus', 'memory_mb', 'local_gb', 'power_status',
                   'state')
        table_actions = (NodeFilterAction, SetPowerStateOn, SetPowerStateOff,
                         DeleteNode)
        row_actions = (SetPowerStateOn, SetPowerStateOff, DeleteNode)
        template = "horizon/common/_enhanced_data_table.html"


class ProvisionedNodesTable(BaseNodesTable):

    class Meta(object):
        name = "provisioned_nodes_table"
        verbose_name = _("Provisioned")
        hidden_title = False
        table_actions = (NodeFilterAction, SetPowerStateOn, SetPowerStateOff,
                         DeleteNode)
        row_actions = (SetPowerStateOn, SetPowerStateOff, DeleteNode)
        template = "horizon/common/_enhanced_data_table.html"


class FreeNodesTable(BaseNodesTable):

    class Meta(object):
        name = "free_nodes_table"
        verbose_name = _("Free")
        hidden_title = False
        columns = ('node', 'cpus', 'memory_mb', 'local_gb', 'power_status')
        table_actions = (NodeFilterAction, SetPowerStateOn, SetPowerStateOff,
                         DeleteNode)
        row_actions = (SetPowerStateOn, SetPowerStateOff, DeleteNode,)
        template = "horizon/common/_enhanced_data_table.html"


class MaintenanceNodesTable(BaseNodesTable):

    class Meta(object):
        name = "maintenance_nodes_table"
        verbose_name = _("Maintenance")
        hidden_title = False
        columns = ('node', 'cpus', 'memory_mb', 'local_gb', 'power_status',
                   'state')
        table_actions = (NodeFilterAction, ActivateNode, SetPowerStateOn,
                         SetPowerStateOff, DiscoverNode, DeleteNode)
        row_actions = (ActivateNode, SetPowerStateOn, SetPowerStateOff,
                       DeleteNode)
        template = "horizon/common/_enhanced_data_table.html"
