#!/usr/bin/python3


"""Generate various reports about movie collection."""

import os
import sys
import time
import typing
import os.path


def usage(retval: int) -> None:
    """Output a usage message."""
    if retval == 0:
        write = sys.stdout.write
    else:
        write = sys.stderr.write

    write('Usage: {}\n'.format(sys.argv[0]))
    write('   --no-views           list large files with no corresponding .views file, sorted by mtime\n')
    write('   --views-timestamps   list large files with corresponding .views files, sorted by views timestamp\n')
    write('   --list-unfinished    list all files with corresponding .views files, that were never finished\n')
    write('   --size-threshold t   only consider files larger than t (units are bytes)\n')
    write('\n')
    write('Various movie reports, based on movie files and their corresponding *.views files.\n')
    sys.exit(retval)


def find_large_files(size_threshold: int) -> typing.Iterable[str]:
    """Yield filenames greater then size_threshold in length."""
    # find . -type f -size +"${size_threshold}"M -print
    for root, base_dirnames, base_filenames in os.walk('.'):
        for base_filename in base_filenames:
            filename = os.path.join(root, base_filename)
            length = os.path.getsize(filename)
            if length >= size_threshold:
                yield filename


def tail_1(filename: str) -> typing.Optional[str]:
    """Return the last line of filename, sans newline."""
    # This could be done a little more efficiently, but .views files tend to be small anyway
    with open(filename, 'r') as file_:
        lines = file_.readlines()

    if lines:
        return lines[-1].rstrip('\n')
    else:
        sys.stderr.write('{}: empty file: {}\n'.format(sys.argv[0], filename))
        return None


def return_if_start(filename: str, views_filename: str) -> typing.Optional[typing.Tuple[str, str]]:
    """Yield a tuple to report, if last line of views is a 'start'."""
    last_line = tail_1(views_filename)
    if not last_line:
        return None

    fields = last_line.split()

    if fields[6] == 'start':
        return (last_line, filename)
    else:
        return None


def get_unfinished_files() -> typing.Generator[typing.Tuple, None, None]:
    """Yield files that have been started but not finished."""
    for root, base_dirnames, base_filenames in os.walk('.'):
        for base_filename in base_filenames:
            if base_filename.endswith('.views'):
                continue
            filename = os.path.join(root, base_filename)
            views_filename = filename + '.views'
            if os.path.exists(filename) and os.path.exists(views_filename):
                tuple_ = return_if_start(filename, views_filename)
                if tuple_ is None:
                    continue
                yield tuple_


# def get_no_views_files(size_threshold: int) -> typing.Generator[typing.Optional[typing.Tuple], None, None]:
def get_no_views_files(size_threshold: int) -> typing.Iterable[typing.Tuple[float, str, str]]:
    """List files with no .views files."""
    for filename in find_large_files(size_threshold):
        views_filename = filename + '.views'
        if not os.path.exists(views_filename):
            mtime = os.path.getmtime(filename)
            yield (mtime, time.ctime(mtime), filename)


def get_views_timestamps_files(size_threshold: int) -> typing.Generator[typing.Tuple, None, None]:
    """List files with corresponding .views files, along with timestamp data."""
    for filename in find_large_files(size_threshold):
        views_filename = filename + '.views'
        if os.path.exists(views_filename):
            last_line = tail_1(views_filename)
            if last_line is None:
                continue
            tuple_ = (last_line, filename)
            yield tuple_


def to_str_list(list_: typing.Tuple) -> typing.List[str]:
    """Convert list of whatever to list of str's."""
    new_list = []
    for element in list_:
        if isinstance(element, str):
            new_list.append(element)
        else:
            new_list.append(str(element))
    return new_list


def main() -> None:
    """Generate reports about movie collection - main function."""
    no_views = False
    views_timestamps = False
    list_unfinished = False
    size_threshold = 680 * 1024 * 1024

    while sys.argv[1:]:
        if sys.argv[1] == '--no-views':
            no_views = True
        elif sys.argv[1] == '--views-timestamps':
            views_timestamps = True
        elif sys.argv[1] == '--list-unfinished':
            list_unfinished = True
        elif sys.argv[1] == '--size-threshold':
            size_threshold = int(sys.argv[2])
            del sys.argv[1]
        elif sys.argv[1] in ('--help', '-h'):
            usage(0)
        else:
            sys.stderr.write('{}: Unrecognized option: {}\n'.format(sys.argv[0], sys.argv[1]))
            usage(1)
        del sys.argv[1]

    if views_timestamps + no_views + list_unfinished != 1:
        # No report requested, give a usage message
        sys.stderr.write('{}: You must specify exactly one of --no-views, --views-timestamps and '.format(sys.argv[0]))
        sys.stderr.write('--list-unfinished\n')
        usage(1)

    if views_timestamps:
        list_ = list(get_views_timestamps_files(size_threshold))

    if no_views:
        list_ = list(get_no_views_files(size_threshold))

    if list_unfinished:
        list_ = list(get_unfinished_files())

    list_.sort()

    for element in list_:
        print(' '.join(to_str_list(element)))


main()