#!/usr/bin/env python3

import dataclasses
import pathlib
import re
import subprocess


# license header to use for generated go files
HEADER = '''\
// -*- Mode: Go; indent-tabs-mode: t -*-
//
// Copyright (C) 2022 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0

// Code generated by gen_commands.py DO NOT EDIT.

'''

BASE_MODULE_NAME = 'github.com/kerberos-io/onvif'

SERVICE_NAMES = [
    'analytics',
    'device',
    'event',
    'imaging',
    'media',
    'media2',
    'ptz',
    'recording',
]


@dataclasses.dataclass
class FunctionsGenerator:
    service_name: str
    input_file: pathlib.Path
    output_file: pathlib.Path
    commands = []

    @staticmethod
    def make_function(cmd):
        return '''\
type %sFunction struct{}

func (_ *%sFunction) Request() interface{} {
\treturn &%s{}
}
func (_ *%sFunction) Response() interface{} {
\treturn &%sResponse{}
}

''' % (cmd, cmd, cmd, cmd, cmd)

    def run(self):
        self.commands = []
        with open(self.input_file) as f:
            with open(self.output_file, 'w') as w:
                w.write(HEADER)
                w.write(f'package {self.service_name}\n\n')

                while line := f.readline():
                    if m := re.search(r'type ([^ ]+)Response struct', line):
                        cmd = m.group(1)
                        if cmd.endswith('Fault'):
                            continue  # skip Fault responses
                        self.commands.append(cmd)

                self.commands = sorted(self.commands)
                for cmd in self.commands:
                    print(f'{self.service_name}.{cmd}')
                    w.write(FunctionsGenerator.make_function(cmd))

    def write_func_map(self, w):
        w.write('var %sFunctionMap = map[string]Function{\n' % self.proper_name)
        for cmd in self.commands:
            w.write('\t%s: &%s.%sFunction{},\n' % (cmd, self.service_name, cmd))
        w.write('}\n\n')

    @property
    def proper_name(self):
        return (self.service_name[0].upper() + self.service_name[1:]).replace('Ptz', 'PTZ')


@dataclasses.dataclass
class MainGenerator:
    root_dir: pathlib.Path
    generators = []
    unique_cmds = set()

    def _generate_mappings(self):
        with open(self.root_dir.joinpath('mappings.go'), 'w') as w:
            w.write(HEADER)
            w.write('package onvif\n\n')
            w.write('import (\n')
            for service_name in SERVICE_NAMES:
                w.write(f'\t"{BASE_MODULE_NAME}/{service_name}"\n')
            w.write(')\n\n')

            for generator in self.generators:
                generator.write_func_map(w)

    def _generate_names(self):
        with open(self.root_dir.joinpath('names.go'), 'w') as w:
            w.write(HEADER)
            w.write('package onvif\n\n')
            w.write('// Onvif WebService\n')
            w.write('const (\n')
            for gen in self.generators:
                w.write(f'\t{gen.proper_name}WebService = "{gen.proper_name}"\n')
            w.write(')\n\n')

            for gen in self.generators:
                w.write(f'// WebService - {gen.proper_name}\n')
                w.write('const (\n')
                for cmd in gen.commands:
                    if cmd not in self.unique_cmds:
                        w.write(f'\t{cmd} = "{cmd}"\n')
                    self.unique_cmds.add(cmd)
                w.write(')\n\n')

    def run(self):
        for service_name in SERVICE_NAMES:
            gen = FunctionsGenerator(service_name,
                                     self.root_dir.joinpath(f'{service_name}/types.go'),
                                     self.root_dir.joinpath(f'{service_name}/function.go'))
            gen.run()
            self.generators.append(gen)

        self._generate_mappings()
        self._generate_names()


def main():
    root_dir = pathlib.Path(__file__).parent.parent.resolve()
    main_gen = MainGenerator(root_dir)
    main_gen.run()

    print('\n\nFormatting generated files...')
    try:
        subprocess.check_call(['go', 'fmt', str(root_dir) + "/..."])
    except OSError as e:
        print(f"\n\033[31;1mError occurred while trying to format generated files: {e}\033[0m")
        print("\033[33;1mPlease run 'go fmt ./...' manually to cleanup generated code formatting!\033[0m")


if __name__ == '__main__':
    main()
