import os
import re

import subprocess
from Queue import Queue, Empty
from threading import Thread
import uuid
import os
import shutil
import sys
from celery import Celery
from celery.contrib import rdb

from ovirt_imageio_common import directio
from kombu import Queue as kqueue
import json
import random
import logging
import logging.config

log = logging.getLogger("server")


app = Celery('celery_tasks', backend='redis', broker='redis://localhost:6379/0')
'''app.conf.task_queues = (
    kqueue('backup_tasks'),
    kqueue('restore_tasks'),
)'''
#rdb.set_trace()

def is_blk_device(dev):
    try:
        if stat.S_ISBLK(os.stat(dev).st_mode):
            return True
        return False
    except Exception:
        print ('Path %s not found in is_blk_device check', dev)
        return False


def check_for_odirect_support(src, dest, flag='oflag=direct'):

    # Check whether O_DIRECT is supported
    try:
        nova_utils.execute('dd', 'count=0', 'if=%s' % src, 'of=%s' % dest,
                           flag, run_as_root=True)
        return True
    except processutils.ProcessExecutionError:
        return False


def enqueue_output(out, queue):
    line = out.read(17)
    while line:
        line = out.read(17)
        queue.put(line)
    out.close()

@app.task(bind=True, name="ovirt_imageio_daemon.celery_tasks.backup")
def backup(self, ticket_id, path, dest, size, type, buffer_size, recent_snap_id):

    if type == "full":
        cmdspec = [
            'qemu-img',
            'convert',
            '-p',
        ]
        cmdspec += ['-O', 'qcow2', path, dest]
        cmd = " ".join(cmdspec)
        print('Take a full snapshot with qemu-img convert cmd: %s ' % cmd)
        process = subprocess.Popen(cmdspec,
                                   stdin=subprocess.PIPE,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE,
                                   bufsize=-1,
                                   close_fds=True,
                                   shell=False)

        queue = Queue()
        read_thread = Thread(target=enqueue_output,
                             args=(process.stdout, queue))

        read_thread.daemon = True  # thread dies with the program
        read_thread.start()

        percentage = 0.0
        while process.poll() is None:
            try:
                try:
                    output = queue.get(timeout=300)
                except Empty:
                    continue
                except Exception as ex:
                    print(ex)

                percentage = re.search(r'\d+\.\d+', output).group(0)

                print(("copying from %(path)s to "
                           "%(dest)s %(percentage)s %% completed\n") %
                          {'path': path,
                           'dest': dest,
                           'percentage': str(percentage)})

                percentage = float(percentage)

                self.update_state(state='PENDING',
                                  meta={'percentage': percentage})

            except Exception as ex:
                pass

        '''qemu_cmd = ["qemu-img", "info", "--output", "json", dest]
        temp_process = subprocess.Popen(qemu_cmd, stdout=subprocess.PIPE)
        data, err = temp_process.communicate()
        data = json.loads(data)
        size = data["actual-size"]
        process.stdin.close()
        self.update_state(state='PENDING',
                          meta={'actual-size': size})'''

        _returncode = process.returncode  # pylint: disable=E1101
        if _returncode:
            print(('Result was %s' % _returncode))
            raise Exception("Execution error %(exit_code)d (%(stderr)s). "
                            "cmd %(cmd)s" %
                            {'exit_code': _returncode,
                             'stderr': process.stderr.read(),
                             'cmd': cmd})
    else:
        process = subprocess.Popen('qemu-img info --backing-chain --output json ' + path, stdout=subprocess.PIPE, shell=True)
        stdout, stderr = process.communicate()
        if stderr:
            print(('Result was %s' % stderr))
            raise Exception("Execution error %(exit_code)d (%(stderr)s). "
                            "cmd %(cmd)s" %
                            {'exit_code': 1,
                             'stderr': stderr,
                             'cmd': 'qemu-img info --backing-chain --output json ' + path})

        result = json.loads(stdout)

        first_record = result[0]
        first_record_backing_file = first_record.get('backing-filename', None)
        recent_snap_path = recent_snap_id.get(str(first_record_backing_file), None)
        if first_record_backing_file and recent_snap_path:
            op = directio.Send(path,
                               None,
                               size,
                               buffersize=buffer_size)
            total = 0
            print('Executing task id {0.id}, args: {0.args!r} kwargs: {0.kwargs!r}'.format(
                self.request))
            gigs = 0
            with open(dest, "w+") as f:
                for data in op:
                    total += len(data)
                    f.write(data)
                    if total/1024/1024/1024 > gigs:
                        gigs = total/1024/1024/1024
                        percentage = (total/size) * 100
                        self.update_state(state='PENDING',
                                          meta={'percentage': percentage})
            process = subprocess.Popen('qemu-img rebase -u -b ' + recent_snap_path + ' ' + dest, stdout=subprocess.PIPE, shell=True)
            stdout, stderr = process.communicate()
            if stderr:
                log.error("Unable to change the backing file", dest, stderr)
        else:

            temp_random_id = generate_random_string(5)
            tempdir = '/var/triliovault-mounts/staging/' + temp_random_id
            os.makedirs(tempdir)
            commands = []
            for record in result:
                filename = os.path.basename(str(record.get('filename', None)))
                recent_snap_path = recent_snap_id.get(str(record.get('backing-filename')), None)
                if record.get('backing-filename', None) and str(record.get('backing-filename', None)) and not recent_snap_path:
                    try:
                        shutil.copy(path, tempdir)
                        backing_file = os.path.basename(str(record.get('backing-filename', None)))

                        command = 'qemu-img rebase -u -b ' + backing_file + ' ' + filename
                        commands.append(command)
                    except IOError as e:
                        print("Unable to copy file. %s" % e)
                    except:
                        print("Unexpected error:", sys.exc_info())
                else:
                    try:
                        shutil.copy(path, tempdir)
                        command = 'qemu-img rebase -u ' + filename
                        commands.append(command)
                    except IOError as e:
                        print("Unable to copy file. %s" % e)
                    except:
                        print("Unexpected error:", sys.exc_info())
                    break
                path = str(record.get('full-backing-filename'))
            string_commands = ";".join(str(x) for x in commands)
            process = subprocess.Popen(string_commands, stdin=subprocess.PIPE, stdout=subprocess.PIPE
                                       , cwd=tempdir, shell=True)
            stdout, stderr = process.communicate()
            if stderr:
                raise Exception(stdout)
            cmdspec = [
                'qemu-img',
                'convert',
                '-p',
            ]
            filename = os.path.basename(str(first_record.get('filename', None)))
            path = os.path.join(tempdir, filename)
            cmdspec += ['-O', 'qcow2', path, dest]
            process = subprocess.Popen(cmdspec,
                                       stdin=subprocess.PIPE,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.PIPE,
                                       bufsize=-1,
                                       close_fds=True,
                                       shell=False)

            queue = Queue()
            read_thread = Thread(target=enqueue_output,
                                 args=(process.stdout, queue))

            read_thread.daemon = True  # thread dies with the program
            read_thread.start()

            percentage = 0.0
            while process.poll() is None:
                try:
                    try:
                        output = queue.get(timeout=300)
                    except Empty:
                        continue
                    except Exception as ex:
                        print(ex)

                    percentage = re.search(r'\d+\.\d+', output).group(0)

                    print(("copying from %(path)s to "
                           "%(dest)s %(percentage)s %% completed\n") %
                          {'path': path,
                           'dest': dest,
                           'percentage': str(percentage)})

                    percentage = float(percentage)

                    self.update_state(state='PENDING',
                                      meta={'percentage': percentage})

                except Exception as ex:
                    pass
            if recent_snap_path:
                process = subprocess.Popen('qemu-img rebase -u -b ' + recent_snap_path + ' ' + dest, stdout=subprocess.PIPE, shell=True)
                stdout, stderr = process.communicate()
                if stderr:
                    log.error("Unable to change the backing file", dest, stderr)
            del_command = 'rm -rf ' + tempdir
            delete_process = subprocess.Popen(del_command, shell=True, stdout=subprocess.PIPE)
            delete_process.communicate()


