#!/usr/bin/env python3

# Copyright (c) 2014 Martin Mahner <martin@mahner.org>
#
# 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.

"""
tab - Opens a new OS X Terminal window in the current directory and runs
an optional command in it.

If the current directory contains a file `.tabfile` this one is executed
instead (see below).

Download the latest version from: https://github.com/bartTC/tab

Installation:
-------------

Install it with pip: `pip install tab-osx` or if you prefer the manual way,
copy the `tab` file into your `/usr/local/bin` and make it executable:

    cp tab /usr/local/bin
    chmod +x /usr/local/bin/tab

Options:
--------

    (no arguments) Opens a new Terminal tab and changes to the current
                   directory.
    -n: Window name of  the new tab
    -w: Open a new window instead of a tab
    -p: Position of the new window on screen in the format x,y (e.g 400,200)
    -s: Size of the new window in the format w,h (e.g. 400,200)
    -t: Path to a tabfile to execute.

Example:
--------

    tab                                # Opens a new tab
    tab vim                            # Opens vim in a new tab
    tab -w -s 100,100 -p 400,700 vim   # Opens vim in a new window at the
                                       # specified position and size
    tab -t ~/Projects/project.tabfile  # Open the tabfile

Tabfile:
--------

You can specify multiple `tab` actions in one file, to quickly open multiple
tabs with commands. Tabfile is an INI file, each tab is seperated by a group
called either `tab:<n>` or `window:<n>`.

You can specify options as 'name:', 'size:', and 'position:'. All other
lines are commands which will be executed.

    [tab:1]                                     # Gulp Watcher
    source ~/myproject/bin/activate
    gulp watch

    [tab:2]                                     # Django Project
    source ~/myproject/bin/activate
    manage.py runserver                         # Start the server

    [tab:3]
    open http://127.0.0.1:8000/                 # Open web browser for above server
    clear                                       # and have a blank tab to work in

    [window:1]                                  # Vim Editor in a separate
    size: 200,400                               # window.
    position: 600,300
    vim
"""

import os
import sys
import re
import configparser
from time import sleep
from subprocess import Popen, PIPE
from optparse import OptionParser, OptionGroup

version = "1.3"
wd = os.getcwd()

description = """tab - Opens a new OS X Terminal window with the current
directory and runs an optional command in it. If the
current directory contains a file `.tabfile` this one is
executed instead (see file for directions)."""

usage = "Usage: %prog [options] cmd1 cmd2"

# -----------------------------------------------------------------------------
# Options
# -----------------------------------------------------------------------------

parser = OptionParser(usage=usage, description=description, version="%prog " + version)
parser.add_option("-n", dest="name", help="Name of the new tab: e.g. \"Vim Editor\"")
parser.add_option("-t", dest="tabfile", help="Path to tabfile")

group = OptionGroup(parser, "Window mode")
group.add_option("-w", action="store_true", dest="window",
    help="Open a new window instead of a tab")
group.add_option("-p", dest="position",
    help="Position of the new window in the format 400,200")
group.add_option("-s", dest="size",
    help="Size of the new window in the format 800x,600")

parser.add_option_group(group)

(options, args) = parser.parse_args()

# -----------------------------------------------------------------------------

def osascript(scpt, args=[]):
     p = Popen(['osascript', '-'] + args, stdin=PIPE, stdout=PIPE, stderr=PIPE)
     stdout, stderr = p.communicate(scpt.encode('utf-8'))
     return stdout, stderr

def get_config_items(config, groupname):
    options = {}
    commands = []
    for n in config.items(groupname):
        if n[0] in ('name', 'size', 'position'):
            options[n[0]] = n[1]
        elif n[0] == None:
            continue
        else:
            commands.append(n[0])
    return options, commands

def opentab(window, scriptargs):
    if window:
        script = """
        tell application "Terminal"
            activate
            do script with command "cd {wd}"
            do script "clear" in window 1
            {name}
            {size}
            {position}
            {commands}
        end tell
        """
    else:
        script = """
        tell application "System Events"
            tell process "Terminal" to keystroke "t" using command down
        end
        tell application "Terminal"
            activate
            do script with command "cd {wd}" in window 1
            do script "clear" in window 1
            {name}
            {commands}
        end tell
        """
    stdout, stderr = osascript(script.format(**scriptargs))

    if stderr:
        sys.stderr.write('Error in Applescript: {}\n'.format(stderr))

# -----------------------------------------------------------------------------
# Tabfile
# -----------------------------------------------------------------------------

tabfile = None
local_tabfile = os.path.join(wd, '.tabfile')

if options.tabfile:
    if not os.path.isfile(options.tabfile):
        sys.stderr.write('Tabfile "{}" does not exist.\n'.format(options.tabfile))
        sys.exit()
    tabfile = options.tabfile
elif os.path.isfile(local_tabfile):
    tabfile = local_tabfile

if tabfile:
    scripts = []
    config = configparser.ConfigParser(delimiters=('=',), allow_no_value=True)
    try:
        config.read(tabfile)
    except ConfigParser.ParsingError as e:
        sys.stderr.write('Tabfile seem to be broken. Error was: {}\n'.format(e))
        sys.exit()

    sys.stdout.write('Found tabfile in {}n'.format(tabfile))

    for groupname in config.sections():
        options, cmds = get_config_items(config, groupname)
        scriptargs = {
            'name': options.get('name') and 'set custom title of window 1 to "{}"'.format(options['name']) or '',
            'position': options.get('position') and 'set position of window 1 to {{{}}}'.format(options['position']) or '',
            'size': options.get('size') and 'set size of window 1 to {{{}}}'.format(options['size']) or '',
            'wd': wd,
        }
        if cmds:
            scriptargs['commands'] = '\n'.join(['do script with command "{}" in window 1'.format(a) for a in cmds])

        window = groupname.startswith('window')
        sleep(1)  # Applescript gets irritated if you do too much at once
        opentab(window, scriptargs)

# -----------------------------------------------------------------------------
# Manual handling
# -----------------------------------------------------------------------------

else:
    scriptargs = {
        'name': options.name and 'set custom title of window 1 to "{}"'.format(options.name) or '',
        'position': options.position and 'set position of window 1 to {{{}}}'.format(options.position) or '',
        'size': options.size and 'set size of window 1 to {{{}}}'.format(options.size) or '',
        'wd': wd,
        'commands': '\n'.join(['do script with command "" in window 1'.format(a) for a in args])
    }
    opentab(options.window, scriptargs)
