// USB-MOUSE to UART converter
// USB host interface built from Harmony
// Translation done to UART (custom library)

//needed for user code
#include <xc.h>

#include <stddef.h>                     // Defines NULL
#include <stdbool.h>                    // Defines true
#include <stdlib.h>                     // Defines EXIT_FAILURE
#include "system/common/sys_module.h"   // SYS function prototypes

//user lib for uart
#include "uart.h"
// Generic IO functions
#include "io.h"
//manipulate settings
#include "settings.h"
//flash libs
#include "flash.h"

#include "usb/src/usb_host_hid_mouse_local.h"

#define CORE_TIMER_FREQ (24000000L)
void APP_HOST_Mouse_EventHandler(USB_HOST_HID_MOUSE_HANDLE handle, USB_HOST_HID_MOUSE_EVENT event, void * pData); //mouse event
volatile int32_t xAccum=0,yAccum=0,zAccum=0,lastUpdate=0;        //accumulated mouse movements for periodic reporting, button events reported immediately
uint8_t buttons=0;                                  //map buttons to bitmap
void doUART(uint8_t buttons);                       //do report as needed
void amberFlicker();        //turn on amber LED, set timer for later turn off
uint32_t flickerTime;       //time when LED was turned on

int main ( void ){
    int jp4last;                    //check if JP4 changes
    SYS_Initialize ( NULL );                                                    //Harmony code- setup
    //defaultSettings();
    loadSettings();
    currentBaudRate=newBaudRate;
    currentUartMode=newUartMode;
    currentDPIdivider=newDPIdivider;
    currentUpdateInterval=newUpdateInterval;
    uart_init(currentBaudRate);                                                 //start UART
    //uart_print("Start\r\n");                                                    //for debugging
    IOinit();
    USB_HOST_HID_MOUSE_EventHandlerSet(APP_HOST_Mouse_EventHandler);            //set mouse event handler
    RED_LED=1;
    GREEN_LED=0;
    USB_HOST_BusEnable(0);                                                      //need to do this too    
    jp4last=JP1_4;

    while (1){
        int f=0;
        SYS_Tasks();                                                            //Harmony code- USB servicing etc
        if(JP1_3!=0){
            if((JP1_4==0)&&(jp4last!=0)){       //jp4 has been set
                defaultSettings();
                if(currentBaudRate!=newBaudRate){     //change baud rate if necessary
                    uart_init(newBaudRate);
                }
                currentUartMode=newUartMode;
                currentDPIdivider=newDPIdivider;
                currentUpdateInterval=newUpdateInterval;
                currentBaudRate=newBaudRate;
                RAMImage[0]=newUartMode;
                RAMImage[1]=newBaudRate;
                RAMImage[2]=newDPIdivider;
                RAMImage[3]=newUpdateInterval;
                saveToFlash();          //don't use sub as it outputs to serial port
            }
            jp4last=JP1_4;
            if(JP1_1==0){f=1;}                                                      //jumper set for setup
            while(uart_available()){                                                //purge buffer, so no other keys get in setup
                if(uart_receive()=='~'){f=1;}                                       //set flag to do setup
            }
            if(f){doSetupMenu();}
            if(JP1_2==0){                                                           //force defaults on JP2
                defaultSettings();
                if(currentBaudRate!=newBaudRate){                                   //change baud rate if necessary
                    uart_init(newBaudRate);
                }
                currentUartMode=newUartMode;
                currentDPIdivider=newDPIdivider;
                currentUpdateInterval=newUpdateInterval;
                currentBaudRate=newBaudRate;
            }
        }
        if(GREEN_LED){            
            if(_CP0_GET_COUNT()-lastUpdate>(CORE_TIMER_FREQ/1000)*currentUpdateInterval){
                doUART(buttons);
            }
        }
        if((_CP0_GET_COUNT()-flickerTime)>(CORE_TIMER_FREQ/20)){AMBER_LED=0;}       //LED off after brief delay
    }
}

void APP_HOST_Mouse_EventHandler(USB_HOST_HID_MOUSE_HANDLE handle, USB_HOST_HID_MOUSE_EVENT event, void * pData){   //mouse event
    //todo set maximum report rate, as many small increments can occur
    USB_HOST_HID_MOUSE_DATA* mouseData=pData;    
    static uint8_t oldButtons=0;    //old button bitmap
    int i;
    if(event==USB_HOST_HID_MOUSE_EVENT_ATTACH){
        //uart_print("AT\r\n");                                                   //for debug        
        RED_LED=0;
        GREEN_LED=1;
    }
    if(event==USB_HOST_HID_MOUSE_EVENT_DETACH){
        //uart_print("DE\r\n");                                                   //for debug                
        RED_LED=1;
        GREEN_LED=0;
    }
    if(event==USB_HOST_HID_MOUSE_EVENT_REPORT_RECEIVED){
        buttons=0;
        for(i=0;i<USB_HOST_HID_MOUSE_BUTTONS_NUMBER;i++){
            if(mouseData->buttonState[i]==USB_HID_BUTTON_PRESSED){buttons=buttons|(128>>i);}            //set up bitmap, button 1 at bit7
        }        
        xAccum=xAccum+mouseData->xMovement;
        yAccum=yAccum+mouseData->yMovement;
        zAccum=zAccum+mouseData->zMovement;
        if(buttons!=oldButtons){                //do report immediately on button change
            doUART(buttons);
        }
        oldButtons=buttons;                     //for next comparison
    }
}

void doUART(uint8_t buttons){          //do report as needed
    int32_t x,y,z,d;
    lastUpdate=_CP0_GET_COUNT();
    d=currentDPIdivider;
    if(d<1){d=1;}       //avoid /0
    x=xAccum;           //local copies
    xAccum=0;
    y=yAccum;
    yAccum=0;
    z=zAccum;
    zAccum=0;
    x=x/d;
    y=y/d;
    z=z/d;
    switch(newUartMode){
        case UART_MODE_MSMOUSE:     //8bits available for x,y+2 buttons 18bits used in total
            if(x<-127){x=-127;}
            if(y<-127){y=-127;}
            if(x>127){x=127;}
            if(y>127){y=127;}
            uart_send(192|((buttons>>2)&48)|((y>>4)&12)|((x>>6)&3));
            uart_send(128|(x&63));
            uart_send(128|(y&63));
            break;
        case UART_MODE_MSMOUSE_EXT:     //9bits available for x,y+3 buttons 21bits used in total
            if(x<-255){x=-255;}
            if(y<-255){y=-255;}
            if(x>255){x=255;}
            if(y>255){y=255;}
            uart_send(128|((buttons>>1)&112)|((y>>5)&12)|((x>>7)&3));
            uart_send(x&127);
            uart_send(y&127);
            break;
            break;
        case UART_MODE_ASCII:
            uart_printl(buttons);
            uart_send(',');
            uart_printl(x);
            uart_send(',');
            uart_printl(y);
            uart_send(',');
            uart_printl(z);
            uart_send('\r');
            uart_send('\n');                                    
            break;
    }
    amberFlicker();    
}
 
void amberFlicker(){        //turn on amber LED, set timer for later turn off
    AMBER_LED=1;
    flickerTime=_CP0_GET_COUNT();
}