#!/usr/bin/env python3

# Copyright (C) 2013 Wind River Systems, Inc.
# Copyright (C) 2014 Intel Corporation
#
# SPDX-License-Identifier: GPL-2.0-or-later
#
# - list available recipes which have PACKAGECONFIG flags
# - list available PACKAGECONFIG flags and all affected recipes
# - list all recipes and PACKAGECONFIG information

import sys
import optparse
import os


scripts_path = os.path.abspath(os.path.dirname(os.path.abspath(sys.argv[0])))
lib_path = os.path.abspath(scripts_path + '/../lib')
sys.path = sys.path + [lib_path]

import scriptpath

# For importing the following modules
bitbakepath = scriptpath.add_bitbake_lib_path()
if not bitbakepath:
    sys.stderr.write("Unable to find bitbake by searching parent directory of this script or PATH\n")
    sys.exit(1)

import bb.cooker
import bb.providers
import bb.tinfoil

def get_fnlist(bbhandler, pkg_pn, preferred):
    ''' Get all recipe file names '''
    if preferred:
        (latest_versions, preferred_versions, required_versions) = bb.providers.findProviders(bbhandler.config_data, bbhandler.cooker.recipecaches[''], pkg_pn)

    fn_list = []
    for pn in sorted(pkg_pn):
        if preferred:
            fn_list.append(preferred_versions[pn][1])
        else:
            fn_list.extend(pkg_pn[pn])

    return fn_list

def get_recipesdata(bbhandler, preferred):
    ''' Get data of all available recipes which have PACKAGECONFIG flags '''
    pkg_pn = bbhandler.cooker.recipecaches[''].pkg_pn

    data_dict = {}
    for fn in get_fnlist(bbhandler, pkg_pn, preferred):
        data = bbhandler.parse_recipe_file(fn)
        flags = data.getVarFlags("PACKAGECONFIG")
        flags.pop('doc', None)
        if flags:
            data_dict[fn] = data

    return data_dict

def collect_pkgs(data_dict):
    ''' Collect available pkgs in which have PACKAGECONFIG flags '''
    # pkg_dict = {'pkg1': ['flag1', 'flag2',...]}
    pkg_dict = {}
    for fn in data_dict:
        pkgconfigflags = data_dict[fn].getVarFlags("PACKAGECONFIG")
        pkgconfigflags.pop('doc', None)
        pkgname = data_dict[fn].getVar("PN")
        pkg_dict[pkgname] = sorted(pkgconfigflags.keys())

    return pkg_dict

def collect_flags(pkg_dict):
    ''' Collect available PACKAGECONFIG flags and all affected pkgs '''
    # flag_dict = {'flag': ['pkg1', 'pkg2',...]}
    flag_dict = {}
    for pkgname, flaglist in pkg_dict.items():
        for flag in flaglist:
            if flag in flag_dict:
                flag_dict[flag].append(pkgname)
            else:
                flag_dict[flag] = [pkgname]

    return flag_dict

def display_pkgs(pkg_dict):
    ''' Display available pkgs which have PACKAGECONFIG flags '''
    pkgname_len = len("RECIPE NAME") + 1
    for pkgname in pkg_dict:
        if pkgname_len < len(pkgname):
            pkgname_len = len(pkgname)
    pkgname_len += 1

    header = '%-*s%s' % (pkgname_len, str("RECIPE NAME"), str("PACKAGECONFIG FLAGS"))
    print(header)
    print(str("").ljust(len(header), '='))
    for pkgname in sorted(pkg_dict):
        print('%-*s%s' % (pkgname_len, pkgname, ' '.join(pkg_dict[pkgname])))


def display_flags(flag_dict):
    ''' Display available PACKAGECONFIG flags and all affected pkgs '''
    flag_len = len("PACKAGECONFIG FLAG") + 5

    header = '%-*s%s' % (flag_len, str("PACKAGECONFIG FLAG"), str("RECIPE NAMES"))
    print(header)
    print(str("").ljust(len(header), '='))

    for flag in sorted(flag_dict):
        print('%-*s%s' % (flag_len, flag, '  '.join(sorted(flag_dict[flag]))))

def display_all(data_dict):
    ''' Display all pkgs and PACKAGECONFIG information '''
    print(str("").ljust(50, '='))
    for fn in data_dict:
        print('%s' % data_dict[fn].getVar("P"))
        print(fn)
        packageconfig = data_dict[fn].getVar("PACKAGECONFIG") or ''
        if packageconfig.strip() == '':
            packageconfig = 'None'
        print('PACKAGECONFIG %s' % packageconfig)

        for flag,flag_val in data_dict[fn].getVarFlags("PACKAGECONFIG").items():
            if flag == "doc":
                continue
            print('PACKAGECONFIG[%s] %s' % (flag, flag_val))
        print('')

def main():
    pkg_dict = {}
    flag_dict = {}

    # Collect and validate input
    parser = optparse.OptionParser(
        description = "Lists recipes and PACKAGECONFIG flags. Without -a or -f, recipes and their available PACKAGECONFIG flags are listed.",
        usage = """
    %prog [options]""")

    parser.add_option("-f", "--flags",
            help = "list available PACKAGECONFIG flags and affected recipes",
            action="store_const", dest="listtype", const="flags", default="recipes")
    parser.add_option("-a", "--all",
            help = "list all recipes and PACKAGECONFIG information",
            action="store_const", dest="listtype", const="all")
    parser.add_option("-p", "--preferred-only",
            help = "where multiple recipe versions are available, list only the preferred version",
            action="store_true", dest="preferred", default=False)

    options, args = parser.parse_args(sys.argv)

    with bb.tinfoil.Tinfoil() as bbhandler:
        bbhandler.prepare()
        print("Gathering recipe data...")
        data_dict = get_recipesdata(bbhandler, options.preferred)

        if options.listtype == 'flags':
            pkg_dict = collect_pkgs(data_dict)
            flag_dict = collect_flags(pkg_dict)
            display_flags(flag_dict)
        elif options.listtype == 'recipes':
            pkg_dict = collect_pkgs(data_dict)
            display_pkgs(pkg_dict)
        elif options.listtype == 'all':
            display_all(data_dict)

if __name__ == "__main__":
    main()
