#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function
from __future__ import division

import os
import re
import sys
import json
import threading

def msg(message, *args, **kwargs):
    print(message.format(*args, **kwargs), file=sys.stdout)

def set_msg(m):
    global msg
    msg = m

MAIN_LOCK = threading.RLock()

DIR_ENV = "env"
DIR_SERVER = "server"
DIR_PROJECT = "project"
EXT = ".json"

DEFAULT_REGEX = "(.*)"
DEFAULT_LINE = 0

def _get_config_list(config):
    if not os.path.exists(config):
        os.makedirs(config)
    return [ c[:-len(EXT)] for c in os.listdir(config) if c.endswith(EXT) ]

def get_envs():
    return _get_config_list(DIR_ENV)

def get_servers():
    return _get_config_list(DIR_SERVER)

def get_projects():
    return _get_config_list(DIR_PROJECT)

def _get_config_path(c, config):
    return "{0}{1}".format(os.path.join(config, c), EXT)

def _read_config(f_in, config):
    if not os.path.exists(config):
        os.makedirs(config)
    with open(_get_config_path(f_in, config), 'rb') as f:
        return json.load(f)

def _write_config(f_out, config, obj):
    if not os.path.exists(config):
        os.makedirs(config)
    with open(_get_config_path(f_out, config), 'wb') as f:
        json.write(obj)

def _read_env(f_in):
    es = _read_config(f_in, DIR_ENV)

    def get(field):
        res = []
        if field in es:
            for e in es[field]:
                name = e["name"]
                cmd = e["cmd"]
                regex = re.compile(e.get("regex", DEFAULT_REGEX))
                line = int(e.get("line", DEFAULT_LINE))
                res.append((name, cmd, regex, line))
        return res

    return {
        "versions": get("versions"),
        "cpus": get("cpus"),
    }

def _write_env(f_out, env):
    obj = {}

    def conv(e):
        name, cmd, regex, line = e
        res = {
            "name": name,
            "cmd": cmd,
        }
        if regex != DEFAULT_REGEX:
            res["regex"] = regex
        if line != DEFAULT_LINE:
            res["line"] = line
        return res

    for (k, es) in env.items():
        obj[k] = [ conv(e) for e in es ]
    _write_config(f_out, DIR_ENV, obj)

def _read_server(f_in):
    res = _read_config(f_in, DIR_SERVER)
    if "password" in res:
        raise ValueError("password should not be stored in config! {0}".format(f_in))
    return res

def _write_server(f_out, server):
    obj = {}
    for (k, v) in server.items():
        if k == "password":
            continue
        obj[k] = v
    _write_config(f_out, DIR_SERVER, obj)

ALL_SERVERS = {}
def get_server(s):
    with MAIN_LOCK:
        if s not in ALL_SERVERS:
            ALL_SERVERS[s] = _read_server(s)
        return ALL_SERVERS[s]

def read_project(f_in):
    pr = _read_config(f_in, DIR_PROJECT)
    path_local = pr["local"]
    if not os.path.exists(path_local):
        os.makedirs(path_local)
    command = pr["cmd"]
    env = (pr["env"], _read_env(pr["env"]))
    servers = pr["servers"]
    s_conn = dict([ (s, get_server(s)) for s in servers ])
    return (path_local, command, env, servers, s_conn)

def _write_project(f_out, path_local, command, env, servers, s_conn):
    e_name, e_obj = env
    _write_env(e_name, e_obj)

    def get_server_name(s_name):
        _write_server(s_name, s_conn[s_name])
        return s_name

    obj = {
        "local": path_local,
        "cmd": command,
        "env": e_name,
        "servers": [ get_server_name(s) for s in servers ],
    }

@@ -460,16 +460,75 @@
 NE = 0%0A%0A
+UPGRADE_ENV = %5B%5D%0AUPGRADE_SERVER = %5B%5D%0AUPGRADE_PROJECT = %5B%5D%0A%0A
 def _get
@@ -981,24 +981,33 @@
 f_in, config
+, upgrade
 ):%0A    if no
@@ -1128,20 +1128,19 @@
       re
-turn
+s =
  json.lo
@@ -1144,16 +1144,128 @@
 .load(f)
+%0A    res, chg = _check_version(res, upgrade)%0A    if chg:%0A        _write_config(f_in, config, res)%0A    return res
 %0A%0Adef _w
@@ -1440,24 +1440,249 @@
 write(obj)%0A%0A
+def _check_version(obj, upgrade):%0A    v = int(obj.get(%22version%22, 0))%0A    chg = False%0A    while v %3C len(upgrade):%0A        obj = upgrade%5Bv%5D(obj)%0A        v += 1%0A        obj%5B%22version%22%5D = v%0A        chg = True%0A    return obj, chg%0A%0A
 def _read_en
@@ -1725,16 +1725,29 @@
  DIR_ENV
+, UPGRADE_ENV
 )%0A%0A    d
@@ -2677,16 +2677,32 @@
 R_SERVER
+, UPGRADE_SERVER
 )%0A    if
@@ -3243,16 +3243,33 @@
 _PROJECT
+, UPGRADE_PROJECT
 )%0A    pa

introduce upgrade mechanism