#!/usr/bin/python3 -u

global version
global config_path
global new_name
global old_name
config_path = "~/.config/autojack/"
new_name = f"{config_path}autojack.json"
old_name = f"{config_path}/autojackrc"

import os
from os.path import expanduser
import glob
import configparser
import json
import sys, getopt


def read_old():
    ''' read old config file. '''
    print("read old file")
    global config
    global def_config
    global version
    global default_device
    config = configparser.ConfigParser()
    def_config = config['DEFAULT']

    default_device = "0,0,0"
    for adir in glob.glob("/proc/asound/card*/codec*"):
        idfile = adir.rsplit('/', 1)[0]
        with open(f"{idfile}/id", "r") as card_file:
            for line in card_file:
                # only need one line
                tmpname = line.rstrip()
            if tmpname != "HDMI" and tmpname != "NVidia":
                default_device = f"{tmpname},0,0"

    # first set defaults, This makes sure there is always something to convert
    config['DEFAULT'] = {
        'JACK': "False",
        'DRIVER': "alsa",
        'CHAN-IN': "0",
        'CHAN-OUT': "0",
        'RATE': "48000",
        'FRAME': "1024",
        'PERIOD': "2",
        'CONNECT-MODE': "n",
        'ZFRAME': "512",
        'XDEV': "",
        'PULSE-IN': "pulse_in",
        'PULSE-OUT': "pulse_out",
        'PJ-IN-CON': 'system:capture_1',
        'PJ-OUT-CON': 'monitor',
        'PJ-IN-COUNT': '2',
        'PJ-OUT-COUNT': '2',
        'A2J': "True",
        'DEV': default_device,
        'USBAUTO': "True",
        'USB-SINGLE': "False",
        'USBDEV': "",
        "PULSE": "True",
        "LOG-LEVEL": "15",
        "BLACKLIST": "",
        "PHONE-ACTION": "switch",
        "PHONE-DEVICE": default_device,
        "MONITOR": 'system:playback_1'
    }

    c_file = expanduser(old_name)
    if os.path.isfile(c_file):
        # config file exists, read it in
        config.read(c_file)
        # rename to *.old
        os.replace(c_file, f"{c_file}.bak")
    # fix some well known problems
    if def_config['MONITOR'] == "none":
        def_config['MONITOR'] = 'system:playback_1'
    if def_config['PJ-IN-CON'] == "0":
        def_config['PJ-IN-CON'] = "none"
    if def_config['PJ-OUT-CON'] == "0":
        def_config['PJ-OUT-CON'] = "none"
    if def_config['DEV'] == "default":
        def_config['DEV'] = default_device
    if not def_config['CONNECT-MODE'] in ['a', 'A', 'e', 'E']:
        def_config['CONNECT-MODE'] = "n"

    #for key in config['DEFAULT']: print(f"{key}: {def_config[key]}")


