#!/usr/bin/env python3

import itertools
import os
import stat
import sys

import gitignorefile


paths = sys.argv[1:] or ["."]


def color(name, mode, is_link):
    if mode is None:
        if is_link:
            return "or"
        else:
            return "mi"
    else:
        type = stat.S_IFMT(mode)
        if type == stat.S_IFIFO:
            return "fi"
        elif type == stat.S_IFCHR:
            return "cd"
        elif type == stat.S_IFDIR:
            if mode & stat.S_ISVTX:
                return "tw" if mode & stat.S_IWOTH else "st"
            elif mode & stat.S_IWOTH:
                return "ow"
            else:
                return "di"
        elif type == stat.S_IFBLK:
            return "bd"
        elif type == stat.S_IFLNK:
            return "ln"
        elif type == stat.S_IFDOOR:
            return "do"
        elif type == stat.S_IFSOCK:
            return "so"
        elif type == stat.S_IFREG:
            ext = f"*{os.path.splitext(name)[1]}"
            if mode & stat.S_ISUID:
                return "su"
            elif mode & stat.S_ISGID:
                return "sg"
            elif mode & (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH):
                return "ex"
            elif ext in colors:
                return ext
            else:
                return "fi"
        else:
            return "no"


def apply_color(name, mode, is_link):
    name_color = color(name, mode, is_link)
    if name_color in colors:
        return f"\033[{colors[name_color]}m{name}\033[0m"
    else:
        return name


def pairwise(collection):
    # Supporting lower Pythons.
    a, b = itertools.tee(collection)
    next(b, None)
    return zip(a, b)


def get_mode(path):
    try:
        return os.lstat(path).st_mode
    except FileNotFoundError:
        """"""


def filter_path(path, prefix):
    children = sorted(os.listdir(path), key=lambda name: (name.lower(), name))

    for name in children:
        sub_path = os.path.join(path, name)
        mode = get_mode(sub_path)
        is_link = mode and stat.S_IFMT(mode) == stat.S_IFLNK
        if is_link:
            link = os.readlink(sub_path)
            link_mode = get_mode(os.path.join(path, link))
            link_suffix = f" -> {apply_color(link, link_mode, False)}"
            if link_mode is None:
                mode = None

        else:
            link_suffix = ""

        if name != ".git" and (is_link and "FIXME" or not gitignores(sub_path)):
            yield sub_path, is_link, name, link_suffix, mode


def process_path(path, prefix=""):
    for (sub_path, is_link, name, link_suffix, mode), next in pairwise(
        itertools.chain(filter_path(path, prefix), [None])
    ):
        sticks = "├──" if next else "└──"
        print(f"{prefix}{sticks}", f"{apply_color(name, mode, is_link)}{link_suffix}")

        if not is_link and os.path.isdir(sub_path):
            sticks = "│   " if next else "    "
            process_path(sub_path, prefix=f"{prefix}{sticks}")
            statistics["directories"] += 1

        else:
            statistics["files"] += 1


if os.name == "nt":
    try:
        import colorama

        colorama.init()

    except ImportError:
        apply_color = lambda x, *y: x

colors = dict(
    [
        entry.split("=", maxsplit=1)
        for entry in (
            os.environ.get("_3_COLORS")
            or os.environ.get("TREE_COLORS")
            or os.environ.get("LS_COLORS")
            or ":no=00:rs=0:fi=00:di=01;34:ln=01;36:pi=40;33:so=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:ex=01;32:*.bat=01;32:*.BAT=01;32:*.btm=01;32:*.BTM=01;32:*.cmd=01;32:*.CMD=01;32:*.com=01;32:*.COM=01;32:*.dll=01;32:*.DLL=01;32:*.exe=01;32:*.EXE=01;32:*.arj=01;31:*.bz2=01;31:*.deb=01;31:*.gz=01;31:*.lzh=01;31:*.rpm=01;31:*.tar=01;31:*.taz=01;31:*.tb2=01;31:*.tbz2=01;31:*.tbz=01;31:*.tgz=01;31:*.tz2=01;31:*.z=01;31:*.Z=01;31:*.zip=01;31:*.ZIP=01;31:*.zoo=01;31:*.asf=01;35:*.ASF=01;35:*.avi=01;35:*.AVI=01;35:*.bmp=01;35:*.BMP=01;35:*.flac=01;35:*.FLAC=01;35:*.gif=01;35:*.GIF=01;35:*.jpg=01;35:*.JPG=01;35:*.jpeg=01;35:*.JPEG=01;35:*.m2a=01;35:*.M2a=01;35:*.m2v=01;35:*.M2V=01;35:*.mov=01;35:*.MOV=01;35:*.mp3=01;35:*.MP3=01;35:*.mpeg=01;35:*.MPEG=01;35:*.mpg=01;35:*.MPG=01;35:*.ogg=01;35:*.OGG=01;35:*.ppm=01;35:*.rm=01;35:*.RM=01;35:*.tga=01;35:*.TGA=01;35:*.tif=01;35:*.TIF=01;35:*.wav=01;35:*.WAV=01;35:*.wmv=01;35:*.WMV=01;35:*.xbm=01;35:*.xpm=01;35:"
        ).split(":")[1:-1]
    ]
)

gitignores = gitignorefile.Cache()
statistics = {"directories": 0, "files": 0}
for path in paths:
    mode = get_mode(path)
    colored_name = apply_color(path, mode, False)
    if stat.S_IFMT(mode) == stat.S_IFDIR:
        print(colored_name)
        process_path(path)

    else:
        print(colored_name, "[error opening dir]")

print()
print(", ".join((f"{v} {k}" for k, v in statistics.items())))
