#!/usr/bin/env python
#
# Copyright (C) 2016 Samsung
#
#    This program is free software; you can redistribute it and/or
#    modify it under the terms of the GNU General Public License
#    as published by the Free Software Foundation; version 2
#    of the License.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
"""Check syntax and semantics if they are in system."""

import os
import subprocess
import collections
import git_diff_parse

class Server:
    def __init__(self, port, account, url):
        self.port = port
        self.account = account
        self.url = url

class SyntaxError(Exception):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return self.value

def get_diff_info(dic):
        obj_list = []

        git_diff_parse.git_diff_parse(obj_list)
        for i in range(len(obj_list)):
                for j in range(len(meta_file_list)):
                        if obj_list[i][1] == meta_file_list[j]:
                                dic[meta_file_list[j]].append(obj_list[i][:])
                                break

def get_key_list(dic, readme_file):
    with open('%s' % readme_file, 'r') as lines:
        read_status = False
        cur_file = ''
        for line in lines:
            if line.split(':')[0] == 'Filename':
                cur_file = line.split(':')[1].split('"')[1]
                if cur_file in meta_file_list:
                    read_status = True
                    continue
            if read_status:
                if line.strip() == '':
                    read_status = False
                else:
                    temp = line.strip().split(' ')
                    if temp[len(temp) - 2] + ' ' + temp[len(temp) - 1] == 'Read Only':
                        attr = 1
                    else:
                        attr = 0
                    dic[cur_file][line.strip()[0]] = attr

def init_info(file_list, readme_file, sdir, server):
    global meta_file_list
    global meta_info
    global key_list
    global scripts_dir

    meta_file_list = []
    meta_info = {}
    key_list = {}
    scripts_dir = sdir

    for i in range(len(file_list)):
        meta_file_list.append(file_list[i])
        meta_info[file_list[i]] = []
        key_list[file_list[i]] = collections.OrderedDict()

    get_diff_info(meta_info)
    get_key_list(key_list, readme_file)

    global group_list
    global git_domain
    global git_submit_type
    global domain_list
    global submit_type_list

    group_list = {'A' : 'Architects', 'M' : 'Maintainers', 'I' : 'Integrators', 'R' : 'Reviewers'}
    git_domain = {}
    git_submit_type = {}
    domain_list = subprocess.check_output("cat domains | grep D:", shell = True).split('\n')
    submit_type_list = subprocess.check_output("cat %s | grep \'S\ -\ \'" % readme_file, shell = True).strip().split(':')[1].strip().strip('[').strip(']').split(' | ')

def print_message(status, fn, ln, lm, comment):
    if status == 'OK':
        print("[%s, %d, OK] %s - %s" % (fn, ln, lm, comment))
    else:
        print("[%s, %d, Error] %s - %s" % (fn, ln, lm, comment))
        return "[%s, %d, Error] %s - %s" % (fn, ln, lm, comment)

def find_section(line_num, index):
        return subprocess.check_output('%s/check_section.sh %d %s' % (scripts_dir, line_num, index), shell = True)

def check_syntax(cur_file, obj, line_num, line_message):
    if obj[0] == '+':
        if len(obj) == 1:
            next_line = subprocess.check_output('head -%d %s | tail -1' % (line_num + 1, cur_file), shell = True).strip()
            if next_line == '' or next_line[0] == list(key_list[cur_file].keys())[0]:
                print_message('OK', cur_file, line_num, line_message, 'check_syntax')
            else:
                raise SyntaxError(print_message('Error', cur_file, line_num, line_message, "Next line has to be new section.\n[%s, %d, Error] %s" % (cur_file, line_num + 1, next_line)))
        elif 1 < len(obj) < 4:
            raise SyntaxError(print_message('Error', cur_file, line_num, line_message, "Syntax is wrong."))
        else:
            prev_line = subprocess.check_output('head -%d %s | tail -1' % (line_num - 1, cur_file), shell = True).strip()
            if prev_line == '':
                raise SyntaxError(print_message('Error', cur_file, line_num, line_message, "New section has to be %s in %s.\n[%s, %d, Error] %s" % (list(key_list[cur_file].keys())[0], cur_file, cur_file, line_num - 1, prev_line)))
            else:
                cur_key = obj[1] + obj[2] + obj[3]
                if cur_key == cur_key[0] + ': ':
                    if cur_key[0] in key_list[cur_file].keys():
                        print_message('OK', cur_file, line_num, line_message, 'check_syntax')
                    else:
                        raise SyntaxError(print_message('Error', cur_file, line_num, line_message, "\'%s\' is invalid key in this file." % cur_key[0]))
                else:
                    raise SyntaxError(print_message('Error', cur_file, line_num, line_message, "Syntax is wrong."))
    elif obj[0] == '-' and len(obj) == 1:
        next_line = subprocess.check_output('head -%d %s | tail -1' % (line_num + 1, cur_file), shell = True).strip()
        prev_line = subprocess.check_output('head -%d %s | tail -1' % (line_num - 1, cur_file), shell = True).strip()
        if next_line and prev_line:
            raise SyntaxError(print_message('Error', cur_file, line_num, line_message, "New section has to be %s in %s." % (list(key_list[cur_file].keys())[0], cur_file)))
        else:
            print_message('OK', cur_file, line_num, line_message, 'check_syntax')
    else:
        print_message('OK', cur_file, line_num, line_message, 'check_syntax : Don\'t be checked.')

