uint8_t debugOn=0;

//PS2 on GP4/GP5=DT/CK
#define PS2DT 4
#define PS2CK 5
#define PS2TX_BUFFER_SIZE 128
#define PS2_ISR_PERIOD 20
#define PS2_DELAY 20
#define PS2_HOST_CK_HOLD 5
#define PS2_HOST_TX 100
//host commands and specific codes
#define PS2_CMD_LEDS 0xED
#define PS2_CMD_ACK 0xFA
#define PS2_CMD_RESEND 0xFE
#define PS2_CMD_ECHO 0xEE
#define PS2_CMD_REQID 0xF2
#define PS2_CMD_TYPEMATIC 0xF3
#define PS2_CMD_ENABLE 0xF4
#define PS2_CMD_DISABLE 0xF5
#define PS2_CMD_DEFAULT 0xF6
#define PS2_CMD_RESET 0xFF
#define PS2_CMD_ALTCODES 0xF0
#define PS2_BAT_OK 0xAA
#define PS2_KEY_ID_1 0xAB
#define PS2_KEY_ID_2 0x83
#define PS2_CAPSMASK 0x04
#define PS2_NUMMASK 0x02
#define PS2_SCROLLMASK 0x01

//specific USB HID scan codes
#define USB_CAPSLOCK 0x39
#define USB_NUMLOCK 0x53
#define USB_SCROLLLOCK 0x47
#define USB_PAUSEBREAK 0x48

struct repeating_timer ps2Timer;
int ps2txPhase=0;
uint8_t ps2txQueue[PS2TX_BUFFER_SIZE];
uint8_t ps2txHead=0,ps2txTail=0;
#define LEDTIMEOUT (100000/PS2_ISR_PERIOD)
uint16_t ledCtr=0;
uint16_t dataFromHost=0;
uint16_t hostByte=0;    //raw, so that it can be properly acked/req resend etc
uint8_t hostCmdByte=0;    //multibyte sequences
uint8_t hostByteReady=0;
int32_t tmPeriod=92000;     //defaults
int32_t tmDelay=500000;     
int32_t tmTimer=0;
uint16_t tmCode=0;
int hostInt=-1;

uint8_t getParity(uint8_t c){
  uint8_t p=0;
  p=c^(c>>4);
  p=p^(p>>2);
  p=(p^(p>>1)^1)&1;   //parity
  return p;
}

uint16_t getPhy(uint8_t c){
  uint16_t p=getParity(c)<<9;
  return (c<<1)|(7<<10)|p; //implied start bit at position 0 is 0, send a few extra stop bits
}

uint8_t ps2DataReady(){
  return (ps2txHead+PS2TX_BUFFER_SIZE-ps2txTail)%PS2TX_BUFFER_SIZE;
}

void ps2Send(uint8_t d){
  if(debugOn){Serial.printf("Send:0x%x\r\n",d);}
  ps2txQueue[ps2txHead]=d;
  ps2txHead=(ps2txHead+1)%PS2TX_BUFFER_SIZE;
}

uint8_t ps2getFromQueue(){
  uint8_t d=0;
  if(ps2DataReady()!=0){
    d=ps2txQueue[ps2txTail];
    ps2txTail=(ps2txTail+1)%PS2TX_BUFFER_SIZE;
  }
  return d;  
}