def make_db():
    ''' Stuff config into this db:
        Version: text
        log-level: int (20)
        JACK: # things that require restart
            Used: bool (False)
            driver: string (alsa)
            chan_in: int (2)
            chan_out: int (2)
            rate: int (48000)
            frame: int (1024)
            nperiods: int (2)
            connect_mode: char (' ')
            dev: string (1st internal non-hdmi)
            USBdev: string (blank)
        extra: # Stuff that can be changed without restart
            A2J: bool (True)
            USBAUTO: bool (True)
            USBSingle: bool (False)
            Monitor: string (system:playback_1)
            Phone_action: string (none)
            Phone_device: string (default device)
        pulse:
            pulse_in:
                {
                name
                connection
                channels
                }...
            pulse_out:
                {
                name
                connection
                channels
                }...
        Devices:
            {
            name: string
                {
                number: int (-1 for unplugged)
                usb: bool
                internal: bool
                hdmi: bool
                rates: list
                sub:
                    {
                    number: int
                    playback: bool
                    capture: bool
                    play_chan: int
                    cap_chan: int
                    rate: int
                    frame: int
                    nperiods: int
                    hide: bool
                    }...
                }
            }...
    '''
    print("make new data base")
    global def_config
    global version
    global our_db
    global default_device
    print(f"version: {version}")
    
    our_db = {'version': version}
    our_db['log-level'] = int(def_config['log-level'])
    jack_db = {'on': bool(def_config['jack'] in ['True']), 'driver': def_config['driver'],
                'chan-in': int(def_config['chan-in']), 'chan-out': int(def_config['chan-out']),
                'rate': int(def_config['rate']), 'frame': int(def_config['frame']),
                'period': int(def_config['period']), 'connect-mode': def_config['connect-mode'],
                'dev': def_config['dev'], 'usbdev': def_config['usbdev']}

    extra_db = {'a2j': bool(def_config['a2j'] in ['True']), 'usbauto': bool(def_config['usbauto'] in ['True']),
                'usb-single': bool(def_config['usb-single'] in ['True']), 'monitor': def_config['monitor'],
                'phone-action': def_config['phone-action'], 'phone-device': def_config['phone-device']
                }
    our_db['jack'] = jack_db
    our_db['extra'] = extra_db
    pulse_in_db = {}
    pulse_out_db = {}
    pulse_db = {'inputs': pulse_in_db, 'outputs': pulse_out_db}
    our_db['pulse'] = pulse_db
    for idx, bridge in enumerate(def_config['PULSE-IN'].split()):
        con = def_config['PJ-IN-CON'].split()
        if len(con) < (idx + 1):
            this_con = "none"
        else:
            this_con = def_config['PJ-IN-CON'].split()[idx]
        cnt = def_config['PJ-IN-COUNT'].split()
        if len(cnt) < (idx + 1):
            this_cnt = "2"
        else:
            this_cnt = def_config['PJ-IN-COUNT'].split()[idx]
        temp_db = {'connection': this_con, 'count': int(this_cnt)}
        pulse_in_db[bridge] = temp_db
    for idx, bridge in enumerate(def_config['PULSE-OUT'].split()):
        con = def_config['PJ-OUT-CON'].split()
        if len(con) < (idx + 1):
            this_con = "none"
        else:
            this_con = def_config['PJ-OUT-CON'].split()[idx]
        cnt = def_config['PJ-OUT-COUNT'].split()
        if len(cnt) < (idx + 1):
            this_cnt = "2"
        else:
            this_cnt = def_config['PJ-OUT-COUNT'].split()[idx]
        temp_db = {'connection': this_con, 'count': int(this_cnt)}
        pulse_out_db[bridge] = temp_db

    ''' Now devices we just want to add devices in the config file
        studio-controls will add all devices it finds and autojack will
        make it's own DB as well. We don't check if the rate, buffer,etc
        makes sense, the other two programs will correct that and the old
        style config file assumed jack master numbers anyway so we use
        those.
    '''
    devices_db = {}
    our_db['devices'] = devices_db
    devicelist = [default_device]
    if def_config['DEV'] not in devicelist:
        devicelist.append( def_config['DEV'])
    if def_config['USBDEV'] not in devicelist and def_config['USBDEV'] != '':
        devicelist.append( def_config['USBDEV'])
    if def_config['PHONE-DEVICE'] not in devicelist:
        devicelist.append( def_config['PHONE-DEVICE'])
    devicelist = devicelist + def_config['XDEV'].split() + def_config['BLACKLIST'].split()
    #print(f"device list: {str(devicelist)}")
    for rdev in devicelist:
        rdev_l = rdev.split(',')
        #print(f"found: {str(rdev_l)}")
        if len(rdev_l) == 3:
            dev, sub, ssub = rdev_l
            #print(f"Device: {dev} sub: {sub} ssub: {ssub}")
            if dev not in devices_db:
                devices_db[dev] = make_dev_temp()
            dev_db = devices_db[dev]
            if dev == default_device.split(',')[0]:
                dev_db['internal'] = True
            if dev == def_config['USBDEV'].split(',')[0]:
                dev_db['usb'] = True
            if sub not in dev_db['sub']:
                dev_db['sub'][sub] = make_sub_temp()
            sub_db = dev_db['sub'][sub]
            if rdev in def_config['XDEV'].split():
                sub_db['play-chan'] = 100
                sub_db['cap-chan'] = 100
            if rdev in def_config['BLACKLIST'].split():
                sub_db['hide'] = True

    return

