#!/bin/python3
# This is MAMAS Python-CGI lib.
# Used also for testing by other tools.
# Copyright (c) 2022 Jan Otte, Dwarf Technologies s.r.o. All rights reserved.
import os
import sys
import subprocess
import string
import random
import logging
from pathlib import Path
import datetime

initfile = "base_defs"
brandfile_name = ".brand_selection"

if os.path.isfile(brandfile_name):
    brandfile = brandfile_name
elif os.path.isfile("../" + brandfile_name):
    brandfile = "../" + brandfile_name
else:
    raise Exception("Brand file was not found.")

command = "env -i bash -c 'set -a && . ./"+initfile+" && env'"
brand_command = "env -i bash -c 'set -a && . " + brandfile + " && env'"
# define the variables beginning with md (mamas defs)
md_name = ""
md_bindir = ""
md_srvdir = ""
md_srvdir_srv = ""
md_srvdir_cli = ""
md_srvdir_clidata = ""
md_srvdir_evgui = ""
md_srvdir_evreg = ""
md_srvdir_evdata = ""
md_servid = ""
md_srv_liclock = ""
md_form_devid = ""
md_form_fdata = ""
md_form_ddata = ""
md_form_bdata = ""
md_form_devtype = ""
md_form_protv= ""
md_devt_advr = ""
md_devt_linb = ""
md_devt_owrt = ""
md_ugo_wmgroup = ""
md_aerr_invid = 0
md_aerr_notex = 0
md_aerr_regpr = 0
md_aerr_serfu = 0
md_aerr_serve = 0
md_aerr_invda = 0
md_aerr_datal = 0
md_aerr_datap = 0
md_aerr_comme = 0
md_pylog = ""
md_logdir = ""
md_sfn_data = ""
md_sfn_cfg = ""
md_sfn_defaults = ""
md_sfn_backup = ""
md_env_read = False
mp_brand_short = ""
mp_brand_long = ""
di_allowed_chars = 'ABCDFGOPRSTUVXYZ'
di_free_prefix = "free_"
di_taken_prefix = "taken_"
version_ok_chars = set(string.digits + '.')

def stamp():
    return datetime.datetime.now().strftime("[%H%M:%S.%f]")

def pathstring(inint = -1, depth = 4, take = False, free = False):
    if (8<depth): return ''
    while True:
        if 0 > inint: nmbr = random.randrange(16 ** depth)
        else: nmbr = inint % (16 ** depth)
        if 0 < nmbr: # do not accept 0 as ID
            break
    path = ''
    for i in range(depth):
        res = nmbr >> (4*(depth-1-i)) & 0b1111
        if i+1 == depth:
            if take: path += di_taken_prefix
            elif free: path += di_free_prefix
        path = path + di_allowed_chars[res]
    return path

def checkversion(x):
    return set(x) <= version_ok_chars

def vtuple(x):
    nt = []
    for i in x.split('.'):
        nt.append(i)
    return tuple(nt)

def pathstringtonum(ps = ''):
    num = 0
    exp = 0
    for i in range(len(ps)):
        if ps[-1-i] == 'A':
            val = 0
        elif ps[-1-i] == 'B':
            val = 1
        elif ps[-1-i] == 'C':
            val = 2
        elif ps[-1-i] == 'D':
            val = 3
        elif ps[-1-i] == 'F':
            val = 4
        elif ps[-1-i] == 'G':
            val = 5
        elif ps[-1-i] == 'O':
            val = 6
        elif ps[-1-i] == 'P':
            val = 7
        elif ps[-1-i] == 'R':
            val = 8
        elif ps[-1-i] == 'S':
            val = 9
        elif ps[-1-i] == 'T':
            val = 10
        elif ps[-1-i] == 'U':
            val = 11
        elif ps[-1-i] == 'V':
            val = 12
        elif ps[-1-i] == 'X':
            val = 13
        elif ps[-1-i] == 'Y':
            val = 14
        elif ps[-1-i] == 'Z':
            val = 15
        else: continue
        num += val * 16 ** exp;
        exp += 1
    return num

def expand_path(path, depth = 3):
    np = ''
    if depth > len(path): return ''
    for i in range(depth): np += path[i] + '/'
    np += path[i+1:]
    return np

def valid_devtype(devt):
    if devt == md_devt_advr or devt == md_devt_linb or devt == md_devt_owrt:
        return True
    return False

def create_dir(dname):
    try:
        os.mkdir(dname)
        os.chmod(dname, 0o775)
    except OSError: sys.exit("Unable to create directory "+dname)

def setup_dir_hier(whereto, wherefrom = '', depth = 0):
    if 0 == depth:
        if '' == wherefrom:
            depth = 3
        else:
            depth = 4
    if 1 > depth: return
    if not os.path.isdir(whereto):
        create_dir(whereto)
    cwd = os.getcwd()
    os.chdir(whereto)
    for i in range(len(di_allowed_chars)):
        if 0 == len(wherefrom): # asked to build the persistent tree
            if not os.path.isdir(di_allowed_chars[i]): os.mkdir(di_allowed_chars[i])
        else: # asked to copy tree from peristent to ramdisk
            if depth > 1: # not the last level, simply create the tree
                if not os.path.isdir(di_allowed_chars[i]): os.mkdir(di_allowed_chars[i])
            else: # last level containing dirs and links
                if os.path.isdir(wherefrom + '/' + di_allowed_chars[i]): # if device registered in persist., create in ramdisk
                    if not os.path.isdir(whereto + '/' + di_allowed_chars[i]): os.mkdir(whereto + '/' + di_allowed_chars[i])
                    if os.path.isfile(whereto + '/' + di_free_prefix + di_allowed_chars[i]):
                        os.unlink(whereto + '/' + di_free_prefix + di_allowed_chars[i]) # remove dangling free files
                else: # otherwise it is free, create appropriate file
                    if not os.path.isfile(whereto + '/' + di_free_prefix + di_allowed_chars[i]):
                        Path(whereto + '/' + di_free_prefix + di_allowed_chars[i]).touch()
        follow_wherefrom = wherefrom
        if '' != wherefrom: follow_wherefrom += '/' + di_allowed_chars[i]
        if 1 < depth: setup_dir_hier(whereto + '/' + di_allowed_chars[i], follow_wherefrom, depth-1)
    os.chdir(cwd)

