#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import socket
import struct
import sys

# see com.intellij.idea.SocketLock for the server side of this interface

RUN_PATH = u'/opt/pycharm-professional/bin/pycharm.sh'
CONFIG_PATH = u'~/.PyCharm2019.2/config'
SYSTEM_PATH = u'~/.PyCharm2019.2/system'


def print_usage(cmd):
    print(('Usage:\n' +
           '  {0} -h | -? | --help\n' +
           '  {0} [project_dir] [-w|--wait]\n' +
           '  {0} [-l|--line line] [project_dir|--temp-project] [-w|--wait] file[:line]\n' +
           '  {0} diff <left> <right>\n' +
           '  {0} merge <local> <remote> [base] <merged>').format(cmd))


def write_to_sock(sock, str):
    if sys.version_info[0] >= 3: str = str.encode('utf-8')
    sock.send(struct.pack('>h', len(str)) + str)


def read_from_sock(sock):
    len = struct.unpack('>h', sock.recv(2))[0]
    return sock.recv(len).decode('utf-8')


def read_sequence_from_sock(sock):
    result = []
    while True:
        try:
            str = read_from_sock(sock)
            if str == '---':
                break
            result.append(str)

        except (socket.error, IOError) as e:
            print("I/O error({0}): {1} ({2})".format(e.errno, e.strerror, e))
            traceback.print_exception(*sys.exc_info())
            return result
    return result


def process_args(argv):
    args = []

    skip_next = False
    for i, arg in enumerate(argv[1:]):
        if arg == '-h' or arg == '-?' or arg == '--help':
            print_usage(argv[0])
            exit(0)
        elif i == 0 and (arg == 'diff' or arg == 'merge' or arg == '--temp-project'):
            args.append(arg)
        elif arg == '-l' or arg == '--line':
            args.append(arg)
            skip_next = True
        elif arg == '-w' or arg == '--wait':
            args.append('--wait')
        elif skip_next:
            args.append(arg)
            skip_next = False
        else:
            path = arg
            if ':' in arg:
                file_path, line_number = arg.rsplit(':', 1)
                if line_number.isdigit():
                    args.append('-l')
                    args.append(line_number)
                    path = file_path
            args.append(os.path.abspath(path))

    return args


def try_activate_instance(args):
    port_path = os.path.join(CONFIG_PATH, 'port')
    token_path = os.path.join(SYSTEM_PATH, 'token')
    if not (os.path.exists(port_path) and os.path.exists(token_path)):
        return False

    try:
        with open(port_path) as pf:
            port = int(pf.read())
        with open(token_path) as tf:
            token = tf.read()
    except (ValueError):
        return False

    s = socket.socket()
    s.settimeout(1.0)
    try:
        s.connect(('127.0.0.1', port))
    except (socket.error, IOError):
        return False

    paths = read_sequence_from_sock(s)
    found = CONFIG_PATH in paths

    if found:
        write_to_sock(s, 'activate ' + token + '\0' + os.getcwd() + '\0' + '\0'.join(args))

        s.settimeout(None)
        response = read_sequence_from_sock(s)
        if response[0] != 'ok':
            print('bad response: ' + response)
            exit(1)

        if len(response) > 2:
            print(response[2])

        exit(int(response[1]))

    return False


def start_new_instance(args):
    if sys.platform == 'darwin':
        if len(args) > 0:
            args.insert(0, '--args')
        os.execvp('/usr/bin/open', ['-a', RUN_PATH] + args)
    else:
        bin_file = os.path.split(RUN_PATH)[1]
        os.execv(RUN_PATH, [bin_file] + args)


ide_args = process_args(sys.argv)
if not try_activate_instance(ide_args):
    start_new_instance(ide_args)

