import sys
import re
import argparse
import logging
from pathlib import Path
from typing import Optional

from .types import Color


logger = logging.getLogger(__name__)



class ColorFormatter(logging.Formatter):
    COLORS = {
        logging.DEBUG: Color.BLUE,
        logging.INFO: Color.GREEN + Color.Style.BOLD,
        logging.WARNING: Color.Bright.YELLOW + Color.Style.BOLD,
        logging.ERROR: Color.RED + Color.Style.BOLD,
        logging.CRITICAL: \
            Color.WHITE + Color.Style.BOLD + Color.Background.RED,
    }

    def __init__(self, min_level = logging.DEBUG, *args, **kwargs):
        self._min_level_name = min_level
        super().__init__(*args, **kwargs)

    def format(self, record):
        color = self.COLORS.get(record.levelno, "")
        _fmt = self._style._fmt
        try:
            self._style._fmt = re.sub(
                R"%\(levelname\)([^a-z]*)s(\s*)",
                (f"{color}%(levelname)\\1s{Color.RESET}\\2"
                 if record.levelno >= self._min_level_name
                 else ""),
                self._style._fmt
            )
            return logging.Formatter.format(self, record)
        finally:
            self._style._fmt = _fmt


def get_argument_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(prog='vhsh')
    parser.add_argument('-V', '--version', action="store_true",
        help="Print version information")
    parser.add_argument('-v', '--verbose', action="store_true",
        help="Enable debugging output")
    subparsers = parser.add_subparsers()

    parser_run = subparsers.add_parser('run', help='Run scenes with VHSh')
    parser_run.add_argument('shader', nargs='*', metavar='SHADER',
        help='Path to GLSL fragment shader', type=Path)
    parser_run.add_argument('-M', '--midi-mapping',
        help="Path to TOML file with system MIDI mappings")
    # TODO support selction the microphone
    parser_run.add_argument('-m', '--mic', action="store_true",
        help="Make microphone levels available as uniform.")
    parser_run.set_defaults(func=main_run)

    parser_import = subparsers.add_parser('import',
        help='Import scenes from shadertoy.com',
        description=("To use you own API key, set 'VHSH_API_KEY_SHADERTOY':"
                     " https://www.shadertoy.com/myapps"))
    parser_import.add_argument("url")
    parser_import.add_argument("-o", "--outfile", type=Path,
        help="Path to save the scene")
    parser_import.set_defaults(func=main_import)

    parser_migrate = subparsers.add_parser('migrate',
        help='Migrate scenes from one format to another')
    parser_migrate.add_argument("path", type=Path)
    parser_migrate.add_argument("-f", "--from-version", type=int,
        help="Default: Version from file or 0 if undefined")
    parser_migrate.add_argument("-t", "--to-version", type=int,
        help="Default: Current version supported by VHSh (see -V)")
    parser_migrate.set_defaults(func=main_migrate)

    return parser


def print_version():
    from . import __version__
    import os
    import imgui
    import OpenGL
    from .renderer import Renderer
    from .app import VHSh

    version_line = re.match("#version.*", Renderer.FRAGMENT_SHADER_PREAMBLE)
    gl_profile = version_line[0] if version_line is not None else ""

    print(f"{__package__} {__version__}")
    print(f"  {os.path.dirname(__file__)}")
    print(f"  scene format {VHSh.SCENE_FORMAT_VERSION}")
    print()
    print(f"Python {sys.version}\n  {sys.executable}")
    print(f"{OpenGL.__package__} {OpenGL.__version__}\n  {gl_profile}")
    print(f"{imgui.__package__} {imgui.__version__}")  # type: ignore


def configure_logging(verbose: bool):
    if verbose:
        handler = logging.StreamHandler(sys.stdout)
        handler.setFormatter(
            ColorFormatter(
                fmt=
                f"{Color.Style.FAINT}%(asctime)s{Color.RESET}"
                f" %(levelname)-8s"
                f" %(name)s"
                f"{Color.Style.FAINT}"
                f" [%(threadName)s]"
                f" (%(filename)s:%(lineno)i)"
                f"{Color.RESET}:"
                f"\n%(message)s"
            )
        )
        logging.basicConfig(level=logging.DEBUG,
                            handlers=[handler],
                            force=True)
        logging.getLogger("watchfiles.main").setLevel(logging.INFO)

    else:
        handler = logging.StreamHandler(sys.stdout)
        handler.setFormatter(ColorFormatter(fmt="\n%(levelname)s %(message)s",
                                            min_level=logging.WARNING))
        logging.basicConfig(level=logging.INFO,
                            handlers=[handler],
                            force=True)

        logging.getLogger('watchfiles.main').setLevel(logging.CRITICAL)


def main_run(args: argparse.Namespace):
    import tomllib

    from .app import VHSh

    midi_mapping = {}
    if args.midi_mapping:
        with open(args.midi_mapping, 'rb') as f:
            midi_mapping = tomllib.load(f)

    vhsh = VHSh(scenes=args.shader,
                midi_mapping=midi_mapping,
                microphone=args.mic)
    vhsh.run()


def main_import(args: argparse.Namespace):
    from .scene_import import import_scene

    import_scene(args.url, args.outfile)


def main_migrate(args: argparse.Namespace):
    from .migration import migrate

    migrate(args.path,
            from_version=args.from_version,
            to_version=args.to_version)


def main(argv: Optional[list[str]] = None):
    parser = get_argument_parser()
    args = parser.parse_args(argv)

    if args.version or args.verbose:
        print_version()
    if args.version:
        exit(0)

    configure_logging(args.verbose)

    try:
        args.func(args)
    except Exception as e:
        logger.debug(repr(e), exc_info=True)
        logger.error(repr(e))
        exit(-1)


if __name__ == "__main__":
    main()
