/** @file   functions.c
 *  @brief  Diverse Funktionen
 *  @author Tom, DL7BJ
 */

#include "functions.h"

// EEProm Speicher
uint8_t Dummy;
uint8_t ee_Dummy EEMEM = 0x55;               // Dummy for Address 0
uint8_t ee_TRX EEMEM = 0;                    // TRX 1 oder 2 oder beide
uint8_t ee_KeyerMode EEMEM = 1;              // Iambic A, Iambic B oder Ultimatic
uint8_t ee_SidetoneEnabled EEMEM = 1;        // Mithörton eingeschaltet
uint8_t ee_WpMBpM EEMEM = 0;                 // WpM oder BpM Anzeige
uint8_t ee_Reverse EEMEM = 0;                // linkes/rechtes Paddle vertauschen
uint8_t ee_SpeedRatioEnabled EEMEM = 0;      // Ratio von der Geschwindigkeit abhängig
uint8_t ee_Ratio EEMEM = 30;                 // Punkt/Strich Verhältnis 1:3
uint8_t ee_Memory EEMEM = 0;                 // Punkt/Strich Speicher
uint16_t ee_SidetoneFreq EEMEM = 600;        // Frequenz des Mithörtons
uint8_t ee_WpM EEMEM = 12;                   // Geschwindigkeit WpM
uint8_t ee_RiseTime EEMEM = 5;               // Anstiegszeit Sinuston
uint8_t ee_RiseTimeCounter EEMEM = 5;        // Anzahl Sinusschwingungen für den Anstieg
uint8_t ee_DebounceTime EEMEM = 6;           // Entprellzeit für Straight Key Eingang
// Stringkonstanten für VT100 Terminals
const char CLRSCR[]        = "\033[2J";

/** @brief  Ein paar Millisekunden warten
 *
 *  @param  Zeit in Millisekunden, die gewartet werden soll
 *  @return void
 */
void DelayMilliSeconds(uint16_t ms)
{
    volatile static uint16_t ctr;
    ResetMilliSeconds();
    do
    {
        ATOMIC_BLOCK(ATOMIC_FORCEON)
        {
            ctr = t_delayms;
        }
    } while (ctr < ms);
}
/** @brief  Wert des Zählers für Millisekunden lesen
 * 
 *  @param  none
 *  @return milliseconds
 */
uint16_t GetMilliSeconds(void)
{
    volatile static uint16_t ctr;
    ATOMIC_BLOCK(ATOMIC_FORCEON)
    {
        ctr = t_delayms;
    }
    return ctr;
}
/** @brief  Setzt den Zähler für Millisekunden auf 0
 * 
 *  @param  none
 *  @return none
 */
void ResetMilliSeconds(void)
{
    ATOMIC_BLOCK(ATOMIC_FORCEON)
    {
        t_delayms = 0;
    } 
}
/** @brief  SREG wieder herstellen und Interrupts erlauben
 *  @param  none
 *  @return none
 */
void IntEnable(void)
{
  SREG = sreg_tmp;
  sei();
}
/** @brief  SREG sichern und Interrupts verbieten
 *  @param  none
 *  @return none
 */
void IntDisable(void)
{
  sreg_tmp = SREG;
  cli();
}
/** @brief  Pin Change Interrupt abschalten
 *  @param  none
 *  @return none
 */
void DisablePinChangeIRQ(void)
{
    cbi(PCICR,PCIE2);
}
/** @brief  Pin Change Interrupt einschalten
 *  @param  none
 *  @return none
 */
void EnablePinChangeIRQ(void)
{
    sbi(PCICR,PCIE2);
}
/** @brief  EEPROM schreiben
 *  @param  none
 *  @return none
 */
void WriteEEprom(void)
{
    IntDisable();
    eeprom_write_byte(&ee_Dummy,0x55);
    eeprom_write_byte(&ee_WpM, bConfig.WpM);
    eeprom_write_byte(&ee_KeyerMode, bConfig.KeyerMode);
    eeprom_write_word(&ee_SidetoneFreq, bConfig.SidetoneFreq);
    eeprom_write_byte(&ee_TRX, bConfig.TRX);
    eeprom_write_byte(&ee_SidetoneEnabled, bConfig.SidetoneEnabled);
    eeprom_write_byte(&ee_WpMBpM, bConfig.WpMBpM);
    eeprom_write_byte(&ee_Reverse, bConfig.Reverse);
    eeprom_write_byte(&ee_SpeedRatioEnabled, bConfig.SpeedRatioEnabled);
    eeprom_write_byte(&ee_Ratio, bConfig.Ratio);
    eeprom_write_byte(&ee_Memory, bConfig.Memory);
    eeprom_write_byte(&ee_RiseTime, bConfig.RiseTime);
    eeprom_write_byte(&ee_RiseTimeCounter, bConfig.RiseTimeCounter);
    eeprom_write_byte(&ee_DebounceTime, bConfig.DebounceTime);
    IntEnable();
}