@app.task(bind=True, name="ovirt_imageio_daemon.celery_tasks.restore")
def restore(self, ticket_id, volume_path, backup_image_file_path, size, buffer_size):

    def transfer_qemu_image_to_volume(
            volume_path,
            backup_image_file_path):

        cmdspec = [
            'qemu-img',
            'convert',
            '-p',
        ]

        if is_blk_device(volume_path) and \
            check_for_odirect_support(backup_image_file_path,
                                      volume_path, flag='oflag=direct'):
            cmdspec += ['-t', 'none']

        cmdspec += ['-O', 'qcow2', backup_image_file_path, volume_path]

        default_cache = True
        if default_cache is True:
            if '-t' in cmdspec:
                cmdspec.remove('-t')
            if 'none' in cmdspec:
                cmdspec.remove('none')
        cmd = " ".join(cmdspec)
        print('transfer_qemu_image_to_volume cmd %s ' % cmd)
        process = subprocess.Popen(cmdspec,
                                   stdin=subprocess.PIPE,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE,
                                   bufsize=-1,
                                   close_fds=True,
                                   shell=False)

        queue = Queue()
        read_thread = Thread(target=enqueue_output,
                             args=(process.stdout, queue))

        read_thread.daemon = True  # thread dies with the program
        read_thread.start()

        percentage = 0.0
        while process.poll() is None:
            try:
                try:
                    output = queue.get(timeout=300)
                except Empty:
                    continue
                except Exception as ex:
                    print(ex)

                percentage = re.search(r'\d+\.\d+', output).group(0)

                print(("copying from %(backup_path)s to "
                           "%(volume_path)s %(percentage)s %% completed\n") %
                          {'backup_path': backup_image_file_path,
                           'volume_path': volume_path,
                           'percentage': str(percentage)})

                percentage = float(percentage)

                self.update_state(state='PENDING',
                                  meta={'percentage': percentage})

            except Exception as ex:
                pass

        process.stdin.close()

        _returncode = process.returncode  # pylint: disable=E1101
        if _returncode:
            print(('Result was %s' % _returncode))
            raise Exception("Execution error %(exit_code)d (%(stderr)s). "
                            "cmd %(cmd)s" %
                            {'exit_code': _returncode,
                             'stderr': process.stderr.read(),
                             'cmd': cmd})

    transfer_qemu_image_to_volume(volume_path, backup_image_file_path)

def generate_random_string(string_length=5):
    """Returns a random string of length string_length."""

    random = str(uuid.uuid4())
    random = random.upper()
    random = random.replace("-","")
    return random[0:string_length]
