# -*- coding: utf-8 -*-
#
# This file is part of Karesansui.
#
# Copyright (C) 2009 HDE, Inc.
#
# This program 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 2
# of the License, or (at your option) any later version.
#

import os.path

import web
import simplejson as json

import karesansui
from karesansui.gadget.guest import regist_guest
from karesansui.lib.rest import Rest, auth
from karesansui.lib.const import \
    VIRT_COMMAND_REPLICATE_GUEST, \
    VIRT_DISK_IMAGE_DIR, VIRT_LIBVIRT_DATA_DIR, \
    VNC_PORT_MIN_NUMBER, PORT_MAX_NUMBER, \
    ID_MIN_LENGTH, ID_MAX_LENGTH
from karesansui.lib.utils import comma_split, generate_uuid, \
    string_from_uuid, is_param, uni_force, \
    next_number, generate_mac_address, chk_create_disk
from karesansui.db.model.machine import ATTRIBUTE
from karesansui.db.access.machine import findbyguest1, new as m_new
from karesansui.db.access.tag import new as t_new
from karesansui.db.access.notebook import new as n_new
from karesansui.lib.virt.virt import KaresansuiVirtConnection

from karesansui.lib.checker import Checker, \
    CHECK_EMPTY, CHECK_VALID, CHECK_LENGTH, CHECK_ONLYSPACE, \
    CHECK_MIN, CHECK_MAX

from karesansui.lib.const import \
    NOTE_TITLE_MIN_LENGTH, NOTE_TITLE_MAX_LENGTH, \
    MACHINE_NAME_MIN_LENGTH, MACHINE_NAME_MAX_LENGTH, \
    TAG_MIN_LENGTH, TAG_MAX_LENGTH, \
    VNC_PORT_MIN_NUMBER, VNC_PORT_MAX_NUMBER, \
    DOMAIN_NAME_MIN_LENGTH, DOMAIN_NAME_MAX_LENGTH


def validates_guest_replicate(obj):
    checker = Checker()
    check = True

    _ = obj._
    checker.errors = []

    if not is_param(obj.input, 'm_name'):
        check = False
        checker.add_error(_('Parameter m_name does not exist.'))
    else:
        check = checker.check_string(
                    _('Machine Name'),
                    obj.input.m_name,
                    CHECK_EMPTY | CHECK_LENGTH | CHECK_ONLYSPACE,
                    None,
                    min = MACHINE_NAME_MIN_LENGTH,
                    max = MACHINE_NAME_MAX_LENGTH,
            ) and check

    if is_param(obj.input, 'note_title'):
        check = checker.check_string(
                    _('Title'),
                    obj.input.note_title,
                    CHECK_LENGTH | CHECK_ONLYSPACE,
                    None,
                    min = NOTE_TITLE_MIN_LENGTH,
                    max = NOTE_TITLE_MAX_LENGTH,
                ) and check

    if is_param(obj.input, 'note_value'):
        check = checker.check_string(
                    _('Note'),
                    obj.input.note_value,
                    CHECK_ONLYSPACE,
                    None,
                    None,
                    None,
                ) and check

    if is_param(obj.input, 'tags'):
        for tag in comma_split(obj.input.tags):
            check = checker.check_string(
                        _('Tag'),
                        tag,
                        CHECK_LENGTH | CHECK_ONLYSPACE,
                        None,
                        min = TAG_MIN_LENGTH,
                        max = TAG_MAX_LENGTH,
                    ) and check

    if not is_param(obj.input, 'domain_dest_name'):
        check = False
        checker.add_error(_('Parameter domain_dest_name does not exist.'))
    else:
        check = checker.check_string(
                _('Destination Domain Name'),
                obj.input.domain_dest_name,
                CHECK_EMPTY | CHECK_VALID | CHECK_LENGTH,
                '[^-a-zA-Z0-9_\.]+',
                DOMAIN_NAME_MIN_LENGTH,
                DOMAIN_NAME_MAX_LENGTH,
            ) and check

    if not is_param(obj.input, 'xen_vncport'):
        check = False
        checker.add_error(_('Parameter xen_vncport does not exist.'))
    else:
        check = checker.check_number(
                _('VNC Port Number'),
                obj.input.xen_vncport,
                CHECK_EMPTY | CHECK_VALID | CHECK_MIN | CHECK_MAX,
                VNC_PORT_MIN_NUMBER,
                VNC_PORT_MAX_NUMBER,
            ) and check

    if not is_param(obj.input, 'xen_mac'):
        check = False
        checker.add_error(_('Parameter xen_mac does not exist.'))
    else:
        check = checker.check_macaddr(
                _('MAC Address'),
                obj.input.xen_mac,
                CHECK_EMPTY | CHECK_VALID,
            ) and check

    obj.view.alert = checker.errors
    return check


def validates_src_id(obj):
    """<comment-ja>
    ゲストOSコピー元のチェッカー
    
    @param obj: karesansui.lib.rest.Rest オブジェクト
    @type obj: karesansui.lib.rest.Rest
    @return: check
    @rtype: bool
    </comment-ja>
    <comment-en>
    TODO: English Comment
    </comment-en>
    """
    checker = Checker()
    check = True

    _ = obj._
    checker.errors = []

    if not is_param(obj.input, 'src_id'):
        check = False
        checker.add_error(_('"%s" is required.') % _('Copy Source'))
    else:
        check = checker.check_number(_('Copy Source'),
                                     obj.input.src_id,
                                     CHECK_EMPTY | CHECK_VALID | CHECK_MIN | CHECK_MAX,
                                     ID_MIN_LENGTH,
                                     ID_MAX_LENGTH
                                     ) and check
        
        obj.view.alert = checker.errors
    return check