void ReadEEpromWpM(void)
{
    IntDisable();
    bConfig.WpM = eeprom_read_byte(&ee_WpM);
    IntEnable();
}

void WriteEEpromWpM(void)
{
    IntDisable();
    eeprom_write_byte(&ee_WpM, bConfig.WpM);
    IntEnable();
}
/** @brief  Standardeinstellungen (bei neuem Controller)
 *
 *  @param  none
 *  @return none
 */
void SetEEprom(void)
{
    Dummy = 0x55;
    bConfig.WpM = 15;
    bConfig.KeyerMode = 2;
    bConfig.SidetoneFreq = 600;
    bConfig.TRX = 1;
    bConfig.SidetoneEnabled = 1;
    bConfig.WpMBpM = 1;
    bConfig.Reverse = 0;
    bConfig.SpeedRatioEnabled = 0;
    bConfig.Ratio = 30;
    bConfig.Memory = 0;
    bConfig.RiseTime = 5;
    bConfig.RiseTimeCounter = 5;
    bConfig.DebounceTime = 5;
    WriteEEprom();
}
/** @brief  EEprom lesen
 *  @param  none
 *  @return none
 */
void ReadEEprom(void)
{
    IntDisable();
    Dummy = eeprom_read_byte(&ee_Dummy);
    IntEnable();
    if(Dummy != 0x55)
    {
        SetEEprom();
    }
    IntDisable();
    bConfig.WpM = eeprom_read_byte(&ee_WpM);
    bConfig.KeyerMode = eeprom_read_byte(&ee_KeyerMode);
    bConfig.SidetoneFreq = eeprom_read_word(&ee_SidetoneFreq);
    bConfig.TRX = eeprom_read_byte(&ee_TRX);
    bConfig.SidetoneEnabled = eeprom_read_byte(&ee_SidetoneEnabled);
    bConfig.WpMBpM = eeprom_read_byte(&ee_WpMBpM);
    bConfig.Reverse = eeprom_read_byte(&ee_Reverse);
    bConfig.SpeedRatioEnabled = eeprom_read_byte(&ee_SpeedRatioEnabled);
    bConfig.Ratio = eeprom_read_byte(&ee_Ratio);
    bConfig.Memory = eeprom_read_byte(&ee_Memory);
    bConfig.RiseTime = eeprom_read_byte(&ee_RiseTime);
    bConfig.RiseTimeCounter = eeprom_read_byte(&ee_RiseTimeCounter);
    bConfig.DebounceTime = eeprom_read_byte(&ee_DebounceTime);
    IntEnable();
}
/** @brief  Aktuelle Einstellungen über serielle Schnittstelle ausgeben
 *  @param  none
 *  @return none
 */
void SerialInfo(void)
{
    sprintf(sdebug,"WpM             : %i\r\n", bConfig.WpM);
    SerialWriteString(sdebug);
    sprintf(sdebug,"KeyerMode       : %i\r\n", bConfig.KeyerMode);
    SerialWriteString(sdebug);
    sprintf(sdebug,"SidetoneFreq    : %u\r\n", bConfig.SidetoneFreq);
    SerialWriteString(sdebug);
    sprintf(sdebug,"TRX             : %i\r\n", bConfig.TRX);
    SerialWriteString(sdebug);
    sprintf(sdebug,"SidetoneEnabled : %i\r\n", bConfig.SidetoneEnabled);
    SerialWriteString(sdebug);
    sprintf(sdebug,"Ratio           : %i\r\n", bConfig.Ratio);
    SerialWriteString(sdebug);
    sprintf(sdebug,"WpMBpM          : %i\r\n", bConfig.WpMBpM);
    SerialWriteString(sdebug);
    sprintf(sdebug,"Reverse         : %i\r\n", bConfig.Reverse);
    SerialWriteString(sdebug);
    sprintf(sdebug,"Memory          : %i\r\n", bConfig.Memory);
    SerialWriteString(sdebug);
    sprintf(sdebug,"SpeedRatio      : %i\r\n", bConfig.SpeedRatioEnabled);
    SerialWriteString(sdebug);
    sprintf(sdebug,"RiseTime        : %i\r\n", bConfig.RiseTime);
    SerialWriteString(sdebug);
    sprintf(sdebug,"RiseTimeCounter : %i\r\n", bConfig.RiseTimeCounter);
    SerialWriteString(sdebug);
    sprintf(sdebug,"DebounceTime    : %i\r\n", bConfig.DebounceTime);
    SerialWriteString(sdebug);
}
/** @brief  Versionsinformation über serielle Schnittstelle ausgeben
 *  @param  none
 *  @return none
 */
