/* 
 * File:   alarm_v2.c
 * Author: corrado
 *
 * Created on November 30, 2015, 9:31 AM
 */

#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

void setup(void);

#define TIME_200MS  -12500

// ---------------------------------------------------------
// PERIPHERAL SETUP AND MANAGEMENT

void setup_peripherals(void)
{
    // configure ports
    TRISBbits.TRISB0 = 0;
    TRISBbits.TRISB1 = 0;
    TRISBbits.TRISB2 = 0;
    TRISBbits.TRISB3 = 0;
    TRISBbits.TRISB4 = 0;
    TRISBbits.TRISB5 = 0;
    TRISAbits.TRISA2 = 1;
    TRISAbits.TRISA3 = 1;
    TRISCbits.TRISC2 = 1;

    // configure timer 0
    T0CONbits.TMR0ON = 0; // stop the timer
    T0CONbits.T08BIT = 0; // timer configured as 16-bit
    T0CONbits.T0CS = 0; // use system clock
    T0CONbits.PSA = 0; // use prescaler
    T0CONbits.T0PS = 0b111; // prescaler 1:256 ('0b' is a prefix for binary)
    TMR0 = TIME_200MS; // initial timer value
    INTCONbits.T0IF = 0; // clear the overflow bit initially

    // configure UART
    // setup UART
    TRISCbits.TRISC6 = 0;   // TX as output
    TRISCbits.TRISC7 = 1;   // RX as input
    LATCbits.LATC6 = 1;

    TXSTA1bits.SYNC = 0; // Async operation
    TXSTA1bits.TX9 = 0; // No tx of 9th bit
    TXSTA1bits.TXEN = 1; // Enable transmitter
    RCSTA1bits.RC9 = 0; // No rx of 9th bit
    // Setting for 19200 BPS
    BAUDCON1bits.BRG16 = 0; // Divisor at 8 bit
    TXSTA1bits.BRGH = 0; // No high-speed baudrate
    SPBRG1 = 51; // divisor value for 19200

    RCSTA1bits.CREN = 1; // Enable receiver
    RCSTA1bits.SPEN = 1; // Enable serial port

    PIE1bits.TX1IE = 0; // disable TX hardware interrupt
    PIE1bits.RC1IE = 0; // disable RX hardware interrupt

    // Configure interrupt circuits
    INTCONbits.GIE = 1; // global enable interrupts
    INTCONbits.PEIE = 1; // peripheral enable interrupts
    RCONbits.IPEN = 0; // no priorities

    INTCONbits.T0IE = 1; // enable interrupt on TMR0

    T0CONbits.TMR0ON = 1; // start the timer
}

void putch(char c) {
    // wait the end of transmission
    while (TXSTA1bits.TRMT == 0) {};
    TXREG1 = c; // send the new byte
}

char read_char(void)
{
    while (PIR1bits.RC1IF == 0) {
        if (RCSTA1bits.OERR == 1) {
            RCSTA1bits.OERR = 0;
            RCSTA1bits.CREN = 0;
            RCSTA1bits.CREN = 1;
        }
    }
    return RCREG1;
}

int nb_read_char(char * c)
{
    if (RCSTA1bits.OERR == 1) {
        RCSTA1bits.OERR = 0;
        RCSTA1bits.CREN = 0;
        RCSTA1bits.CREN = 1;
    }
    if (PIR1bits.RC1IF == 1) {
        *c = RCREG1;
        return 1;
    }
    else
        return 0;
}

void read_line(char * s, int max_len)
{
    int i = 0;
    for(;;) {
        char c = read_char();
        if (c == 13) {
            putchar(c);
            putchar(10);
            s[i] = 0;
            return;
        }
        else if (c == 8) {
            if (i > 0) {
                putchar(c);
                putchar(' ');
                putchar(c);
                --i;
            }
        }
        else if (c >= 32) {
            if (i < max_len) {
                putchar(c);
                s[i] = c;
                ++i;
            }
        }
    }
}

// ----------------------------------------------------------------
// ALARM MANAGEMENT
//

#define LED_STATE_DISABLED() { \
    LATBbits.LATB0 = 0; \
    LATBbits.LATB1 = 1; \
    LATBbits.LATB2 = 1; \
    LATBbits.LATB3 = 1; \
    LATBbits.LATB4 = 1; \
}

#define LED_STATE_PREACTIVATION() { \
    LATBbits.LATB0 = 0; \
    LATBbits.LATB1 = 1; \
    LATBbits.LATB2 = 1; \
    LATBbits.LATB4 = 1; \
}

#define LED_STATE_ACTIVATED() { \
    LATBbits.LATB0 = 1; \
    LATBbits.LATB1 = 0; \
    LATBbits.LATB2 = 1; \
    LATBbits.LATB3 = 1; \
    LATBbits.LATB4 = 1; \
}

#define LED_STATE_PRE_ALARM() { \
    LATBbits.LATB0 = 1; \
    LATBbits.LATB1 = 0; \
    LATBbits.LATB2 = 0; \
    LATBbits.LATB4 = 1; \
}


#define LED_STATE_ALARM() { \
    LATBbits.LATB0 = 1; \
    LATBbits.LATB1 = 0; \
    LATBbits.LATB2 = 0; \
    LATBbits.LATB3 = 1; \
    LATBbits.LATB4 = 0; \
}

#define PRE_ACTIVATION_TIME   25
#define PRE_ALARM_TIME        100

#define STATE_DISABLED                  0
#define STATE_PRE_ACTIVATION_PASSWORD   1
#define STATE_PRE_ACTIVATION            2
#define STATE_ACTIVATED                 3
#define STATE_DEACTIVATION_PASSWORD     4
#define STATE_PRE_ALARM                 5
#define STATE_ALARM                     6

