#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
The MIT License (MIT)

Copyright (c) 2021 David Parsons

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the 'Software'), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""

import json
import os
import shutil
import subprocess
import sys
import time

from configobj import ConfigObj

from bottle import run, get, delete, post, patch, request, response, static_file, error
from pyvmxdict import VMDict

__author__ = 'David Parsons'
__version__ = '1.0.0'
__license__ = 'MIT'

VIX_SHAREDFOLDER_WRITE_ACCESS = 4

platform = ''
names = {}
vms = {}

DEFAULT_VM_PATH = ''
DEFAULT_PARENT_VM_PATH = ''
DEFAULT_LOG_PATH = ''
PORT = ''
VMRUN = ''
VMTYPE = ''

if sys.version_info < (3, 7):
    sys.stderr.write('You need Python 3.7 or later\n')
    sys.exit(1)


def log_date_time_string():
    """Return the current time formatted for logging."""
    now = time.time()
    year, month, day, hh, mm, ss, x, y, z = time.localtime(now)
    s = "%02d/%3s/%04d %02d:%02d:%02d" % (
        day, monthname[month], year, hh, mm, ss)
    return s


weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']

monthname = [None,
             'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
             'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']


def log_message(msgfmt, *args):
    sys.stderr.write("%s - - [%s] %s\n" %
                     ('127.0.0.1',
                      log_date_time_string(),
                      msgfmt % args))


def getstringpath():
    return os.path.dirname(os.path.realpath(sys.argv[0]))


def isfile(filename):
    return os.path.isfile(filename)


def isfolder(foldername):
    return os.path.isdir(foldername)


def joinpath(folder, filename):
    return os.path.join(folder, filename)


def runcmd(cmd, strip=True):
    # vmrun does not return any exit codes and all errors are in stdout!
    command = VMRUN + VMTYPE + cmd
    log_message('%s %s', 'VMRUN Command: ', command)
    proc = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
    stdout = proc.stdout.readlines()

    if any('Error'.encode() in s for s in stdout):
        code = 500
    else:
        code = 200

    if len(stdout) > 0:
        output = ''.join(str(l) for l in stdout)
    else:
        output = ''

        log_message('%s %s', 'VMRUN Output: ', output)

    if strip:
        output = output.replace('\n', '').replace('\r', '')
    return code, output


def symlink(src, dest):
    command = 'mklink /d "' + dest + '" "' + src + '"'
    log_message('%s %s', 'Windows creating symlink: ', command)
    proc = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
    stdout = proc.stdout.readlines()
    log_message('%s %s', 'Windows creating symlink: ', stdout)


@get('/api')
def api():
    # TODO: Return configuration via JSON
    # Return the API version

    return __version__


@get('/api/config')
def api():
    # TODO: Return configuration via HTML template
    # Return the config details (appcatalyst.conf)
    # DEFAULT_VM_PATH
    # DEFAULT_PARENT_VM_PATH
    # DEFAULT_LOG_PATH
    # PORT
    # VMRUN
    # VMTYPE
    return


@get('/api/vms')
def get_vms():
    # Return the names dict keys in non-JSON text form:
    # ["photon1", "photons2", ...]
    # Ignore the "tags" parameters as not used in AppCatalyst previews
    output = ''
    for key in names.keys():
        output = output + '"' + key + '",'
    output = '[' + output[:-1] + ']'
    response.body = output
    response.content_type = 'text/plain; charset=utf-8'
    response.status = 200
    return response


@post('/api/vms')
def post_vms():
    # Create a new cloned VM from default or specified source
    body = json.load(request.body)
    destvmx = ''
    output = ''

    if 'id' in body:
        if 'sourceReference' in body:
            destvmx = joinpath(joinpath(DEFAULT_VM_PATH, body['id']), os.path.basename(body['sourceReference']))
        else:
            destvmx = joinpath(joinpath(DEFAULT_VM_PATH, body['id']), body['id'] + '.vmx')
    else:
        response.body = '{"code": 500, "message": "EOF"}'
        response.body = 500

    if 'parentId' in body:
        srcvmx = joinpath(joinpath(DEFAULT_VM_PATH, body['parentId']), body['parentId'] + '.vmx')
        code, output = runcmd('clone ' + srcvmx + ' ' + destvmx + ' full')
    elif 'sourceReference' in body:
        srcvmx = body['sourceReference']
        try:
            srcdir = os.path.dirname(srcvmx)
            destdir = joinpath(DEFAULT_VM_PATH, body['id'])
            if platform == 'windows':
                symlink(srcdir, destdir)
            else:
                os.makedirs(destdir)
                os.symlink(srcvmx, destvmx)
            code = 200
        except OSError:
            code = 500
            output = "Failed to create VMX symlink"
    else:
        srcvmx = DEFAULT_PARENT_VM_PATH
        code, output = runcmd('clone ' + srcvmx + ' ' + destvmx + ' full')

    # Check response code from the vmrun procedure & return to caller
    if code == 200:
        if 'parentId' in body:
            response.body = '{"id": "' + body['id'] + \
                            '", "parentId": "' + body['parentId'] + '", "name": "", "tag": "", "sourceReference": ""}'
        elif 'sourceReference' in body:
            response.body = '{"id": "' + body['id'] + \
                            '", "parentId": "", "name": "", "tag": "", "sourceReference": "' + \
                            body['sourceReference'] + '"}'
        else:
            response.body = '{"id": "' + body['id'] + \
                            '", "parentId": "", "name": "", "tag": "", "sourceReference": ""}'

        names[body['id']] = destvmx
        vmx = ConfigObj(destvmx)
        vms[body['id']] = vmx
        with open('vmInventory', 'w') as f:
            json.dump(names, f)
        response.status = code
    elif code == 500:
        response.body = '{"code": 500, "message": "' + output + '"}'
        response.status = code
    else:
        pass

    response.content_type = 'application/json; charset=utf-8'
    return response


@get('/api/vms/<vmid>')
def get_vms_id(vmid):
    # Check id is valid
    output = ''
    if vms:
        for key, value in vms.items():
            if vmid == key:
                output = '{"id": "' + key + '", "name": "' + vms[key]['displayname'] + '", "tag": ""}'
            response.status = 200
    else:
        output = '{}'
        response.status = 500

    response.body = output
    response.content_type = 'application/json; charset=utf-8'
    return response


@delete('/api/vms/<vmid>')
def del_vms_id(vmid):
    # Delete the VM from inventory
    # code, output = runcmd('stop ' + names[vmid] + ' hard')
    code, output = runcmd('deleteVM ' + names[vmid])
    try:
        shutil.rmtree(joinpath(DEFAULT_VM_PATH, vmid), True)
    except OSError:
        code = 500
        output = 'Failed to delete VMX folder'
    if code == 200:
        response.status = 204
    elif code == 500:
        response.body = '{"code": 500, "message": "' + output + '"}'
        response.status = code
    else:

        pass

    # Remove the relevant dict entries
    if vmid in names:
        del names[vmid]
        del vms[vmid]

    with open('vmInventory', 'w') as f:
        json.dum
p(names, 