void SerialAbout(void)
{
    sprintf(sdebug,"%s", CLRSCR);
    SerialWriteString(sdebug);
    sprintf(sdebug,"%s %s %s\r\n\r\n", PRG,VER,CALL);
    SerialWriteString(sdebug);
}
/** @brief  Ein Zeichen über serielle Schnittstelle ausgeben
 *  @param  Zeichen 
 *  @return none
 */
void SerialWriteChar(unsigned char data)
{
    while(!(UCSR0A & (1<<UDRE0)));
    UDR0 = data;
}
/** @brief  Eine Zeichenkette über serielle Schnittstelle ausgeben
 *  @param  Zeiger auf Zeichenkette, die mit 0x00 abgeschlossen sein muss
 *  @return none
 */
void SerialWriteString(char *s)
{
    while(*s != 0x00)
        SerialWriteChar(*s++);
}

/** @brief   Read port pin of morse keys
 *
 *  This function reads the input of
 *  the Paddle or Straight key.
 *  @params: Pin to read
 *  @return: State of pin
*/
uint8_t ReadKeyPin(uint8_t pin)
{
    return(PIND & (1<<pin));
}
/** @brief  Tonausgabe einschalten
 *  @param  none
 *  @return none
 */
void SidetoneOn(void)
{
    bState.SidetoneOff = 0;
    StateRiseTime = bConfig.RiseTime;
    StateRiseTimeCounter = 0;
    icnt = 0;
    sbi(TIMSK1,OCIE1A);
}
/** @brief  Tonausgabe abschalten
 *  @param  none
 *  @return none
 */
void SidetoneOff(void)
{
    bState.SidetoneOff = 1;
    StateRiseTime = 0;
    StateRiseTimeCounter = 0;
}
/** @brief  Audioverstärker abschalten
 *  @param  none
 *  @return none
 */
void SidetoneDisable(void)
{
    bConfig.SidetoneEnabled = 0;
    cbi(PORTB,AUDIO_EN);
}
/** @brief  Audioverstärker einschalten
 *  @param  none
 *  @return none
 */
void SidetoneEnable(void)
{
    bConfig.SidetoneEnabled = 1;
    sbi(PORTB,AUDIO_EN);
}

/*
 ** TXKey
 **
 ** Tastung TRX1, TRX2 oder beide
 */
void TXKey(uint8_t State)
{
    if((State) && (bState.KeyState == 0))
    {
        if(bState.KeyTX)
        {
            SidetoneOn();
            sbi(PORTC,MORSE_LED);
            bState.KeyState = 1;
        }
    }
    else
    {
        if((State == 0) && (bState.KeyState))
        {
            if(bState.KeyTX)
            {
                SidetoneOff();
                cbi(PORTC,MORSE_LED);
            }
            bState.KeyState = 0;
        }
    }
}

/** \brief StraigtKey
 *
 * Check Straightkey 
 * 
*/
void CheckStraightKey(void)
{
    if(PIND & (1<<STRAIGHT_KEY))
    {
        if(bState.SendStatus == NOTHING)
            TXKey(0);
        StateStraightKeyPressed = NO_KEY_PRESSED;
    } else {
        if(StateStraightKeyPressed == NO_KEY_PRESSED)
        {
            StateStraightKeyPressed = KEY_PRESSED_DEBOUNCE;
            TimerStraightKeyPressed = 0;
        }
        if(StateStraightKeyPressed == KEY_PRESSED)
        {
            if(bState.SendStatus == NOTHING)
                TXKey(1);
        }
    }
}
/** \brief CheckPaddles
 ** 
*/
void CheckPaddles(void)
{
    CheckDitPaddle();
    CheckDahPaddle();
}
/** \brief CheckDitPaddle
 *
 *
*/
void CheckDitPaddle(void)
{
    uint8_t pinvalue = 0;

    if(PaddleMode == PADDLE_NORMAL)                     // no reverse paddle
        pinvalue = PIND & (1<<LEFT_PADDLE);
    else
        pinvalue = PIND & (1<<RIGHT_PADDLE);            // reverse paddle

    if(pinvalue == 0)                                   // DitPaddle betätigt
    {
        bState.DitPressed = 1;
    }
}
/*
 ** CheckDahPaddle
 */