class GuestReplicate(Rest):

    @auth
    def _GET(self, *param, **params):
        host_id = self.chk_hostby1(param)
        if host_id is None: web.notfound()

        if not validates_src_id(self):
            return web.badrequest(self.view.alert)

        src_id = self.input.src_id
        if self.is_mode_input() is False:
            return web.nomethod()

        self.view.src_id = src_id
        self.view.mac_address = generate_mac_address()

        src_guest = findbyguest1(self.orm, src_id)
        if not src_guest:
            return web.badrequest()

        kvc = KaresansuiVirtConnection()
        try:
            domname = kvc.uuid_to_domname(src_guest.uniq_key)
            #if not domname: return web.conflict(web.ctx.path)
            virt = kvc.search_kvg_guests(domname)[0]
            self.view.domain_src_name = virt.get_domain_name()
            used_ports = kvc.list_used_vnc_port()
            self.view.vnc_port = next_number(VNC_PORT_MIN_NUMBER,PORT_MAX_NUMBER,used_ports)
        finally:
            kvc.close()

        return True


    @auth
    def _POST(self, *param, **params):
        host_id = self.chk_hostby1(param)
        if host_id is None: web.notfound()
        
        if not validates_guest_replicate(self):
            return web.badrequest(self.view.alert)
            
        uuid = string_from_uuid(generate_uuid())

        if not validates_src_id(self):
            return web.badrequest(self.view.alert)
        src_guest = findbyguest1(self.orm, self.input.src_id)

        if not src_guest:
            return web.badrequest()

    
        # Note
        note_title = None
        if is_param(self.input, "note_title"):
            note_title = self.input.note_title

        note_value = None
        if is_param(self.input, "note_value"):
            note_value = self.input.note_value

        _notebook = n_new(note_title, note_value)
        
        # Tag
        _tags = None
        if is_param(self.input, "tags"):
            _tags = []
            for x in comma_split(self.input.tags):
                _tags.append(t_new(x))
            
        # Icon
        icon_filename = None
        if is_param(self.input, "icon_filename", empty=True):
            icon_filename = self.input.icon_filename

        dest_guest = m_new(created_user=self.me,
                           modified_user=self.me,
                           uniq_key=uni_force(uuid),
                           name=self.input.m_name,
                           attribute=ATTRIBUTE['GUEST'],
                           hypervisor=src_guest.hypervisor,
                           notebook=_notebook,
                           tags=_tags,
                           icon=icon_filename,
                           is_deleted=False,
                           parent=src_guest.parent,
                           )


        kvc = KaresansuiVirtConnection()
        try:
            domname = kvc.uuid_to_domname(src_guest.uniq_key)
            if not domname: return web.conflict(web.ctx.path)
            virt = kvc.search_kvg_guests(domname)[0]
            options = {}
            options["src-name"] = virt.get_domain_name()
            if is_param(self.input, "domain_dest_name"):
                options["dest-name"] = self.input.domain_dest_name
            if is_param(self.input, "xen_vncport"):
                options["vnc-port"] = self.input.xen_vncport
            if is_param(self.input, "xen_mac"):
                options["mac"] = self.input.xen_mac

            options["uuid"] = uuid

            # disk check
            src_disk = VIRT_DISK_IMAGE_DIR + "/"+ options["src-name"] +".img"
            s_size = os.path.getsize(src_disk) / (1024 * 1024) # a unit 'MB'
            if chk_create_disk(VIRT_LIBVIRT_DATA_DIR, s_size) is False:
                return web.badrequest('Do not have enough free disk size.')

            active_guests = kvc.list_active_guest()
            inactive_guests = kvc.list_inactive_guest()
            used_vnc_ports = kvc.list_used_vnc_port()
            used_mac_addrs = kvc.list_used_mac_addr()

            conflict_location = "%s/host/%d/guest/%d.json" \
                                % (web.ctx.homepath, src_guest.parent_id, src_guest.id)
            # source guestos
            if not (options["src-name"] in active_guests or options["src-name"] in inactive_guests):
                return web.conflict(conflict_location, "Unable to get the source guest OS.")
            
            # Check on whether value has already been used
            # destination guestos
            if (options["dest-name"] in active_guests or options["dest-name"] in inactive_guests):
                return web.conflict(conflict_location, "Destination Guest OS is already there.")
            # VNC port number
            if(int(self.input.xen_vncport) in used_vnc_ports):
                return web.conflict(conflict_location, "VNC Port is already there.")
            # MAC address
            if(self.input.xen_mac in used_mac_addrs):
                return web.conflict(conflict_location, "MAC Address is already there.")
                    
        finally:
            kvc.close()
            
        ret = regist_guest(self,
                            dest_guest,
                            icon_filename,
                            VIRT_COMMAND_REPLICATE_GUEST,
                            options,
                            ['Replicate Guest', 'Replicate Guest'],
                            {"name" : options['dest-name']},
                            )

        if ret is True:
            return web.accepted()
        else:
            return False

urls = (
    '/host/(\d+)/guest/replicate/?$', GuestReplicate,
    )