#define EVENT_NONE              0
#define ASK_PASSWORD            1

int fsm_state = STATE_DISABLED;
int event = EVENT_NONE;
int pre_activation_timer, pre_alarm_timer;
int pre_activation_duration = PRE_ACTIVATION_TIME;
int pre_alarm_duration = PRE_ALARM_TIME;
char current_password[5];

void interrupt isr()
{
    if (INTCONbits.T0IF == 1) {
        TMR0 = TIME_200MS;
        INTCONbits.T0IF = 0;
        switch (fsm_state) {
            case STATE_DISABLED:
                if (PORTAbits.RA3 == 0) {
                    fsm_state = STATE_PRE_ACTIVATION_PASSWORD;
                }
                break;
            case STATE_PRE_ACTIVATION:
                LATBbits.LATB3 = !LATBbits.LATB3;
                ++pre_activation_timer;
                if (pre_activation_timer == pre_activation_duration) {
                    fsm_state = STATE_ACTIVATED;
                    LED_STATE_ACTIVATED();
                }
                break;
            case STATE_ACTIVATED:
                if (PORTCbits.RC2 == 0) {
                    fsm_state = STATE_PRE_ALARM;
                    LED_STATE_PRE_ALARM();
                    pre_alarm_timer = 0;
                }
                else if (PORTAbits.RA2 == 0) {
                    fsm_state = STATE_DEACTIVATION_PASSWORD;
                }
                break;
            case STATE_DEACTIVATION_PASSWORD:
                if (PORTCbits.RC2 == 0) {
                    fsm_state = STATE_PRE_ALARM;
                    LED_STATE_PRE_ALARM();
                    pre_alarm_timer = 0;
                }
                break;
            case STATE_PRE_ALARM:
                LATBbits.LATB3 = !LATBbits.LATB3;
                ++pre_alarm_timer;
                if (pre_alarm_timer == pre_alarm_duration) {
                    fsm_state = STATE_ALARM;
                    LED_STATE_ALARM();
                }
                break;
        }
    }
}

#define PASSWORD_BAD        0
#define PASSWORD_OK         1

int verify_password(void)
{
    int i;
    char pwd[5];
    printf("PASSWORD:");

    for (i = 0; i < 4;i++) {
        char c;
        do {
            c = read_char();
        }
        while (!isdigit(c));
        putchar('*');
        pwd[i] = c;
    }
    pwd[4] = 0;
    if (strcmp(pwd, current_password) == 0) {
        printf("-OK\n\r");
        return PASSWORD_OK;
    }
    else {
        printf("-FAIL!\n\r");
        return PASSWORD_BAD;
    }
}

void alarm_fsm(void)
{
    char c;
    switch(fsm_state) {
        case STATE_DISABLED:
            if (nb_read_char(&c) && c == '!') {
                setup();
            }
            break;
        case STATE_PRE_ACTIVATION_PASSWORD:
            if (verify_password() == PASSWORD_OK) {
                fsm_state = STATE_PRE_ACTIVATION;
                LED_STATE_PREACTIVATION();
                pre_activation_timer = 0;
                printf("ACTIVATING...\n\r");
            }
            else {
                fsm_state = STATE_DISABLED;
                LED_STATE_DISABLED();
            }
            break;
        case STATE_DEACTIVATION_PASSWORD:
        case STATE_PRE_ALARM:
        case STATE_ALARM:
            if (verify_password() == PASSWORD_OK) {
                fsm_state = STATE_DISABLED;
                LED_STATE_DISABLED();
                printf("ALARM OFF\n\r");
            }
            break;
    }
}

/*
 * 
 */

void setup(void)
{
    char line[81];
    int num_of_tokens;
    char *tokens[20];

    for (;;) {
    read_the_line:
        printf("SETUP>");
        read_line(line, 80);

        num_of_tokens = 0;
        tokens[num_of_tokens] = strtok(line, " ");
        while (tokens[num_of_tokens] != NULL) {
            ++num_of_tokens;
            tokens[num_of_tokens] = strtok(NULL, " ");
        }

        if (num_of_tokens == 0)
            continue;

        if (strcmp(tokens[0], "password") == 0 && num_of_tokens == 2) {
            if (strlen(tokens[1]) != 4)
                printf("BAD PASSWORD LENGTH\n\r");
            else {
                int i;
                for (i = 0;i < 4;i++) {
                    if (!isdigit(tokens[1][i])) {
                        printf("BAD PASSWORD\n\r");
                        goto read_the_line;
                    }
                }
                strcpy(current_password, tokens[1]);
            }
        }
        else if (strcmp(tokens[0], "out-time") == 0 && num_of_tokens == 2) {
            int out_time = atoi(tokens[1]);
            if (out_time < 5 || out_time > 20)
                printf("Invalid value\n\r");
            else
                pre_activation_duration = out_time * 5;
        }
        else if (strcmp(tokens[0], "in-time") == 0 && num_of_tokens == 2) {
            int in_time = atoi(tokens[1]);
            if (in_time < 5 || in_time > 40)
                printf("Invalid value\n\r");
            else
                pre_alarm_duration = in_time * 5;
        }
        else if (!strcmp(tokens[0], "exit")) {
            return;
        }
        else
            printf("BAD COMMAND\n\r");
    }
}

int main(int argc, char** argv) {

    setup_peripherals();

    strcpy(current_password, "1234");

    LED_STATE_DISABLED();
    for (;;) {
        alarm_fsm();
    }

    return (EXIT_SUCCESS);
}