def check_restriction(cur_file, obj, line_num, line_message):
    cur_key = obj[1]

    if key_list[cur_file][cur_key] == 1:
        raise SyntaxError(print_message('Error', cur_file, line_num, line_message, "Key \'%s\' in file \'%s\' is read-only." % (cur_key, cur_file)))
    else:
        print_message('OK', cur_file, line_num, line_message, 'check_restriction')

def check_group(cur_file, obj, line_num, line_message, cur_domain, server):
    group = cur_domain + ' - ' + group_list[obj[1]]
    ret = os.system('ssh -p %s %s@%s gerrit ls-groups | grep -xq \"%s\"' % (server.port, server.account, server.url, group.replace(' ', '\ ')))
    if ret:
        raise SyntaxError(print_message('Error', cur_file, line_num, line_message, "%s group doesn't exist." % group))
    else:
        print_message('OK', cur_file, line_num, line_message, 'check_group')

def check_git_domain(cur_file, obj, line_num, line_message, cur_git):
    if obj[0] == '+':
        ret = 1
        for i in range(len(domain_list)):
            if domain_list[i] == line_message:
                ret = 0
                break
        if ret:
            raise SyntaxError(print_message('Error', cur_file, line_num, line_message, "%s domain doesn't exist." % line_message.split('D: ')[1]))
        else:
            print_message('OK', cur_file, line_num, line_message, 'check_git_domain')

    if cur_git in git_domain:
        git_domain[cur_git].append([line_num, line_message])
    else:
        git_domain[cur_git] = [[line_num, line_message]]

def check_submit_type(cur_file, obj, line_num, line_message, cur_git):
    if obj[0] == '+':
        ret = 1
        for i in range(len(submit_type_list)):
            if submit_type_list[i] == line_message.split('S: ')[1]:
                ret = 0
                break
        if ret:
            raise SyntaxError(print_message('Error', cur_file, line_num, line_message, "%s is wrong submit type." % line_message.split('S: ')[1]))
        else:
            print_message('OK', cur_file, line_num, line_message, 'check_submit_type')
    if cur_git in git_submit_type:
        git_submit_type[cur_git].append([line_num, line_message])
    else:
        git_submit_type[cur_git] = [[line_num, line_message]]

def is_email(name):
    ret = ''

    if '<' and '>' in name:
        email = name.split('<')
        last_index = len(email) - 1
        email = email[last_index].split('>')[0]
        if '@' in email:
            ret = email
    return ret

def is_int(s):
    try:
        int(s)
        return True
    except ValueError:
        return False

def is_account_id(name):
    ret = 0

    if '(' and ')' in name:
        account_id = name.split('(')
        last_index = len(account_id) - 1
        account_id = account_id[last_index].split(')')[0]
        if is_int(account_id):
            ret = account_id
    return ret

def check_account(name, server):
    account_id_list = []

    email_query = ''
    email = is_email(name)
    if email:
        email_query = "and preferred_email=\\\'%s\\\'" % email

    account_id_query = ''
    account_id = is_account_id(name)
    if account_id:
        account_id_query = "and account_id=\\\'%s\\\'" % account_id

    if email:
        full_name = name.split('<%s>' % email)[0].strip()
    elif account_id:
        full_name = name.split('(%s)' % account_id)[0].strip()
    else:
        full_name = name.strip()

    query = "select account_id from accounts where full_name=\\\'%s\\\' %s %s" % (full_name, email_query, account_id_query)
    res = subprocess.check_output('ssh -p %s %s@%s gerrit gsql -c \"%s\"' % (server.port, server.account, server.url, query.replace(' ', '\ ')), shell = True).split('\n')

    for i in range(len(res) - 2):
        if i > 1:
            account_id_list.append(res[i].strip())

    return account_id_list