bool ps2TimerCallback(struct repeating_timer *t) {
  static uint16_t hostT=0;
  static uint16_t phy=0;
  static uint8_t c=0;   //static so we can push back if interrupted
  uint8_t p=0;
  if(digitalRead(PS2CK)){   //check if host is pulling CK down
    hostT=0;
  }else{
    hostT++;
    if(hostT>PS2_HOST_CK_HOLD){hostT=PS2_HOST_CK_HOLD;} 
    if(hostT==PS2_HOST_CK_HOLD){  //hold period met
      if(ps2txPhase<PS2_HOST_TX){    
        hostInt=ps2txPhase;       //jump to host receiver             
        ps2txPhase=PS2_HOST_TX;
      }
    }
  }  
  if(ps2txPhase==0){
    if(ps2DataReady()&&(hostT==0)){ps2txPhase=1;};  //if data ready and host not requesting
  }else if(ps2txPhase==1){
    ledCtr=LEDTIMEOUT;      //flicker LED on TX
    c=ps2getFromQueue();
    phy=getPhy(c);
    ps2txPhase=2;
  }else if((ps2txPhase>1)&&(ps2txPhase<46)){
    switch(ps2txPhase%4){
      case 2:
        if(phy&1){
          pinMode(PS2DT,INPUT_PULLUP);
        }else{
          digitalWrite(PS2DT,LOW);
        }
        phy=phy>>1;
        break;
      case 3:
        digitalWrite(PS2CK,LOW);break;
      case 0:;break;
      case 1:
        pinMode(PS2CK,INPUT_PULLUP);break;
    }
    ps2txPhase++;
  }else if((ps2txPhase>45)&&(ps2txPhase<(45+PS2_DELAY))){  //small delay between tx, mostly to allow host to gain control
    ps2txPhase++;
  }else if(ps2txPhase==(45+PS2_DELAY)){
    pinMode(PS2DT,INPUT_PULLUP);
    ps2txPhase=0;
  }else if(ps2txPhase==PS2_HOST_TX){  //ensure host has control of lines
    pinMode(PS2DT,INPUT_PULLUP);
    pinMode(PS2CK,INPUT_PULLUP);
    dataFromHost=0; //reset data
    ps2txPhase++;
  }else if(ps2txPhase==(PS2_HOST_TX+1)){
    if((digitalRead(PS2DT)==0)&&(digitalRead(PS2CK)==1)){ps2txPhase++;}  //wait for host to pull DT low and release CK
    if((digitalRead(PS2DT)==1)&&(digitalRead(PS2CK)==1)){ps2txPhase=0;}  //if both hi, then host isn't asserting anymore
  }else if((ps2txPhase>(PS2_HOST_TX+1))&&(ps2txPhase<(PS2_HOST_TX+46))){
    if(ps2txPhase==(PS2_HOST_TX+41)){digitalWrite(PS2DT,LOW);}  //start ack
    if(ps2txPhase==(PS2_HOST_TX+45)){pinMode(PS2DT,INPUT_PULLUP);}  //end ack
    ps2txPhase++;
    switch(ps2txPhase%4){
      case ((PS2_HOST_TX+2)%4):
        digitalWrite(PS2CK,LOW);
        break;
      case ((PS2_HOST_TX+3)%4):
        break;
      case ((PS2_HOST_TX+4)%4):
        digitalWrite(PS2CK,HIGH);
        break;
      case ((PS2_HOST_TX+5)%4):
        dataFromHost=dataFromHost>>1;
        if(digitalRead(PS2DT)){dataFromHost=dataFromHost|1024;}
        break;    
    }
  }else if(ps2txPhase==(PS2_HOST_TX+46)){
    hostByte=dataFromHost;
    hostByteReady=1;
    ps2txPhase++;
  }else if(ps2txPhase==(PS2_HOST_TX+47)){
    if(hostByteReady==0){   //controller has reacted to data
      pinMode(PS2DT,INPUT_PULLUP);
      pinMode(PS2CK,INPUT_PULLUP);
      ps2txPhase=0;
      hostByte=0;
    }
  }else{    //lost state?
    pinMode(PS2DT,INPUT_PULLUP);
    pinMode(PS2CK,INPUT_PULLUP);
    ps2txPhase=0;
  }
  if(ledCtr){ledCtr--;}
  tmTimer=tmTimer-PS2_ISR_PERIOD;   //count down
  if(tmTimer<0){tmTimer=0;}
  return true;
}

void ps2Init(){
  pinMode(PS2DT,INPUT_PULLUP);
  //digitalWrite(PS2DT,LOW);
  pinMode(PS2CK,INPUT_PULLUP);
  //digitalWrite(PS2CK,LOW);
  add_repeating_timer_us(-PS2_ISR_PERIOD, ps2TimerCallback, NULL, &ps2Timer);
}

void ps2Reset(){
  tmPeriod=92000;     //defaults
  tmDelay=500000;     
  tmTimer=0;
  tmCode=0;
  ps2txHead=0;    //empty buffer
  ps2txTail=0;
  ps2Send(PS2_BAT_OK);
}


