%----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%

:- module midimon.

:- interface.
:- import_module io.

:- pred main(io::di, io::uo) is cc_multi.

%----------------------------------------------------------------------------%
%----------------------------------------------------------------------------%

:- implementation.

:- import_module global.
:- import_module midi.
:- import_module spawn.
:- import_module stream.

:- import_module bool.
:- import_module char.
:- import_module getopt.
:- import_module int.
:- import_module list.
:- import_module maybe.
:- import_module require.
:- import_module string.

%----------------------------------------------------------------------------%

main(!IO) :-
    io.command_line_arguments(Args0, !IO),
    process_options(
        option_ops_multi(short_option, long_option, option_defaults),
        Args0, _Args, MOpts),
    (
        MOpts = ok(Opts),
        lookup_bool_option(Opts, help, Help),
        (
            Help = yes,
            help(!IO)
        ;
            Help = no,
            lookup_maybe_string_option(Opts, input, MInfile),
            open_input(MInfile, InFileOpened, !IO),
            (
                InFileOpened = yes,
                new(Bytes0, !IO),
                new(Messages, !IO),
                spawn((pred(!.IO::di, !:IO::uo) is cc_multi :-
                    read_midi(Bytes0, Messages, !IO)
                ), !IO),
                spawn((pred(!.IO::di, !:IO::uo) is cc_multi :-
                    print_messages(Messages, !IO)
                ), !IO),
                read_input(Bytes0, !IO)
            ;
                InFileOpened = no
            )
        )
    ;
        MOpts = error(Msg),
        io.stderr_stream(StdErr, !IO),
        io.format(StdErr, "%s\n", [s(Msg)], !IO)
    ).

:- pred open_input(maybe(string)::in, bool::out, io::di, io::uo) is det.

open_input(no, Opened, !IO) :-
    io.see_binary("/dev/midi", Res, !IO),
    (
        Res = ok,
        Opened = yes
    ;
        Res = error(Err),
        io.error_message(Err, Msg),
        io.stderr_stream(StdErr, !IO),
        io.format(StdErr, "error opening `/dev/midi': %s\n", [s(Msg)], !IO),
        Opened = no
    ).
open_input(yes(FileName), Opened, !IO) :-
    ( FileName = "-" ->
            % use stdin
        Opened = yes
    ;
        io.see_binary(FileName, Res, !IO),
        (
            Res = ok,
            Opened = yes
        ;
            Res = error(Err),
            io.error_message(Err, Msg),
            io.stderr_stream(StdErr, !IO),
            io.format(StdErr, "error opening `%s': %s\n",
                [s(FileName), s(Msg)], !IO),
            Opened = no
        )
    ).

:- pred read_input(stream(byte)::in, io::di, io::uo) is det.

read_input(Stream, !IO) :-
    io.read_byte(Res0, !IO),
    (
        Res0 = eof,
        end(Stream, !IO)
    ;
        Res0 = error(Err),
        io.error_message(Err, Msg),
        error(Stream, Msg, !IO)
    ;
        Res0 = ok(Byte),
        put(Stream, Byte, !IO),
        read_input(Stream, !IO)
    ).

:- pred print_messages(stream(message)::in, io::di, io::uo) is det.

print_messages(Stream) -->
    get(Stream, Res0),
    (
        { Res0 = ok(Msg) },
        write(Msg), write_string(".\n"),
        print_messages(Stream)
    ;
        { Res0 = end }
    ;
        { Res0 = error(Msg) },
        write_string(Msg), nl
    ).

%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%

:- type option_table == option_table(option).
:- type maybe_option_table == maybe_option_table(option).

    % The master list of options.
    %
:- type option
    --->    help
    ;       input.

:- pred long_option(string::in, option::out) is semidet.

long_option("help",       help).
long_option("input-file", input).

:- pred short_option(char::in, option::out) is semidet.

short_option('h', help).
short_option('i', input).

:- pred option_defaults(option::out, option_data::out) is multi.

option_defaults(help,  bool(no)).
option_defaults(input, maybe_string(no)).

:- pred help(io::di, io::uo) is det.

help(!IO) :-
    io.write_strings([
"usage: midimon [--help|-h] [--input-file|-i <filename>]\n",
"   --help|-h       print this help message.\n",
"   --input-file|-i <file>  read from <file> (default is /dev/midi).\n"
    ], !IO).

%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
