# Copyright 2014 Budapest University of Technology and Economics (BME IK)
#
# This file is part of CIRCLE Cloud.
#
# CIRCLE is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# CIRCLE is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along
# with CIRCLE.  If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals, absolute_import

import errno
import logging

from django.contrib import messages
from django.core.urlresolvers import reverse
from django.db.models import Q
from django.utils.translation import ugettext_lazy as _
from django.views.generic import UpdateView, TemplateView

from sizefield.utils import filesizeformat
from braces.views import SuperuserRequiredMixin

from common.models import WorkerNotFound
from storage.models import DataStore, Disk
from ..tables import DiskListTable
from ..forms import DataStoreForm, DiskForm 

from django.shortcuts import get_object_or_404, redirect
from django.db import IntegrityError

logger = logging.getLogger(__name__)


class StorageDetail(SuperuserRequiredMixin, TemplateView):
    template_name = "dashboard/storage/detail.html"

    def _current_querystring(self):
        # Preserve UI state across redirects.
        parts = []
        ds = self.request.GET.get("ds")
        if ds:
            parts.append("ds=%s" % ds)

        flt = self.request.GET.get("filter")
        if flt:
            parts.append("filter=%s" % flt)

        s = self.request.GET.get("s")
        if s:
            parts.append("s=%s" % s)

        return ("?" + "&".join(parts)) if parts else ""

    def _redirect_with_ds(self, ds_pk):
        # Redirect back to page selecting a specific datastore.
        parts = ["ds=%s" % ds_pk]
        flt = self.request.GET.get("filter")
        if flt:
            parts.append("filter=%s" % flt)
        s = self.request.GET.get("s")
        if s:
            parts.append("s=%s" % s)
        return redirect("%s?%s" % (self.request.path, "&".join(parts)))

    def get_datastore(self):
        ds_id = self.request.GET.get("ds")
        qs = DataStore.objects.order_by("name")
    
        # "new" (or empty) means create mode, no existing datastore.
        if ds_id == "new":
            return None
        if ds_id:
            return get_object_or_404(DataStore, pk=ds_id)
        return qs.first()  # can be None if empty

    def post(self, request, *args, **kwargs):
        ds_id = request.POST.get("ds")  # hidden field in template: "new" or an id
    
        instance = None
        if ds_id and ds_id != "new":
            instance = get_object_or_404(DataStore, pk=int(ds_id))
    
        form = DataStoreForm(request.POST, instance=instance)
        if not form.is_valid():
            context = self.get_context_data()
            context["form"] = form
            return self.render_to_response(context)
    
        try:
            ds = form.save()
        except IntegrityError as e:
            messages.error(request, _("Could not save datastore: %s") % e)
            return redirect(request.path)
    
        messages.success(request, _("Datastore saved."))
        return redirect("%s?ds=%s" % (request.path, ds.pk))
    