void CheckDahPaddle(void)
{
    uint8_t pinvalue = 0;

    if(PaddleMode == PADDLE_NORMAL)	// no reverse paddle
        pinvalue = PIND & (1<<RIGHT_PADDLE);
    else
        pinvalue = PIND & (1<<LEFT_PADDLE); // reverse paddle

    if(pinvalue == 0) 
    {
        bState.DahPressed = 1;
    }
}
void SetRatio(uint8_t WPMCorrectFactor)
{
    DitMillis = 1200 / WpM;
    DahMillis = DitMillis * (float)(bConfig.Ratio/10);
}

/** @brief  Symbol senden
 *  @param  Dit = 1, wenn Symbol ein Punkt ist
 *  @return none
 */
void SendSymbol(uint8_t Dit)
{
    volatile static uint16_t ticks;     // Länge des Symbols
    if(Dit)
        ticks = DitMillis;
    else 
        ticks = DahMillis;
    t_elementlength = 0; 
    TXKey(1);
    DelayMilliSeconds(ticks);
    TXKey(0);
    DelayMilliSeconds(DitMillis);
}

/*
** void SendDit(uint8_t SendingType)
*/
void SendDit(void)
{
    bState.DitPressed = 0;
    SendSymbol(DIT);
    bState.LastSymbolWasDit = 1;
}
/** \brief Send a dah
**  A Dah with weight = 50 has the length of 3 Dits.
*/
void SendDah(void)
{
    bState.DahPressed = 0;
    SendSymbol(DAH);
    bState.LastSymbolWasDit = 0;
}

void SendIambic(void)
{
    bState.DahPressed = 0;
    bState.DitPressed = 0;
    if(bState.LastSymbolWasDit)
    {
        SendDah();
        if(bConfig.KeyerMode == IAMBIC_B && !bState.DahPressed && !bState.DitPressed)
        {
            SendDit();
        } else {
            if(bConfig.KeyerMode == ULTIMATIC)
            {
                while(bState.DahPressed)
                {
                    bState.DitPressed = 0;
                    bState.DahPressed = 0;
                    SendDah();
                }
            }
        } 
    } else {
        SendDit();
        if(bConfig.KeyerMode == IAMBIC_B && !bState.DitPressed && !bState.DahPressed)
        {
            SendDah();
        } else {
            if(bConfig.KeyerMode == ULTIMATIC)
            {
                while(bState.DitPressed)
                {
                    bState.DitPressed = 0;
                    bState.DahPressed = 0;
                    SendDit();
                }
            }
        }
    }
}
/*
** Set sidetone frequency 
*/
void SetFrequency(uint16_t f)
{
    IntDisable();
    OCR1A = (F_CPUPRESIN / f) - 1;
    IntEnable();
}
/*
** Output a tone width frequency f and duration duration
**
*/
void Tone(uint16_t f, uint8_t duration)
{
    SetFrequency(f);
    SidetoneOn();
    DelayMilliSeconds(duration);
    SidetoneOff();
    SetFrequency(bConfig.SidetoneFreq);
}
void Boop(void)
{
    Tone(600,100);
}
void Beep(void)
{
    Tone(1200,100);
}
void BeepBoop(void)
{
    DelayMilliSeconds(10);
    Beep();
    DelayMilliSeconds(100);
    Boop();
}

