/* 
 * File:   alarm.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 PRE_ACTIVATION_TIME   25
#define PRE_ALARM_TIME        100

#define STATE_DISABLED          0
#define STATE_PRE_ACTIVATION    1
#define STATE_ACTIVATED         2
#define STATE_PRE_ALARM         3
#define STATE_ALARM             4

int fsm_state = STATE_DISABLED;
int pre_activation_timer, pre_alarm_timer;
char current_password[5];

void interrupt isr()
{
    if (INTCONbits.T0IF == 1) {
        TMR0 = TIME_200MS;
        INTCONbits.T0IF = 0;
        switch (fsm_state) {
            case STATE_PRE_ACTIVATION:
                LATBbits.LATB3 = !LATBbits.LATB3;
                ++pre_activation_timer;
                if (pre_activation_timer == PRE_ACTIVATION_TIME)
                    fsm_state = STATE_ACTIVATED;
                break;
            case STATE_PRE_ALARM:
                LATBbits.LATB3 = !LATBbits.LATB3;
                ++pre_alarm_timer;
                if (pre_alarm_timer == PRE_ALARM_TIME)
                    fsm_state = STATE_ALARM;
                break;
        }
    }
}

#define PASSWORD_BAD        0
#define PASSWORD_OK         1
#define SENSOR_ACTIVATED    2
#define NEW_STATE           3

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

    for (i = 0; i < 4;i++) {
        char c;
        do {
            if (check_sensor) {
                for(;;) {
                    if (nb_read_char(&c))
                        break;
                    if (PORTCbits.RC2 == 0) {
                        printf("\n\r");
                        return SENSOR_ACTIVATED;
                    }
                }
            }
            else if (check_new_state) {
                for(;;) {
                    if (nb_read_char(&c))
                        break;
                    if (fsm_state == STATE_ALARM) {
                        printf("\n\r");
                        return NEW_STATE;
                    }
                }
            }
            else
                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 (PORTAbits.RA3 == 0) {
                if (verify_password(0,0) == PASSWORD_OK) {
                    fsm_state = STATE_PRE_ACTIVATION;
                    pre_activation_timer = 0;
                    printf("ACTIVATING...\n\r");
                }
                else
                    fsm_state = STATE_DISABLED;
            }
            if (nb_read_char(&c) && c == '!') {
                setup();
            }
            break;
        case STATE_ACTIVATED:
            if (PORTAbits.RA2 == 0) {
                switch (verify_password(1,0)) {
                    case PASSWORD_OK:
                        fsm_state = STATE_DISABLED;
                        printf("ALARM OFF\n\r");
                        break;
                    case SENSOR_ACTIVATED:
                        fsm_state = STATE_PRE_ALARM;
                        pre_alarm_timer = 0;
                        break;
                }
            }
            if (PORTCbits.RC2 == 0) {
                fsm_state = STATE_PRE_ALARM;
                pre_alarm_timer = 0;
            }
            break;
        case STATE_PRE_ALARM:
            switch (verify_password(0,1)) {
                case PASSWORD_OK:
                    fsm_state = STATE_DISABLED;
                    printf("ALARM OFF\n\r");
                    break;
                case NEW_STATE:
                    fsm_state = STATE_ALARM;
                    break;
            }
            break;
        case STATE_ALARM:
            if (verify_password(0,0) == PASSWORD_OK) {
                fsm_state = STATE_DISABLED;
                printf("ALARM OFF\n\r");
            }
            break;
    }
}

void signal_leds(void)
{
    switch(fsm_state) {
        case STATE_DISABLED:
            LATBbits.LATB0 = 0;
            LATBbits.LATB1 = 1;
            LATBbits.LATB2 = 1;
            LATBbits.LATB3 = 1;
            LATBbits.LATB4 = 1;
            break;
        case STATE_PRE_ACTIVATION:
            LATBbits.LATB0 = 0;
            LATBbits.LATB1 = 1;
            LATBbits.LATB2 = 1;
            LATBbits.LATB4 = 1;
            break;
        case STATE_ACTIVATED:
            LATBbits.LATB0 = 1;
            LATBbits.LATB1 = 0;
            LATBbits.LATB2 = 1;
            LATBbits.LATB3 = 1;
            LATBbits.LATB4 = 1;
            break;
        case STATE_PRE_ALARM:
            LATBbits.LATB0 = 1;
            LATBbits.LATB1 = 0;
            LATBbits.LATB2 = 0;
            LATBbits.LATB4 = 1;
            break;
        case STATE_ALARM:
            LATBbits.LATB0 = 1;
            LATBbits.LATB1 = 0;
            LATBbits.LATB2 = 0;
            LATBbits.LATB3 = 1;
            LATBbits.LATB4 = 0;
            break;
    }
}
/*
 * 
 */

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

    for (;;) {
        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
                strcpy(current_password, tokens[1]);
        }
        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");

    for (;;) {
        alarm_fsm();
        signal_leds();
    }

    return (EXIT_SUCCESS);
}
