extern "C"
{
#include <stdio.h>
#include <curses.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <utime.h>
#include <unistd.h>
#include <signal.h>
}

void sig_exit(int signal)
{
    exit(1);
}

int main(int argc, char* argv[])
{
    /// is terminal ///
    if(!isatty(0) || !isatty(1)) {
        fprintf(stderr, "standard input is not a tty\n");
        return 1;
    }

    /// signal ///
    signal(SIGINT, sig_exit);
    signal(SIGQUIT, sig_exit);
    signal(SIGABRT, sig_exit);
    signal(SIGKILL, sig_exit);
    signal(SIGPIPE, sig_exit);
    signal(SIGALRM, sig_exit);
    signal(SIGTERM, sig_exit);
    signal(SIGHUP, sig_exit);

    /// check ///
    if(argc < 2) {
        fprintf(stderr, "no argument\n");
        exit(1);
    }
    for(int i=1; i<argc; i++) {
       if(access(argv[i], F_OK) != 0) {
           fprintf(stderr, "invalid file name\n");
           exit(1);
       }
    }
    
    /// get file stat///
    struct stat stat_;
    memset(&stat_, 0, sizeof(struct stat));
    if(stat(argv[1], &stat_) < 0) {
        fprintf(stderr, "stat err\n");
        exit(1);
    }

    /// editing mode ///
    enum eEditing { kMode, kDate, kOK };
    eEditing editing = kMode;

    /// mode ///
    mode_t mode = stat_.st_mode;
    int mPCursor = 0;

    /// date ///
    int mDCursor = 0;
    struct tm* mtimetm = (struct tm*)localtime(&stat_.st_mtime);
    
    /// start curses ///
    initscr();
    noecho();
    keypad(stdscr, 1);
    raw();

    clear();

    /// start main loop ///
    int key = -1;
    while(1) {
        /// draw ///
        attron(A_REVERSE);
        mvprintw(0,0, "+++ mattr (permission and date editor) +++");
        attroff(A_REVERSE);

        mvprintw(2, 0, "name      : ");
        for(int i=1; i<argc; i++) {
            printw("%s ", argv[i]);
        }
        
        char permission[10];

        permission[0] = mode & S_IRUSR ? 'r': '-';
        permission[1] = mode & S_IWUSR ? 'w': '-';
        permission[2] = mode & S_IXUSR ? 'x': '-';
        permission[3] = mode & S_IRGRP ? 'r': '-';
        permission[4] = mode & S_IWGRP ? 'w': '-';
        permission[5] = mode & S_IXGRP ? 'x': '-';
        permission[6] = mode & S_IROTH ? 'r': '-';
        permission[7] = mode & S_IWOTH ? 'w': '-';
        permission[8] = mode & S_IXOTH ? 'x': '-';
        permission[9] = 0;

        mvprintw(4,0, "permission: %s\n", permission);
        
        mvprintw(5,0, "date      : %3d %02d-%02d %02d:%02d"
                       , mtimetm->tm_year + 1900, mtimetm->tm_mon+1
                       , mtimetm->tm_mday, mtimetm->tm_hour, mtimetm->tm_min);

        mvprintw(7, 0, "OK -> Enter Key, Cansel -> CTRL+C");

        if(editing == kOK)
            mvprintw(9, 0, "Write OK?(Y/n)");        

        /// cursor ///
        switch(editing) {
        case kMode:
            move(4, 12+mPCursor);
            break;

        case kDate: {
            const int cursor[12] = {
                15, 18, 21, 24, 27
            };
            
            move(5, cursor[mDCursor]);
            }
            break;
        }
        
        refresh();
        
        /// input ///
        key = getch();
        if(key == 3) break;

        if(editing == kMode) {
            if(key == ' ' || key == 16 || key == KEY_UP    // CTRL+P 
                || key == 14 || key == KEY_DOWN)           // CTRL+N
            {
                const int mode_flg[9] = {
                    S_IRUSR, S_IWUSR, S_IXUSR,
                    S_IRGRP, S_IWGRP, S_IXGRP,
                    S_IROTH, S_IWOTH, S_IXOTH
                };
                
                mode = mode ^ mode_flg[mPCursor];
            }
            else if(key == 6 || key == KEY_RIGHT) {    // CTRL+F
                mPCursor++;
                if(mPCursor > 8) mPCursor = 8;
            }
            else if(key == 2 || key == KEY_LEFT) {    // CTRL+B
                mPCursor--;
                if(mPCursor < 0) mPCursor = 0;
            }
            else if(key == 10 || key == 13) {    // CTRL+M CTRL+J
                editing = kDate;
                mDCursor = 0;
            }
        }
        else if(editing == kDate) {
            if(key == 10 || key == 13) {    // CTRL+M CTRL+J
                editing = kOK;
            }
            else if(key == 6 || key == KEY_RIGHT) {    // CTRL+F
                mDCursor++;
                if(mDCursor > 4) mDCursor = 4;
            }
            else if(key == 2 || key == KEY_LEFT) {    // CTRL+B
                mDCursor--;
                if(mDCursor < 0) mDCursor = 0;
            }
            else if(key == 16 || key == KEY_UP) { // CTRL-P
                switch(mDCursor) {
                case 0:
                    mtimetm->tm_year++;
                    break;
                case 1:
                    mtimetm->tm_mon++;
                    break;
                case 2:
                    mtimetm->tm_mday++;
                    break;
                case 3:
                    mtimetm->tm_hour++;
                    break;
                case 4:
                    mtimetm->tm_min++;
                    break;
                }
            }
            else if(key == 14 || key == KEY_DOWN) { // CTRL-N
                switch(mDCursor) {
                case 0:
                    mtimetm->tm_year--;
                    break;
                case 1:
                    mtimetm->tm_mon--;
                    break;
                case 2:
                    mtimetm->tm_mday--;
                    break;
                case 3:
                    mtimetm->tm_hour--;
                    break;
                case 4:
                    mtimetm->tm_min--;
                    break;
                }
            }
        }
        else if(editing == kOK) {
            if(key == 'y' || key == 'Y' || key == 10 || key == 13) {
                break;
            }
            else if(key == 'n' || key == 'N') {
                key = 3;
                break;
            }
        }

        /// rage check ///
        if(mtimetm->tm_year <= 0)
            mtimetm->tm_year = 0;
        else if(mtimetm->tm_year > 8099)
            mtimetm->tm_year = 8099;
        else if(mtimetm->tm_mon < 0)
            mtimetm->tm_mon = 0;
        else if(mtimetm->tm_mon > 11)
            mtimetm->tm_mon = 11;
        else if(mtimetm->tm_mday < 1)
            mtimetm->tm_mday = 1;
        else if(mtimetm->tm_mday > 31)
            mtimetm->tm_mday = 31;
        else if(mtimetm->tm_hour < 0)
            mtimetm->tm_hour = 0;
        else if(mtimetm->tm_hour > 23)
            mtimetm->tm_hour = 23;
        else if(mtimetm->tm_min < 0)
            mtimetm->tm_min = 0;
        else if(mtimetm->tm_min > 59)
            mtimetm->tm_min = 59;
    }
    
    endwin();

    if(key != 3) {
        struct utimbuf tb;
        tb.actime = stat_.st_atime;
        tb.modtime = mktime(mtimetm);
        
        for(int i=1; i<argc; i++) {
            chmod(argv[i], mode);
            utime(argv[i], &tb);
        }
    }

    return 0;
}