#    def post(self, request, *args, **kwargs):
#        action = request.POST.get("action")
#        if action == "create":
#            return self._handle_create(request)
#        if action == "update":
#            return self._handle_update(request)
#        messages.error(request, _("Unknown action."))
#        return redirect(request.path + self._current_querystring())

    def _handle_create(self, request):
        form = DataStoreCreateForm(request.POST)
        if not form.is_valid():
            context = self.get_context_data()
            context["create_form"] = form
            return self.render_to_response(context)

        try:
            ds = form.save()
        except IntegrityError as e:
            messages.error(request, _("Could not create datastore: %s") % e)
            return redirect(request.path + self._current_querystring())

        messages.success(request, _("Datastore created."))
        return self._redirect_with_ds(ds.pk)

    def _handle_update(self, request):
        ds = self.get_datastore()
        if ds is None:
            messages.error(request, _("No datastore selected."))
            return redirect(request.path)

        form = DataStoreForm(request.POST, instance=ds)
        if not form.is_valid():
            context = self.get_context_data()
            context["edit_form"] = form
            return self.render_to_response(context)

        try:
            ds = form.save()
        except IntegrityError as e:
            messages.error(request, _("Could not update datastore: %s") % e)
            return self._redirect_with_ds(ds.pk)

        messages.success(request, _("Datastore updated."))
        return self._redirect_with_ds(ds.pk)

    def get_table_data(self, ds, missing):
        if ds is None:
            return Disk.objects.none()

        qs = Disk.objects.filter(datastore=ds, destroyed=None)

        filter_name = self.request.GET.get("filter")
        search = self.request.GET.get("s")

        filter_queries = {
            'vm': {'instance_set__isnull': False},
            'template': {'template_set__isnull': False},
            'none': {'template_set__isnull': True, 'instance_set__isnull': True},
        }

        if filter_name:
            if filter_name == 'missing':
                qs = missing 
            else: 
                qs = qs.filter(**filter_queries.get(filter_name, {}))

        if search:
            qs = qs.filter(
                Q(name__icontains=search) |
                Q(filename__icontains=search) |
                Q(instance_set__name__icontains=search) |
                Q(template_set__name__icontains=search)
            ).distinct()

        return qs

    def get_stats(self, ds):
        stats = ds.get_statistics()
        free_space = int(stats['free_space'])
        free_percent = float(stats['free_percent'])

        total_space = free_space / (free_percent / 100.0)
        used_space = total_space - free_space

        data = ds.get_file_statistics()
        dumps_size = sum(d['size'] for d in data['dumps'])
        trash = sum(d['size'] for d in data['trash'])
        iso_raw = sum(d['size'] for d in data['disks'] if d['format'] in ("iso", "raw"))

        vm_size = vm_actual_size = template_actual_size = 0
        for d in data['disks']:
            if d['format'] == "qcow2" and d['type'] == "normal":
                template_actual_size += d['actual_size']
            else:
                vm_size += d['size']
                vm_actual_size += d['actual_size']

        return {
            'used_percent': int(100 - free_percent),
            'free_space': filesizeformat(free_space),
            'used_space': filesizeformat(used_space),
            'total_space': filesizeformat(total_space),
            'dumps': dumps_size,
            'trash': trash,
            'iso_raw': iso_raw,
            'vm_size': vm_size,
            'vm_actual_size': vm_actual_size,
            'template_actual_size': template_actual_size,
        }

    def get_context_data(self, **kwargs):
        context = super(StorageDetail, self).get_context_data(**kwargs)

        ds = self.get_datastore()
        context["datastores"] = DataStore.objects.order_by("name")
        context["ds"] = ds

        # If no datastore exists yet, only show the create form.
        if ds is not None:
            context["form"] = DataStoreForm(instance=ds)
            context["mode"] = "update"
            context["ds_selected"] = str(ds.pk)
            try:
                context["stats"] = self.get_stats(ds)
                context["missing_disks"] = ds.get_missing_disks()
                context["orphan_disks"] = ds.get_orphan_disks()
            except WorkerNotFound:
                messages.error(self.request, _("The DataStore is offline."))
            except (OSError, IOError) as e:
                messages.error(self.request, e)
        else:
            context["form"] = DataStoreForm()
            context["mode"] = "create"
            context["ds_selected"] = "new"
            context["stats"] = None
            context["missing_disks"] = None
            context["orphan_disks"] = None

        context["disk_table"] = DiskListTable(
            self.get_table_data(ds, missing = context["missing_disks"]), 
            request=self.request,
            template="django_tables2/with_pagination.html"
        )

        context["filter_names"] = (
            ('vm', _("virtual machine")),
            ('template', _("template")),
            ('none', _("none")),
            ('missing', _("missing")),
        )
        return context

class DiskDetail(SuperuserRequiredMixin, UpdateView):
    model = Disk
    form_class = DiskForm
    template_name = "dashboard/storage/disk.html"

    def form_valid(self, form):
        # Save only allowed edits (datastore) and redirect back.
        self.object = form.save()
        messages.success(self.request, _("Disk updated."))
        return redirect(self.request.path)


#class DiskDetail(SuperuserRequiredMixin, UpdateView):
#    model = Disk
#    form_class = DiskForm
#    template_name = "dashboard/storage/disk.html"
#
#    def form_valid(self, form):
#        pass
#