def make_dev_temp():
    ''' make a biolerplate device with no sub '''
    dev_temp = {'number': -1, 'usb': False, "internal": False, 'hdmi': False,
                'rates': ['32000', '44100', '48000', '88200', '96000', '192000'],
                'sub': {}}
    return dev_temp
    
def make_sub_temp():
    ''' make a template for a sub device '''
    global def_config
    sub_temp = {'playback': True, 'capture': True, 
                'play-chan': 0, 'cap-chan': 0,
                'rate': int(def_config['RATE']), 'frame': int(def_config['FRAME']),
                'nperiods': int(def_config['PERIOD']), 'hide': False}
    return sub_temp

def write_new():
    ''' write new config file '''
    print("write new config file")
    global our_db
    #print(f"our-db {str(our_db)}")
    #print(json.dumps(our_db, indent = 4))
    #print(f" test access our_db['jack']['rate'] {str(our_db['jack']['rate'])}")
    c_file = expanduser(new_name)
    if not os.path.isfile(c_file):
        # either first run or old version
        c_dir = expanduser(config_path)
        if not os.path.isdir(c_dir):
            os.makedirs(c_dir)

    with open(c_file, 'w') as json_file:
        json.dump(our_db, json_file, indent = 4)
        json_file.write("\n")
    return

def check_new(use_bk):
    global new_name
    global old_name
    global our_db
    global version
    print(f"checking config file for compatability with version {version}")
    c_file = expanduser(new_name)
    if use_bk:
        c_file = expanduser(old_name)
    if os.path.isfile(c_file):
        # config file exists, read it in
        with open(c_file) as f:
            our_db = json.load(f)
    else:
        print(f"Error, {c_file} not found.")
        # set to 0 to not cause errors that stop something
        sys.exit(0)

    if our_db['version'] == version:
        print(f"same version {version}, no fix needed")
    else:
        print(f"config file is version: {our_db['version']} updating")
        print(f"saving old config file to: {c_file}.{our_db['version']}")
        os.replace(c_file, f"{c_file}.{our_db['version']}")

        maj, mn, sub_stuff = our_db['version'].split('.')
        subls = sub_stuff.split('-')
        sub = subls[0]
        if int(maj) > 1:
            #this is up to date major
            if int(mn) > -1:
                #this is up to date minor
                if int(sub) > 20:
                    #this is up to date sub
                    # no change needed just update version
                    our_db['version'] = version
                else:
                    print("print version unusable please run convert-studio-controls -r")
                    return
            else:
                print("print version unusable please run convert-studio-controls -r")
                return
        else:
            print("print version unusable please run convert-studio-controls -r")
            return
        write_new()


def main(argv):

    global config_path
    global new_name
    global old_name
    global version
    force = False
    use_bk = False
    vfile = "/usr/share/studio-controls/version"

    # what is our version?
    if os.path.isfile(vfile):
        with open(vfile, "r") as version_file:
            for line in version_file:
                version = line.strip()
                print(f"Convert Studio Conrols Config File (version {version})")

    try:
        opts, args = getopt.getopt(argv,"hrvb:")
    except getopt.GetoptError:
        print ('bad parameters')
        sys.exit(2)
    for opt, arg in opts:
        if opt == '-h':
            print ('convert-studio-controls is not meant to be run manually')
            sys.exit()
        elif opt in ("-r"):
            # for testing pre json config
            old_name = f"{old_name}.bak"
            force = True
        elif opt in ("-b"):
            # for testing old json config
            old_name = f"{new_name}.{arg}"
            use_bk = True
        elif opt in ("-v"):
            print(f"Convert Studio Controls config Version: {version}")
            sys.exit()

    c_file = expanduser(new_name)
    if not force and os.path.isfile(c_file):
        # Found a config file, test it
        check_new(use_bk)
    else:
        ''' no usable config file found.
            - search for pre json config and convert
            - or use defaults and create one
            - if force is True then use .bak on file name
        '''
        read_old()
        make_db()
        write_new()

if __name__ == '__main__':
    main(sys.argv[1:])