void SendChar(unsigned char c)
{
    switch(c)
    {
        // Buchstaben
/*
        case 'A': SendDit(AUTO); SendDah(AUTO); break;
        case 'B': SendDah(AUTO); SendDit(AUTO); SendDit(AUTO); SendDit(AUTO); break;
        case 'C': SendDit(AUTO); SendDah(AUTO); SendDit(AUTO); SendDah(AUTO); break;
        case 'D': SendDah(AUTO); SendDit(AUTO); SendDit(AUTO); break;
        case 'E': SendDit(AUTO); break;
        case 'F': SendDit(AUTO); SendDah(AUTO); SendDit(AUTO); SendDit(AUTO); break;
        case 'G': SendDah(AUTO); SendDah(AUTO); SendDit(AUTO); break;
        case 'H': SendDit(AUTO); SendDit(AUTO); SendDit(AUTO); SendDit(AUTO); break;
        case 'I': SendDit(AUTO); SendDit(AUTO); break;
        case 'J': SendDit(AUTO); SendDah(AUTO); SendDah(AUTO); SendDah(AUTO); break;
        case 'K': SendDah(AUTO); SendDit(AUTO); SendDah(AUTO); break;
        case 'L': SendDit(AUTO); SendDah(AUTO); SendDit(AUTO); SendDit(AUTO); break;
        case 'M': SendDah(AUTO); SendDah(AUTO); break;
        case 'N': SendDah(AUTO); SendDit(AUTO); break;
        case 'O': SendDah(AUTO); SendDah(AUTO); SendDah(AUTO); break;
        case 'P': SendDit(AUTO); SendDah(AUTO); SendDah(AUTO); SendDit(AUTO); break;
        case 'Q': SendDah(AUTO); SendDah(AUTO); SendDit(AUTO); SendDah(AUTO); break;
        case 'R': SendDit(AUTO); SendDah(AUTO); SendDit(AUTO); break;
        case 'S': SendDit(AUTO); SendDit(AUTO); SendDit(AUTO); break;
        case 'T': SendDah(AUTO); break;
        case 'U': SendDit(AUTO); SendDit(AUTO); SendDah(AUTO); break;
        case 'V': SendDit(AUTO); SendDit(AUTO); SendDit(AUTO); SendDah(AUTO); break;
        case 'W': SendDit(AUTO); SendDah(AUTO); SendDah(AUTO); break;
        case 'X': SendDah(AUTO); SendDit(AUTO); SendDit(AUTO); SendDah(AUTO); break;
        case 'Y': SendDah(AUTO); SendDit(AUTO); SendDah(AUTO); SendDah(AUTO); break;
        case 'Z': SendDit(AUTO); SendDit(AUTO); SendDah(AUTO); SendDah(AUTO); break;
        // Zahlen
        case '0': SendDah(AUTO); SendDah(AUTO); SendDah(AUTO); SendDah(AUTO); SendDah(AUTO); break;
        case '1': SendDit(AUTO); SendDah(AUTO); SendDah(AUTO); SendDah(AUTO); SendDah(AUTO); break;
        case '2': SendDit(AUTO); SendDit(AUTO); SendDah(AUTO); SendDah(AUTO); break;
        case '3': SendDit(AUTO); SendDit(AUTO); SendDit(AUTO); SendDah(AUTO); SendDah(AUTO); break;
        case '4': SendDit(AUTO); SendDit(AUTO); SendDit(AUTO); SendDit(AUTO); SendDah(AUTO); break;
        case '5': SendDit(AUTO); SendDit(AUTO); SendDit(AUTO); SendDit(AUTO); SendDit(AUTO); break;
        case '6': SendDah(AUTO); SendDit(AUTO); SendDit(AUTO); SendDit(AUTO); SendDit(AUTO); break;
        case '7': SendDah(AUTO); SendDah(AUTO); SendDit(AUTO); SendDit(AUTO); SendDit(AUTO); break;
        case '8': SendDah(AUTO); SendDah(AUTO); SendDah(AUTO); SendDit(AUTO); SendDit(AUTO); break;
        case '9': SendDah(AUTO); SendDah(AUTO); SendDah(AUTO); SendDah(AUTO); SendDit(AUTO); break;
        // Sonderzeichen
        case '=': SendDah(AUTO); SendDit(AUTO); SendDit(AUTO); SendDit(AUTO); SendDah(AUTO); break;
        case '/': SendDah(AUTO); SendDit(AUTO); SendDit(AUTO); SendDah(AUTO); SendDit(AUTO); break;
        case '.': SendDit(AUTO); SendDah(AUTO); SendDit(AUTO); SendDah(AUTO); SendDit(AUTO); SendDah(AUTO); break;
        case ',': SendDah(AUTO); SendDah(AUTO); SendDit(AUTO); SendDit(AUTO); SendDah(AUTO); SendDah(AUTO); break;
        case '?': SendDit(AUTO); SendDit(AUTO); SendDah(AUTO); SendDah(AUTO); SendDit(AUTO); SendDit(AUTO); break;
        case '@': SendDit(AUTO); SendDah(AUTO); SendDah(AUTO); SendDit(AUTO); SendDah(AUTO); SendDit(AUTO); break;
  */
    }
}