def setlevel(level):
    global log
    log.setLevel(level)

def find_devtype(dt):
    global devtypes
    try:
        ret = devtypes.index(dt)
    except: return -1
    if 0 == ret: return -1 # "any device" device type not allowed during registration
    return ret

def readenv():
    global md_name, md_bindir, md_srvdir, md_srvdir_srv, md_srvdir_cli, md_srvdir_clidata, md_srvdir_evgui, md_srvdir_evreg, md_srvdir_evdata, md_servid, md_srv_liclock, md_env_read, md_form_devid, md_form_fdata, md_form_ddata, md_form_bdata, md_form_devtype, md_form_protv, md_devt_advr, md_devt_linb, md_devt_owrt, md_ugo_wmgroup, md_aerr_invid, md_aerr_notex, md_aerr_regpr, md_aerr_serfu, md_aerr_serve, md_aerr_invda, md_aerr_datal, md_aerr_datap, md_aerr_comme, md_pylog, md_logdir, md_sfn_data, md_sfn_cfg, md_sfn_defaults, md_sfn_backup, mp_brand_short, mp_brand_long
    if os.path.isfile(brandfile):
        for line in subprocess.getoutput(brand_command).split("\n"):
            key, value = line.split("=")
            os.environ[key] = value
        mp_brand_short = os.getenv('BRAND_SHORT', "")
        mp_brand_long = os.getenv('BRAND_LONG', "")
    else:
        raise Exception("Brand file not found (should not happen)")

    if os.path.isfile(initfile):
        for line in subprocess.getoutput(command).split("\n"):
#            print("splitting line:" + str(line))
            key, value = line.split("=")
            os.environ[key] = value
#            print("key: "+key+" value: "+value)
        md_name = os.getenv('NAME', "")
        md_bindir = os.getenv('BINDIR', "")
        md_srvdir = os.getenv('SRVDIR', "")
        md_aerr_invid = os.getenv('AERR_INVID', -1) # Error: invalid ID
        md_aerr_notex = os.getenv('AERR_NOTEX', -1) # Error: not exist on server
        md_aerr_regpr = os.getenv('AERR_REGPR', -1) # Error: registration still in progress
        md_aerr_serfu = os.getenv('AERR_SERFU', -1) # Error: server full
        md_aerr_serve = os.getenv('AERR_SERVE', -1) # Error: internal server error
        md_aerr_invda = os.getenv('AERR_INVDA', -1) # Error: invalid data received
        md_aerr_datal = os.getenv('AERR_DATAL', -1) # Error: old data waiting for proc. (locked)
        md_aerr_datap = os.getenv('AERR_DATAP', -1) # Error: old data being processed
        md_aerr_comme = os.getenv('AERR_COMME', -1) # Error: communication error: protocol version unknown to server
        md_srvdir_srv = os.getenv('SRVDIR_SRV', "")
        md_srvdir_cli = os.getenv('SRVDIR_CLI', "")
        md_srvdir_clidata = os.getenv('SRVDIR_CLIDATA', "")
        md_srvdir_evgui = os.getenv('SRVDIR_EVGUI', "")
        md_srvdir_evreg = os.getenv('SRVDIR_EVREG', "")
        md_srvdir_evdata = os.getenv('SRVDIR_EVDATA', "")
        md_servid = os.getenv('SERVID', "")
        md_srv_liclock = os.getenv('SRV_LICLOCK', "")
        md_form_devid = os.getenv('FORM_DEVID', "")
        md_form_fdata = os.getenv('FORM_FDATA', "")
        md_form_ddata = os.getenv('FORM_DDATA', "")
        md_form_bdata = os.getenv('FORM_BDATA', "")
        md_form_devtype = os.getenv('FORM_DEVT', "")
        md_form_protv = os.getenv('FORM_PROTV', "")
        md_devt_advr = os.getenv('DEVT_ADVR', "")
        md_ugo_wmgroup = os.getenv('WMGROUP', "")
        md_devt_linb = os.getenv('DEVT_LINB', "")
        md_devt_owrt = os.getenv('DEVT_OWRT', "")
        md_pylog = os.getenv('PYLOG', "")
        md_logdir = os.getenv('LOGDIR', "")
        md_sfn_data = os.getenv('SFN_DATA', "")
        md_sfn_cfg = os.getenv('FN_CFGMERGE', "")
        md_sfn_defaults = os.getenv('FN_DEFAULTS', "")
        md_sfn_backup = os.getenv('FN_BACKARCH', "")
        md_env_read = True
        return True
    return False

# Automatic module initialization
# (1) Read Mamas variables
readenv()
devtypes = [ "any_device", md_devt_linb, md_devt_advr, md_devt_owrt ]
# (2) Initialize logging
try:
    log = logging.getLogger()
    setlevel(logging.WARNING)
    fh = logging.FileHandler(md_logdir + "/" + md_pylog, "a+")
    log.addHandler(fh)
except:
    #print("CGI Logging intialization failure. No logging will be done to MAMAS CGI log.")
    pass