// for USB side of things:
typedef enum{
    KEYNONE=0,
    KEYUP=1,    
    KEYDOWN=2,
} keyEventType_t;

typedef struct {
  uint8_t lCtrl :1;
  uint8_t lShift :1;
  uint8_t lAlt :1;
  uint8_t lGui :1;
  uint8_t rCtrl :1;
  uint8_t rShift :1;
  uint8_t rAlt :1;
  uint8_t rGui :1;
} modKeys_t;

typedef struct {
  uint8_t numLock :1;
  uint8_t capsLock :1;
  uint8_t scrollLock :1;
  uint8_t compose :1;
  uint8_t kana :1;
  uint8_t res :3;
} lockKeys_t;

typedef struct {
  uint8_t key;
  modKeys_t mod;
  lockKeys_t lock;
  keyEventType_t event;
} keyEvent_t;

typedef union {
    struct  {
        modKeys_t mod;
        uint8_t res;
        uint8_t k[6];
    }; 
    uint8_t buffer[8];
} report_t;

#define EVENT_BUFFER_SIZE 128
keyEvent_t eventQueue[EVENT_BUFFER_SIZE];
uint8_t eventHead=0,eventTail=0;

uint8_t eventAvailable(){
  return (eventHead+EVENT_BUFFER_SIZE-eventTail)%EVENT_BUFFER_SIZE;
}

void queueEvent(keyEvent_t event){
  eventQueue[eventHead]=event;
  eventHead=(eventHead+1)%EVENT_BUFFER_SIZE;
}

keyEvent_t getEvent(){
  keyEvent_t event={.key=0,.mod={0,0,0,0,0,0,0,0},.event=KEYNONE}; //blank event
  if(eventAvailable()!=0){
    event=eventQueue[eventTail];
    eventTail=(eventTail+1)%EVENT_BUFFER_SIZE;
  }
  return event;  
}
const uint16_t usb2set2[]={   //USB scan codes directly converted to PS2 scan codes
  0x00,  0x00,  0x00,  0x00,  0x1C,  0x32,  0x21,  0x23,  0x24,  0x2B,  0x34,  0x33,  0x43,  0x3B,  0x42,  0x4B,
  0x3A,  0x31,  0x44,  0x4D,  0x15,  0x2D,  0x1B,  0x2C,  0x3C,  0x2A,  0x1D,  0x22,  0x35,  0x1A,  0x16,  0x1E,
  0x26,  0x25,  0x2E,  0x36,  0x3D,  0x3E,  0x46,  0x45,  0x5A,  0x76,  0x66,  0x0D,  0x29,  0x4E,  0x55,  0x54,
  0x5B,  0x5D,  0x00,  0x4C,  0x52,  0x0E,  0x41,  0x49,  0x4A,  0x58,  0x05,  0x06,  0x04,  0x0C,  0x03,  0x0B,
  0x83,  0x0A,  0x01,  0x09,  0x78,  0x07,0xE07C,  0x7E,  0x00,0xE070,0xE06C,0xE07D,0xE071,0xE069,0xE07A,0xE074,
0xE06B,0xE072,0xE075,  0x77,0xE04A,  0x7C,  0x7B,  0x79,0xE05A,  0x69,  0x72,  0x7A,  0x6B,  0x73,  0x74,  0x6C,
  0x75,  0x7D,  0x70,  0x71,  0x5D,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
  0x14,  0x12,  0x11,0xE01F,0xE014,  0x59,0xE011,0xE027,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
};

const uint32_t tmPeriods[]={   //from 5 lsbs of typematic data => typematic period in us
  33360,  37530,  41700,  45870,  50040,  54210,  58380,  62550,
  66720,  75060,  83400,  91740, 100080, 108420, 116760, 125100,
 133440, 150120, 166800, 183480, 200160, 216840, 233520, 250200,
 266880, 300240, 333600, 366960, 400320, 433680, 467040, 500400
};

const uint32_t tmDelays[]={   //from bits 6&5 of typematic data => typmatic delay in us
  250000,500000,750000,1000000
};

int tmOut=0;
report_t lastReport,newReport;
uint8_t keyAdd=0;
uint8_t keyInst=0;
lockKeys_t lockState;
modKeys_t modState;
uint8_t ctrlState,shiftState,altState,guiState;

