#!/usr/bin/env python3
import javalang
import os
import pandas as pd
import glob
import tabulate

# God class if:
# |M(C)| > E(M) + 6*V(M)
# (number of methods greater than average across all classes plus 6 times the
# standard deviation)


DIR: str = os.path.dirname(os.path.realpath(__file__))
SOURCES: str = DIR + '/resources/xerces2-j-src'
OUT_DIR: str = DIR + '/god_classes'


def clean_output():
    filelist = glob.glob(OUT_DIR + '/*.csv')
    for f in filelist:
        os.remove(f)


def parse(path: str) -> list[tuple[str, str]]:
    # Get the AST of the file
    with open(path) as file:
        data = file.read()
    tree = javalang.parse.parse(data)

    # Fetch package name from package declaration
    # if node is missing, assuming default package ('')
    package_name = ''
    for _, node in tree.filter(javalang.tree.PackageDeclaration):
        package_name = node.name
        break

    # Get all classes and number of methods for each one
    rows: list[tuple[str, str]] = []
    for _, node in tree.filter(javalang.tree.ClassDeclaration):
        fqdn = package_name + '.' + node.name
        rows.append((fqdn, len(node.methods),))

    return rows


def create_df(root) -> pd.DataFrame:
    frame = pd.DataFrame(columns=['class_name', 'method_num'])

    i: int = 0
    for path, dirs, files in os.walk(root):
        for f in files:
            if f.endswith('.java'):
                # for each java file, add all entries found to dataframe
                for row in parse(path + '/' + f):
                    frame.loc[i] = row
                    i += 1

    return frame


def main():
    clean_output()
    df = create_df(SOURCES)

    mean = df.loc[:, 'method_num'].mean()
    std = df.loc[:, 'method_num'].std()
    threshold = mean + 6 * std

    god_classes_df = df[df['method_num'] > threshold]
    god_classes_df.to_csv(OUT_DIR + '/god_classes.csv')

    god_classes_df.columns = ['Class Name', '# Methods']
    god_classes_df = god_classes_df.set_index('Class Name', drop=True)
    print(god_classes_df.to_markdown())


if __name__ == '__main__':
    main()
