/******************************************************************************!
 * \file iambic.c
 * \author Sebastien Beaugrand
 * \sa http://beaugrand.chez.com/
 * \copyright CeCILL 2.1 Free Software license
 ******************************************************************************/
#include <avr/io.h>

#define F_CPU 8000000UL
#include <util/delay.h>

// Broches
#define PIN_TONE _BV(PB0)
#define PIN_KEY _BV(PB1)
#define PIN_DOT _BV(PB3)
#define PIN_DASH _BV(PB4)

// Etat du manipulateur
#define STATE_COUNT 159  // 256 - 97
enum {
    STATE_IDLE,
    STATE_DOT,
    STATE_DASH,
    STATE_WAIT_DOT,
    STATE_WAIT_DASH
};

// Etat des palettes
enum {
    PADDLE_NOT_PRESSED,
    PADDLE_PRESSED,
    PADDLE_MAINTENED,
    PADDLE_OPERATED
};

// Tampon
#define BUFF_SIZE 8  // 2^n

/******************************************************************************!
 * \fn main
 ******************************************************************************/
int
main()
{
    // Globales
    uint8_t gBuff[BUFF_SIZE] = {
        0
    };
    uint8_t gRPos = 0;
    uint8_t gWPos = 0;
    uint8_t gState = STATE_IDLE;

    // Pour timer 1
    uint8_t nextState = STATE_IDLE;
    uint8_t dashCount = 0;
    uint8_t stateCount = STATE_COUNT;

    // Locales
    uint8_t dotState = PADDLE_NOT_PRESSED;
    uint8_t dashState = PADDLE_NOT_PRESSED;

    // Ports
    DDRB = PIN_KEY;  // sorties=1
    PORTB = PIN_DOT | PIN_DASH;  // entrees

    // Timer 0
    GTCCR = 0x00;
    OCR0A = 89;  // 700 Hz
    TCCR0A = 0x42;
    TCCR0B = 0x03;

    // Timer 1
    TCNT1 = STATE_COUNT;
    TCCR1 = 0x0e;

    // ADC
    ADMUX = 0x21;
    ADCSRA = 0xf6;
    ADCSRB = 0x00;
    DIDR0 = 1 << ADC1D;

    for (;;) {
        _delay_ms(10);  // 10 = 100 Hz

        if (TIFR & (1 << TOV1)) {
            TIFR |= (1 << TOV1);
            TCNT1 = stateCount;

            gState = nextState;
            if (gState == STATE_IDLE && gBuff[gRPos] != 0) {
                gState = gBuff[gRPos];
                gBuff[gRPos] = 0;
                ++gRPos;
                if (gRPos & BUFF_SIZE) {
                    gRPos = 0;
                }
            }

            switch (gState) {
            case STATE_IDLE:
                // Vitesse
                if ((ADCSRA & (1 << ADIF)) == (1 << ADIF)) {
                    stateCount = 256 - ADCH;
                    ADCSRA |= (1 << ADIF);
                }
                break;
            case STATE_DOT:  // Envoie point
                DDRB |= PIN_TONE;
                PORTB |= PIN_KEY;
                nextState = STATE_WAIT_DOT;
                break;
            case STATE_DASH:  // Envoie trait
                DDRB |= PIN_TONE;
                PORTB |= PIN_KEY;
                ++dashCount;
                if (dashCount >= 3) {
                    dashCount = 0;
                    nextState = STATE_WAIT_DASH;
                } else {
                    nextState = STATE_DASH;
                }
                break;
            case STATE_WAIT_DOT:  // Fin du point
                DDRB &= ~PIN_TONE;
                PORTB &= ~PIN_KEY;
                nextState = STATE_IDLE;
                break;
            case STATE_WAIT_DASH:  // Fin du trait
                DDRB &= ~PIN_TONE;
                PORTB &= ~PIN_KEY;
                nextState = STATE_IDLE;
                break;
            }
        }

        if ((PINB & PIN_DOT) == 0) {
            if (dotState == PADDLE_NOT_PRESSED) {
                dotState = PADDLE_PRESSED;
            } else if (dotState == PADDLE_PRESSED) {
                dotState = PADDLE_MAINTENED;
            }
        } else {
            dotState = PADDLE_NOT_PRESSED;
        }
        if ((PINB & PIN_DASH) == 0) {
            if (dashState == PADDLE_NOT_PRESSED) {
                dashState = PADDLE_PRESSED;
            } else if (dashState == PADDLE_PRESSED) {
                dashState = PADDLE_MAINTENED;
            }
        } else {
            dashState = PADDLE_NOT_PRESSED;
        }
        if (gState == STATE_WAIT_DASH) {
            if (dotState == PADDLE_MAINTENED) {
                gBuff[gWPos] = STATE_DOT;
                ++gWPos;
                if (gWPos & BUFF_SIZE) {
                    gWPos = 0;
                }
                dotState = PADDLE_OPERATED;
                continue;
            }
            if (dashState == PADDLE_MAINTENED) {
                gBuff[gWPos] = STATE_DASH;
                ++gWPos;
                if (gWPos & BUFF_SIZE) {
                    gWPos = 0;
                }
                dashState = PADDLE_OPERATED;
                continue;
            }
        } else if (gState == STATE_WAIT_DOT || gState == STATE_IDLE) {
            if (dashState == PADDLE_MAINTENED) {
                gBuff[gWPos] = STATE_DASH;
                ++gWPos;
                if (gWPos & BUFF_SIZE) {
                    gWPos = 0;
                }
                dashState = PADDLE_OPERATED;
                continue;
            }
            if (dotState == PADDLE_MAINTENED) {
                gBuff[gWPos] = STATE_DOT;
                ++gWPos;
                if (gWPos & BUFF_SIZE) {
                    gWPos = 0;
                }
                dotState = PADDLE_OPERATED;
                continue;
            }
        }
        if (gState == STATE_DOT && dotState == PADDLE_OPERATED) {
            dotState = PADDLE_NOT_PRESSED;
        }
        if (gState == STATE_DASH && dashState == PADDLE_OPERATED) {
            dashState = PADDLE_NOT_PRESSED;
        }
    }
}