#!/usr/bin/perl

use strict;
use warnings;
use feature ":5.10"; # given ... when
use DBI;
use Term::ReadKey; # don't echo password
use Term::ANSIColor qw(:constants);

my $db_name = $ARGV[0];
my $db_login = $ARGV[1];
my $db_pass = $ARGV[2];

sub setup_db {
    print BOLD, RED, " Type ", YELLOW, "y", RED, " to reset database: ", RESET;
    open(my $in, "<-") or die "setup_db: cannot open stdin.\n";
    my $cmd = <$in>;
    my $do_setup = -1;
    given(fix_input($cmd)) {
        when(m/(^y$|^yes$)/) { $do_setup = 1; }
        default              { $do_setup = 0; }
    }
    my $db =
        DBI->connect("DBI:mysql:$db_name", "$db_login", "$db_pass")
        or die "setup_db: couldn't connect to database ($DBI::errstr)";
    if ($do_setup) {
        print BOLD, YELLOW;
        print " Setting up: ";
        print "login table, ";
        $db->do('DROP TABLE IF EXISTS users');
        $db->do('CREATE TABLE
                  users (id   INT PRIMARY KEY,
                         user VARCHAR(16),
                         pass VARCHAR(16))');
        print "notes table, ";
        $db->do('DROP TABLE IF EXISTS notes');
        $db->do('CREATE TABLE
                  notes (id   INT PRIMARY KEY,
                         user INT REFERENCES users(id),
                         note VARCHAR(80))');
        print "test user, ";
        my $user_id = 1;
        $db->do("INSERT INTO users values ($user_id, 'incal', 'doom')");
        print "test notes ... done\n", RESET;
        $db->do("INSERT INTO notes values (1, $user_id, 'note 1')");
        $db->do("INSERT INTO notes values (2, $user_id, 'note 2')");
    }
    return $db;
}

sub add_user ($) {
    my $db = shift;
    open(my $in, "<-") or die "add_user: cannot open stdin.\n";
    my $new_user_done = 0;
    print BOLD, RED, " Type ", YELLOW, "y", RED, " to add a new user: ", RESET;
    my $create_user = <$in>;
    $create_user = fix_input($create_user);
    if ($create_user ne "y") { return; }
    my $new_user;
    until ($new_user_done) {
        print " new user username: ";
        $new_user = <$in>;
        $new_user = fix_input($new_user);
        my $query = "SELECT id FROM users WHERE user=\'$new_user\'";
        my $sth = $db->prepare($query);
        $sth->execute();
        my @result = $sth->fetchrow_array();
        if ($result[0]) { print " Error: user exists.\n"; }
        else { $new_user_done = 1; }
    }
    my $pass_done = 0;
    until ($pass_done) {
        print " new user password: ";
        disable_echo();
        my $pass = <$in>;
        $pass = fix_input($pass);
        print "\n new user password (again): ";
        my $pass_rep = <$in>;
        $pass_rep = fix_input($pass_rep);
        enable_echo();
        if ($pass eq $pass_rep) {
            my $next = next_id("users", $db);
            my $ins =
               "INSERT INTO users values ($next,\"$new_user\", \"$pass\")";
            $db->do($ins);
            $pass_done = 1;
        }
        else { print " Error: passwords are not identical.\n"; }
    }
    print "\n";
}

sub next_id ($$) {
    my $table = shift;
    my $db = shift;
    my $query = "SELECT * FROM $table";
    my $sth = $db->prepare($query);
    $sth->execute();
    my $next = 1;
    while (my @result = $sth->fetchrow_array()) {
        if ($next != $result[0]) { return $next; }
        $next++;
    }
    return $next;
}

sub get_user ($$) {
    my $user = shift;
    my $db = shift;
    my $query = "SELECT * FROM users WHERE user=\'$user\'";
    my $sth = $db->prepare($query);
    $sth->execute();
    return $sth->fetchrow_array();
}

sub check_user ($$$) {
    my $user = shift;
    my $pass = shift;
    my $db = shift;
    print "\n Checking $user ... ";
    my @user_data = get_user($user, $db);
    my $correct_pass = $user_data[2]; # constant?
    if (@user_data and ($pass eq $correct_pass)) {
        print "OK, you are logged in.\n";
        return 1;
    }
    else {
        print "No such user (or incorrect password).\n";
        print " (Hit Ctrl-C to quit.)\n";
        return 0;
    }
}

sub no_note_message {
    print " You have not set such a note.\n";
    print " (Hint: View notes with \"view\".)\n";
}

sub logged_in ($$) {
    my $quit = 0;
    my $user = shift;
    my $db = shift;

    my @user_data = get_user($user, $db);
    my $user_id = $user_data[0];

    open(my $in, "<-") or die "logged_in: cannot open stdin.\n";
    my $cmd;
    until ($quit) {
        print " $user\@$db_name> ";
        $cmd = <$in>;
        given (fix_input($cmd)) {
            when(m/(^e$|^edit$)/) {
                print " Edit what note: ";
                my $edit_note = <$in>;
                $edit_note = fix_input($edit_note);
                if ($edit_note) {
                    my @edit_note_data = get_note($edit_note, $user_id, $db);
                    my $edit_note_id = $edit_note_data[0];
                    if ($edit_note_id) {
                        my $edit_note_text = $edit_note_data[2];
                        print BOLD, RED, " new text: ", RESET;
                        system("writevt /dev/tty \"$edit_note_text\"");
                        my $new_text = <$in>;
                        $new_text = fix_input($new_text);
                        my $set_text = "UPDATE notes SET note = \"$new_text\" WHERE id = $edit_note_id";
                        $db->do($set_text);
                    }
                    else { no_note_message(); }
                }

            }
            when(m/(^v$|^view$)/) {
                print " View notes of what user [enter = yourself]: ";
                my $view_user = <$in>;
                $view_user = fix_input($view_user);
                my $view_user_id;
                if ($view_user) {
                    my @view_user_data = get_user($view_user, $db);
                    $view_user_id = $view_user_data[0]; # constant?
                }
                else {
                    $view_user = $user;
                    $view_user_id = $user_id;
                }
                if ($view_user_id) {
                    print BOLD, BLUE, " Notes of $view_user:\n";
                    print " ID\tNOTE\n";
                    my $view_query =
                        "SELECT * FROM notes WHERE user=$view_user_id";
                    my $view_sth = $db->prepare($view_query);
                    $view_sth->execute();
                    while (my @view_result = $view_sth->fetchrow()) {
                        print " $view_result[0]\t$view_result[2]\n";
                    }
                    print RESET;
                }
                else { print BOLD, RED, " No such user!\n", RESET; }
            }
            when(m/(^n$|^new$)/) {
                print " New note (end with enter): ";
                my $note = <$in>;
                $note = fix_input($note);
                my $next = next_id("notes", $db);
                my $values = "($next, $user_id, \"$note\")";
                my $insert_note = "INSERT INTO notes values $values";
                $db->do($insert_note);
                print " Note added.\n";
            }
            when(m/(^d$|^delete$)/) {
                print " Delete what note (zero to abort): ";
                my $rm_note = <$in>;
                $rm_note = fix_input($rm_note);
                if ($rm_note) {
                    if (get_note($rm_note, $user_id, $db)) {
                        $db->do("DELETE FROM notes WHERE id=$rm_note");
                        print " Note deleted.\n";
                    }
                    else { no_note_message(); }
                }
            }
            when(m/(^h$|^help$)/) {
                print " Commands: view, new, delete, help, and quit\n";
                print " (or the inital character: v for view, etc.)\n";
            }
            when(m/(^q$|^quit$)/) { $quit = 1; }
            default { print " No such command (try \"help\").\n"; }
        }
    }
}

sub get_note ($$$) {
    my $note = shift;
    my $user_id = shift;
    my $db = shift;
    my $cond = "id=$note and user=$user_id";
    my $note_test = "SELECT * FROM notes WHERE $cond";
    my $sth = $db->prepare($note_test);
    $sth->execute();
    return $sth->fetchrow_array();
}

sub fix_input ($) {
    my $input = shift;
    $input =~ s/\n//g;
    return $input;
}

sub disable_echo {
    # use whatever works
    ReadMode('noecho');
    # system "stty -echo";
}

sub enable_echo {
    # use whatever works
    ReadMode('normal');
    # system "stty echo";
}

sub intro ($) {
    my $db = shift;
    print " Welcome to $db_name\n";
    open(my $in, "<-") or die "intro: cannot open stdin.\n";

    my $user_logged_in = 0;
    until ($user_logged_in) {
        print " username: ";
        my $name = <$in>;
        $name = fix_input($name);
        print " password: ";
        disable_echo();
        my $pass = <$in>;
        $pass = fix_input($pass);
        enable_echo();
        if (check_user $name, $pass, $db) {
            $user_logged_in = 1;
            logged_in($name, $db);
            print BOLD, RED, " type ", YELLOW, "y", RED, " to log in again: ", RESET;
            my $new_login = <$in>;
            $new_login = fix_input($new_login);
            if ($new_login eq "y") { $user_logged_in = 0; }
        }
    }
}

sub outro ($) {
    my $db = shift;
    $db->disconnect();
    print " Bye!\n";
}

my $db = setup_db();
add_user($db);
intro($db);
outro($db);
