# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
# Copyright 2012-2014 Canonical, Ltd.
# Author: Thomi Richards
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.


import json
import logging
import os
import subprocess
import tempfile
from threading import Thread


from windowmocker import plugins

__all__ = (
    'create_application_from_data',
    'create_application_from_file',
    'create_application_from_path',
    'launch_window_mocker_with_data',
    )


logger = logging.getLogger(__name__)


def create_application_from_path(path, app_type_name=None):
    """Create and return an Application instance, reading settings from path.

    app_type_name is the plugin name for the type of application you want to
    create. If set to None (the default), the default plugin will be used.

    This function raises an IOError if the specified path does not exist, or is
    not readable.

    """
    with open(path, 'r') as fp:
        return create_application_from_file(fp, app_type_name)


def create_application_from_file(file_object, app_type_name=None):
    """Create and return an Application instance, reading settings from the
    file-like object 'file_object'.

    app_type_name is the plugin name for the type of application you want to
    create. If set to None (the default), the default plugin will be used.

    This function raises AttributeError if the object does not have a 'read()'
    method.

    """
    try:
        data = json.load(file_object)
    except ValueError as e:
        raise RuntimeError(str(e))
    return create_application_from_data(data, app_type_name)


def create_application_from_data(data=None, app_type_name=None):
    """Create an application object from 'data'.

    If data is None (the default), a default application is constructed.

    app_type_name is the plugin name for the type of application you want to
    create. If set to None (the default), the default plugin will be used.

    This function raises RuntimeErrors if the data does not conform to the
    expected format.

    """
    if data is None:
        data = {}
    if type(data) not in (dict, list):
        raise RuntimeError(
            "Top-level data structure must be a dictionary or list, not %r" %
            (type(data).__name__))

    if app_type_name is None:
        app_type_name = plugins.get_default_plugin_name()

    app_plugin = plugins.get_plugin_by_name(app_type_name)
    if app_plugin is None:
        raise RuntimeError("Type name '%s' is invalid" % app_type_name)

    app_instance = app_plugin(data)
    app_instance.run()


def launch_window_mocker_with_data(data=None):
    """Run window-mocker with the contents of 'data' as the window spec file.

    If data is None (the default), a default window will be created. This
    function returns a threading.Thread object that has been started. While the
    window is active, the thread will remain running.

    This function writes 'data' to a temporary file on disk, and calls
    window-mocker on that spec file. The file is cleaned up afterwards.

    window-mocker must be in $PATH.

    """
    if data is None:
        data = {}

    if type(data) not in (dict, list):
        raise RuntimeError(
            "Top-level data structure must be a dictionary or list, not %r" %
            (type(data).__name__))

    def run_thread():
        fd, temp_filename = tempfile.mkstemp()
        os.close(fd)
        try:
            with open(temp_filename, 'w') as f:
                json.dump(data, f)
            subprocess.check_call(["window-mocker", temp_filename])
        finally:
            os.remove(temp_filename)

    thread = Thread(target=run_thread)
    thread.start()
    return thread