def check_user(cur_file, obj, line_num, line_message, server):
    account = obj.split('%s%s: ' % (obj[0], obj[1]))[1]
    account_id_list = check_account(account, server)
    if len(account_id_list) == 0:
        raise SyntaxError(print_message('Error', cur_file, line_num, line_message, "%s is wrong. Check full name or email again." % account))
    elif len(account_id_list) == 1:
        print_message('OK', cur_file, line_num, line_message, 'check_user')
    else:
        raise SyntaxError(print_message('Error', cur_file, line_num, line_message, "%s is ambiguous. Specify account_id %s." % (account, account_id_list)))

def check_user_in_group(cur_file, obj, line_num, line_message, cur_domain, server):
    group = cur_domain + ' - ' + group_list[obj[1]]
    account = obj.split('%s%s: ' % (obj[0], obj[1]))[1]
    ret = os.system('ssh -p %s %s@%s gerrit ls-groups --user \"%s\" | grep -xq \"%s\"' % (server.port, server.account, server.url, account.replace(' ', '\ '), group.replace(' ', '\ ')))
    if ret:
        print_message('OK', cur_file, line_num, line_message, 'check_user_in_group')
    else:
        raise SyntaxError(print_message('Error', cur_file, line_num, line_message, "%s is already in %s." % (account, group)))

def check_semantics(cur_file, obj, line_num, line_message, cur_section, server):
    check_restriction(cur_file, obj, line_num, line_message)

    if cur_file == 'domains':
        if obj[1] in group_list:
            if obj[0] == '+':
                check_group(cur_file, obj, line_num, line_message, cur_section, server)
                check_user(cur_file, obj, line_num, line_message, server)
#                check_user_in_group(cur_file, obj, line_num, line_message, cur_section, server)
    elif cur_file == 'git-trees':
        if obj[1] == 'D':
            check_git_domain(cur_file, obj, line_num, line_message, cur_section)
	if obj[1] == 'S':
            check_submit_type(cur_file, obj, line_num, line_message, cur_section)
        if obj[1] == 'O':
            check_user(cur_file, obj, line_num, line_message, server)

def check_each_object(server):
    for cur_file in meta_info.keys():
        for i in range(len(meta_info[cur_file])):
            for j in range(len(meta_info[cur_file][i])):
                if j > 3:
                    obj = meta_info[cur_file][i][j]
                    if len(obj) == 0:
                        continue
                    elif obj[0] == '+' or obj[0] == '-':
                        line_num = int(meta_info[cur_file][i][3].split(',')[0]) + j - 4
                        line_message = "%s" % obj[0].join(obj.split(obj[0])[1:len(obj)])

                        check_syntax(cur_file, obj, line_num, line_message)

                        if len(obj) > 1:
                            cur_section = find_section(line_num, meta_info[cur_file][i][2]).strip().split('%s: ' % list(key_list[cur_file].keys())[0])[1]
                            check_semantics(cur_file, obj, line_num, line_message, cur_section, server)

def check_domain_edit():
    for git_project in git_domain.keys():
        if len(git_domain[git_project]) != 2:
            raise SyntaxError(print_message('Error', 'git-trees', git_domain[git_project][0][0], git_domain[git_project][0][1], "Domain(Subdomain) can't be added or removed."))
        else:
            print_message('OK', 'git-trees', git_domain[git_project][0][0], git_domain[git_project][0][1], 'check_domain_edit')

def check_submit_type_edit():
    for git_project in git_submit_type.keys():
        if len(git_submit_type[git_project]) != 2:
            raise SyntaxError(print_message('Error', 'git-trees', git_submit_type[git_project][0][0], git_submit_type[git_project][0][1], "Submit type can't be added or removed."))
        else:
            print_message('OK', 'git-trees', git_submit_type[git_project][0][0], git_submit_type[git_project][0][1], 'check_submit_type_edit')

def check_patchset_is_valid(meta_files, readme_file, sdir, port, account, url):

    init_info(meta_files, readme_file, sdir, Server(port, account, url))

    try:
        check_each_object(Server(port, account, url))
        check_domain_edit()
        check_submit_type_edit()

    except SyntaxError, ret:
        return ret

    return 0
