//======================================================================
//
//  nanoIO paddle keyer (c) 2018, David Freese, W1HKJ
//
//  based on code from Iambic Keyer Code Keyer Sketch
//  Copyright (c) 2009 Steven T. Elliott
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details:
//
//  Free Software Foundation, Inc., 59 Temple Place, Suite 330,
//  Boston, MA  02111-1307  USA
//
//======================================================================

#include <Arduino.h>
//#include "TimerOne.h"
//#include "config.h"
#include "Keyer.h"

const uint8_t LP_in = KEYER_LEFT_PADDLE_PIN;
const uint8_t RP_in = KEYER_RIGHT_PADDLE_PIN;

//#define ST_Freq 600   // Set the Sidetone Frequency to 600 Hz

//======================================================================
//  keyerControl bit definitions
//
#define     DIT_L      0x01     // Dit latch
#define     DAH_L      0x02     // Dah latch
#define     DIT_PROC   0x04     // Dit is being processed
#define     PDLSWAP    0x08     // 0 for normal, 1 for swap
//======================================================================
//
//  State Machine Defines

enum KSTYPE { IDLE, CHK_DIT, CHK_DAH, KEYED_PREP, KEYED, INTER_ELEMENT };

UBitxKeyer::UBitxKeyer(int wpm, float weight):
speed(wpm), symWeight(weight)
{
  // Setup outputs
  pinMode(LP_in, INPUT_PULLUP);            // sets Left Paddle digital pin as input
  pinMode(RP_in, INPUT_PULLUP);            // sets Right Paddle digital pin as input

  keyerState = IDLE;
  keyerControl = 0;
  keyMode = IAMBICA;
  keyDown = false;

  calcRatio();
}

// Calculate the length of dot, dash and silence
void UBitxKeyer::calcRatio()
{
  float w = (1 + symWeight) / (symWeight -1);
  spaceLen = (1200 / speed);
  dotLen = spaceLen * (w - 1);
  dashLen =  (1 + w) * spaceLen;
}

void UBitxKeyer::setWPM(int wpm)
{
  speed = wpm;
  calcRatio();
}

//======================================================================
//    Latch paddle press
//======================================================================

void UBitxKeyer::updatePaddleLatch()
{
  if (digitalRead(LP_in) == LOW) {
    keyerControl |= DIT_L;
  }
  if (digitalRead(RP_in) == LOW) {
    keyerControl |= DAH_L;
  }
}

bool UBitxKeyer::doPaddles()
{
  if (keyMode == STRAIGHT) { // Straight Key
    if ((digitalRead(LP_in) == LOW) || (digitalRead(RP_in) == LOW)) {
      keyDown = true;
      return true;
    } else {
      keyDown = false;
    }
    return false;
  }

  // keyerControl contains processing flags and keyer mode bits
  // Supports Iambic A and B
  // State machine based, uses calls to millis() for timing.
  switch (keyerState) {
    case IDLE:      // Wait for direct or latched paddle press
      if ((digitalRead(LP_in) == LOW) || (digitalRead(RP_in) == LOW) || (keyerControl & 0x03)) {
        updatePaddleLatch();
        keyerState = CHK_DIT;
        // letting this fall through // return true;
      } else {
        return false;
      }
//      break;

    case CHK_DIT:      // See if the dit paddle was pressed
      if (keyerControl & DIT_L) {
        keyerControl |= DIT_PROC;
        ktimer = dotLen;
        keyerState = KEYED_PREP;
        return true;
      } else { // fall through
        keyerState = CHK_DAH;
      }
      
    case CHK_DAH:      // See if dah paddle was pressed
      if (keyerControl & DAH_L) {
        ktimer = dashLen;
        keyerState = KEYED_PREP;
        // letting this fall through // return true;
      } else {
        keyerState = IDLE;
        return false;
      }
//      break;

    case KEYED_PREP:                     // Assert key down, start timing
                                         // state shared for dit or dah
      keyDown = true;
      ktimer += millis();                // set ktimer to interval end time
      keyerControl &= ~(DIT_L + DAH_L);  // clear both paddle latch bits
      keyerState = KEYED;                // next state
      // letting this fall through // return true;
//      break;

    case KEYED:                          // Wait for timer to expire
      if (millis() > ktimer) {           // are we at end of key down ?
        keyDown = false;
        ktimer = millis() + spaceLen;  // inter-element time
        keyerState = INTER_ELEMENT;      // next state
        // letting this fall through // return true;
      } else if (keyMode == IAMBICB) {     // Iambic B Mode ?
        updatePaddleLatch();           // yes, early paddle latch in Iambic B mode
      } else {
        return true;
      }
//      break;

    case INTER_ELEMENT:                 // Insert time between dits/dahs
      updatePaddleLatch();              // latch paddle state
      if (millis() > ktimer) {          // are we at end of inter-space ?
        if (keyerControl & DIT_PROC) {  // was it a dit or dah ?
          keyerControl &= ~(DIT_L + DIT_PROC);   // clear two bits
          keyerState = CHK_DAH;                  // dit done, check for dah
          return true;
        } else {
          keyerControl &= ~(DAH_L);     // clear dah latch
          keyerState = IDLE;            // go idle
          return false;
        }
      } else {
        return true;
      }
//      break;
  }

  return false; // resolve compiler warning; do we ever get here?
}

UBitxKeyer basicKeyer(15, 3.0);
UBitxKeyer& Keyer = basicKeyer;

//======================================================================
// EOF
//======================================================================
