/******************************************************************************
 * Includes
 *****************************************************************************/
 
#include "mbed.h"
#include "mbed_interface.h"
#include "rtos.h"
#include "EthernetInterface.h"
#include "HTTPServer.h"
#include "mbed_rpc.h"
#include "USBHostMSD.h"
#include "USBHostMouse.h"
#include "USBHostKeyboard.h"

#include "DMBoard.h"

#include "AppLauncherSpecial.h"
#include "meas.h"

#include "AppNetworkSettings.h"
#include "AppStatus.h"
#include "AppTouchCalibration.h"
#include "AppColorPicker.h"
#include "AppImageViewer.h"
#include "AppSlideShow.h"
#include "AppRTCSettings.h"
#include "AppUSBStatus.h"
#include "AppDraw.h"
#include "image_data.h"
#include "Resource.h"

#include "lpc_swim.h"
#include "lpc_helvr10.h"
#include "lpc_winfreesystem14x16.h"

/******************************************************************************
 * Typedefs and defines
 *****************************************************************************/

typedef enum {
    Res_ok,
    Res_cancel,
    Res_arrow_up,
    Res_arrow_down,
    Res_arrow_left,
    Res_arrow_right,
    Res_repeat,
    Res_digits,
    Res_background,
    Res_about,
    Res_ColorPickerApp,
    Res_ImageViewerApp,
    Res_SlideshowApp,
    Res_SettingsApp, 
    Res_StatusApp, 
    //Res_TouchTestApp,
    Res_RtcApp,
    Res_USBStatusApp,
    Res_DrawingApp,
    //Res_CalibrationApp,
    NumResources,
} resorces_t;

/******************************************************************************
 * Local variables
 *****************************************************************************/

static Resource* RESOURCES[NumResources] = {0};

/******************************************************************************
 * Global variables
 *****************************************************************************/

EthernetInterface eth;
bool ethInitialized = false;
bool ethUsingDHCP = true;
bool haveUSBMSD = false;

/******************************************************************************
 * Local functions
 *****************************************************************************/

static void prepareResources()
{
  Display* disp = DMBoard::instance().display();
  
  // select which images to use based on display size
  if (disp->width() == 480) {
    RESOURCES[Res_ok         ] = new Resource(img_480x272_ok,          img_size_480x272_ok, 40, 40);
    RESOURCES[Res_cancel     ] = new Resource(img_480x272_cancel,      img_size_480x272_cancel, 40, 40);
    RESOURCES[Res_arrow_up   ] = new Resource(img_480x272_arrow_up,    img_size_480x272_arrow_up,    52, 52);
    RESOURCES[Res_arrow_down ] = new Resource(img_480x272_arrow_down,  img_size_480x272_arrow_down,  52, 52);
    RESOURCES[Res_arrow_left ] = new Resource(img_480x272_arrow_left,  img_size_480x272_arrow_left,  52, 52);
    RESOURCES[Res_arrow_right] = new Resource(img_480x272_arrow_right, img_size_480x272_arrow_right, 52, 52);
    RESOURCES[Res_repeat     ] = new Resource(img_480x272_repeat,      img_size_480x272_repeat, 40, 40);
    RESOURCES[Res_digits     ] = new Resource(img_numbers_16b,         img_size_numbers_16b, 13, 242);
      
    RESOURCES[Res_ColorPickerApp] = new Resource(img_480x272_super_mono_3d_part2_19, img_size_480x272_super_mono_3d_part2_19, 64, 64);
    RESOURCES[Res_ImageViewerApp] = new Resource(img_480x272_super_mono_3d_48,       img_size_480x272_super_mono_3d_48,       64, 64);
    RESOURCES[Res_SlideshowApp  ] = new Resource(img_480x272_super_mono_3d_87,       img_size_480x272_super_mono_3d_87,       64, 64);
    RESOURCES[Res_SettingsApp   ] = new Resource(img_480x272_super_mono_3d_93,       img_size_480x272_super_mono_3d_93,       64, 64);
    RESOURCES[Res_StatusApp     ] = new Resource(img_480x272_super_mono_3d_part2_68, img_size_480x272_super_mono_3d_part2_68, 64, 64);
    RESOURCES[Res_RtcApp        ] = new Resource(img_480x272_super_mono_3d_02,       img_size_480x272_super_mono_3d_02,       64, 64);
    RESOURCES[Res_USBStatusApp  ] = new Resource(img_480x272_super_mono_3d_57,       img_size_480x272_super_mono_3d_57,       64, 64);
    RESOURCES[Res_DrawingApp    ] = new Resource(img_480x272_super_mono_3d_22,       img_size_480x272_super_mono_3d_22,       64, 64);
    RESOURCES[Res_background    ] = new Resource(img_480x272_background,             img_size_480x272_background, 364, 57);
    RESOURCES[Res_about         ] = new Resource(img_480x272_about,                  img_size_480x272_about, 240, 245);
    
    swim_set_default_font(&font_helvr10);
  } else {
    RESOURCES[Res_ok         ] = new Resource(img_800x480_ok,          img_size_800x480_ok,       64, 64);
    RESOURCES[Res_cancel     ] = new Resource(img_800x480_cancel,      img_size_800x480_cancel,   64, 64);
    RESOURCES[Res_arrow_up   ] = new Resource("/qspi/800x480_arrow_up.raw",                     128, 128);
    RESOURCES[Res_arrow_down ] = new Resource("/qspi/800x480_arrow_down.raw",                   128, 128);
    RESOURCES[Res_arrow_left ] = new Resource("/qspi/800x480_arrow_left.raw",                   128, 128);
    RESOURCES[Res_arrow_right] = new Resource("/qspi/800x480_arrow_right.raw",                  128, 128);
    RESOURCES[Res_repeat     ] = new Resource(img_800x480_repeat,      img_size_800x480_repeat,  64,  64);
    RESOURCES[Res_digits     ] = new Resource(img_numbers_20b,         img_size_numbers_20b,     16, 322);
      
    RESOURCES[Res_ColorPickerApp] = new Resource("/qspi/800x480_super-mono-3d-part2-19.raw",  64,  64);
    RESOURCES[Res_ImageViewerApp] = new Resource("/qspi/800x480_super-mono-3d-48.raw",        64,  64);
    RESOURCES[Res_SlideshowApp  ] = new Resource("/qspi/800x480_super-mono-3d-87.raw",        64,  64);
    RESOURCES[Res_SettingsApp   ] = new Resource("/qspi/800x480_super-mono-3d-93.raw",        64,  64);
    RESOURCES[Res_StatusApp     ] = new Resource("/qspi/800x480_super-mono-3d-part2-68.raw",  64,  64);
    RESOURCES[Res_RtcApp        ] = new Resource("/qspi/800x480_super-mono-3d-02.raw",        64,  64);
    RESOURCES[Res_USBStatusApp  ] = new Resource("/qspi/800x480_super-mono-3d-57.raw",        64,  64);
    RESOURCES[Res_DrawingApp    ] = new Resource("/qspi/800x480_super-mono-3d-22.raw",        64,  64);
    RESOURCES[Res_background    ] = new Resource("/qspi/800x480_background.raw",             548,  80);
    RESOURCES[Res_about         ] = new Resource("/qspi/800x480_about.raw",                  420, 429);
      
    swim_set_default_font(&font_winfreesys14x16);
  }
}

void aliveTask(void const* args)
{
  DMBoard* board = &DMBoard::instance();
    
  while(true)
  {
    board->setLED(DMBoard::Led4, false);
    board->setLED(DMBoard::Led1, true);
    Thread::wait(300);
    board->setLED(DMBoard::Led1, false);
    board->setLED(DMBoard::Led2, true);
    Thread::wait(300);
    board->setLED(DMBoard::Led2, false);
    board->setLED(DMBoard::Led3, true);
    Thread::wait(300);
    board->setLED(DMBoard::Led3, false);
    board->setLED(DMBoard::Led4, true);
    Thread::wait(300);
  }
}

#if defined(DM_BOARD_USE_DISPLAY)

typedef enum {
    ColorPickerApp,
    ImageViewerApp,
    SlideshowApp,
    SettingsApp, 
    StatusApp, 
    TouchTestApp,
    RtcApp,
    USBStatusApp,
    DrawingApp,
    CalibrationApp =  AppLauncherSpecial::CalibrationApp,
    Placeholder,
} myapps_t;

static App* launchApp(uint32_t id)
{
  App* a = NULL;
  switch ((myapps_t)id) {
      case CalibrationApp:
          a = new AppTouchCalibration();
         ((AppTouchCalibration*)a)->addResource(AppTouchCalibration::Resource_Ok_button, RESOURCES[Res_ok]);
          break;
      case SettingsApp:
          a = new AppNetworkSettings();
         ((AppNetworkSettings*)a)->addResource(AppNetworkSettings::Resource_Ok_button, RESOURCES[Res_ok]);
         ((AppNetworkSettings*)a)->addResource(AppNetworkSettings::Resource_Cancel_button, RESOURCES[Res_cancel]);
         ((AppNetworkSettings*)a)->addResource(AppNetworkSettings::Resource_ArrowUp_button, RESOURCES[Res_arrow_up]);
         ((AppNetworkSettings*)a)->addResource(AppNetworkSettings::Resource_ArrowDown_button, RESOURCES[Res_arrow_down]);
         ((AppNetworkSettings*)a)->addResource(AppNetworkSettings::Resource_ArrowLeft_button, RESOURCES[Res_arrow_left]);
         ((AppNetworkSettings*)a)->addResource(AppNetworkSettings::Resource_ArrowRight_button, RESOURCES[Res_arrow_right]);
         ((AppNetworkSettings*)a)->addResource(AppNetworkSettings::Resource_Digits, RESOURCES[Res_digits]);
          break;
      case ColorPickerApp:
          a = new AppColorPicker();
         ((AppColorPicker*)a)->addResource(AppColorPicker::Resource_Ok_button, RESOURCES[Res_ok]);
          break;
      case ImageViewerApp:
          a = new AppImageViewer();
         ((AppImageViewer*)a)->addResource(AppImageViewer::Resource_Ok_button, RESOURCES[Res_ok]);
          break;
      case SlideshowApp:
          {
              // Select folder for the slideshow based on display size
              Display* d = DMBoard::instance().display();
              if (d->width() == 480) {
                  a = new AppSlideShow("/qspi/slides.txt", "/qspi/480x272", 0, 0);
              } else {
                  a = new AppSlideShow("/qspi/slides.txt", "/qspi/800x480", 0, 0);
              }
              
             ((AppSlideShow*)a)->addResource(AppSlideShow::Resource_Ok_button, RESOURCES[Res_ok]);
             ((AppSlideShow*)a)->addResource(AppSlideShow::Resource_Repeat_button, RESOURCES[Res_repeat]);
          }
          break;
      case StatusApp:
          a = new AppStatus();
         ((AppStatus*)a)->addResource(AppStatus::Resource_Ok_button, RESOURCES[Res_ok]);
         ((AppStatus*)a)->addResource(AppStatus::Resource_About_image, RESOURCES[Res_about]);
          break;
      case RtcApp:
          a = new AppRTCSettings();
         ((AppRTCSettings*)a)->addResource(AppRTCSettings::Resource_Ok_button, RESOURCES[Res_ok]);
         ((AppRTCSettings*)a)->addResource(AppRTCSettings::Resource_Cancel_button, RESOURCES[Res_cancel]);
         ((AppRTCSettings*)a)->addResource(AppRTCSettings::Resource_ArrowUp_button, RESOURCES[Res_arrow_up]);
         ((AppRTCSettings*)a)->addResource(AppRTCSettings::Resource_ArrowDown_button, RESOURCES[Res_arrow_down]);
         ((AppRTCSettings*)a)->addResource(AppRTCSettings::Resource_ArrowLeft_button, RESOURCES[Res_arrow_left]);
         ((AppRTCSettings*)a)->addResource(AppRTCSettings::Resource_ArrowRight_button, RESOURCES[Res_arrow_right]);
         ((AppRTCSettings*)a)->addResource(AppRTCSettings::Resource_Digits, RESOURCES[Res_digits]);
          break;
      case USBStatusApp:
          a = new AppUSBStatus();
         ((AppUSBStatus*)a)->addResource(AppUSBStatus::Resource_Ok_button, RESOURCES[Res_ok]);
          break;
      case DrawingApp:
          a = new AppDraw();
         ((AppDraw*)a)->addResource(AppDraw::Resource_Ok_button, RESOURCES[Res_ok]);
          break;
      default:
          break;
  }
  return a;
}

#define SWIM_TASK_PREFIX  "[SWIM] "
void swimTask(void const* args)
{
  RtosLog* log = DMBoard::instance().logger();
  Display* disp = DMBoard::instance().display();
    
  log->printf(SWIM_TASK_PREFIX"swimTask started\n");
  
  int iconsize = (disp->width()==480) ? 64 : 128;
  AppLauncherSpecial launcher(iconsize, iconsize);
  launcher.addResource(AppLauncherSpecial::Resource_Background, RESOURCES[Res_background]);
    
  if (launcher.setup()) {
    launcher.addImageButton(SettingsApp,    "Network",      0x2d5c, RESOURCES[Res_SettingsApp   ]);
    launcher.addImageButton(DrawingApp,     "Drawing",      0x2d5c, RESOURCES[Res_DrawingApp    ]);
    launcher.addImageButton(SlideshowApp,   "Slideshow",    0x2d5c, RESOURCES[Res_SlideshowApp  ]);
    launcher.addImageButton(ColorPickerApp, "Color Picker", 0x2d5c, RESOURCES[Res_ColorPickerApp]);
    launcher.addImageButton(ImageViewerApp, "Image Viewer", 0x2d5c, RESOURCES[Res_ImageViewerApp]);
    launcher.addImageButton(StatusApp,      "About",        0x2d5c, RESOURCES[Res_StatusApp     ]);
    launcher.addImageButton(USBStatusApp,   "USB Status",   0x2d5c, RESOURCES[Res_USBStatusApp  ]);
    launcher.addImageButton(RtcApp,         "Clock",        0x2d5c, RESOURCES[Res_RtcApp        ]);
      
    launcher.setAppCreatorFunc(launchApp);
    log->printf(SWIM_TASK_PREFIX"Starting menu system\n");
    launcher.runToCompletion();
    launcher.teardown();
  } else {
    log->printf(SWIM_TASK_PREFIX"Failed to prepare menu system\n");
  }
  
  // Should never end up here  
  mbed_die();
}

#endif //DM_BOARD_USE_DISPLAY


#define NET_TASK_PREFIX  "[NET] "

void netTask(void const* args)
{
  RtosLog* log = DMBoard::instance().logger();
  log->printf(NET_TASK_PREFIX"EthernetInterface Setting up...\r\n");
  if(eth.init()!=0) {                             //for DHCP Server
     //if(eth.init(IPAddress,NetMasks,Gateway)!=0) { //for Static IP Address
     log->printf(NET_TASK_PREFIX"EthernetInterface Initialize Error \r\n");
     mbed_die();
  }
  while (eth.connect() != 0) {
     Thread::wait(1000);
  }
  
  ethInitialized = true;
  ethUsingDHCP = true;
  
  log->printf(NET_TASK_PREFIX"IP Address is %s\r\n", eth.getIPAddress());
  log->printf(NET_TASK_PREFIX"NetMask is %s\r\n", eth.getNetworkMask());
  log->printf(NET_TASK_PREFIX"Gateway Address is %s\r\n", eth.getGateway());
  log->printf(NET_TASK_PREFIX"Ethernet Setup OK\r\n");

  HTTPServerAddHandler<SimpleHandler>("/hello"); //Default handler
  FSHandler::mount("/usb", "/");
  HTTPServerAddHandler<FSHandler>("/");
  //HTTPServerAddHandler<RPCHandler>("/rpc");
  //lcd.locate(0,0);
  //lcd.printf("%s",eth.getIPAddress());
  HTTPServerStart(80);
}

static volatile int8_t mouse_button, mouse_x, mouse_y, mouse_z;
static uint16_t cursor_x=0, cursor_y=0, mouse_max_x = 0, mouse_max_y = 0;
void mouseEvent(uint8_t buttons, int8_t x, int8_t y, int8_t z)
{
    mouse_button = buttons;
    mouse_x = x;
    mouse_y = y;
    mouse_z = z;
    
    if (mouse_max_x == 0) {
        mouse_max_x = DMBoard::instance().display()->width() - 1;
    }
    if (mouse_max_y == 0) {
        mouse_max_y = DMBoard::instance().display()->height() - 1;
    }
    
    if (x < 0) {
        if (cursor_x > -x) {
            cursor_x += x;
        } else {
            cursor_x = 0;
        }
    } else {
        if ((cursor_x + x) > mouse_max_x) {
            cursor_x = mouse_max_x;
        } else {
            cursor_x += x;
        }
    }
    y = y/8;
    if (y < 0) {
        if (cursor_y > -y) {
            cursor_y += y;
        } else {
            cursor_y = 0;
        }
    } else {
        if ((cursor_y + y) > mouse_max_y) {
            cursor_y = mouse_max_y;
        } else {
            cursor_y += y;
        }
    }
    
    //Chip_LCD_Cursor_SetPos(LPC_LCD, cursor_x, cursor_y);
    LPC_LCD->CRSR_XY = (cursor_x & 0x3FF) | ((cursor_y & 0x3FF) << 16);
}

#define LCD_CURSOR_32x32 0
#define LCD_CURSOR_64x64 1
#define CURSOR_SIZE  LCD_CURSOR_32x32
#define CURSOR_H_SIZE 32
#define CURSOR_V_SIZE 32
#define CURSOR_NUM   0    
#define CURSOR_H_OFS (10)
#define CURSOR_V_OFS (6)

const unsigned char __attribute__ ((aligned(4))) Cursor[(CURSOR_H_SIZE / 4) * CURSOR_V_SIZE] =
{
	0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAA, 0xFA, 0xAA, 0xAA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAB, 0xFE, 0xAA, 0xAA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAB, 0xFE, 0xAA, 0xAA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAB, 0xFE, 0xAA, 0xAA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAB, 0xFE, 0xAA, 0xAA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAB, 0xFF, 0xEA, 0xAA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAB, 0xFF, 0xFF, 0xAA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAB, 0xFF, 0xFF, 0xFA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAB, 0xFF, 0xFF, 0xFE, 0xAA, 0xAA,
	0xAA, 0xAB, 0xFB, 0xFF, 0xFF, 0xFF, 0xAA, 0xAA,
	0xAA, 0xAB, 0xFF, 0xFF, 0xFF, 0xFF, 0xAA, 0xAA,
	0xAA, 0xAB, 0xFF, 0xFF, 0xFF, 0xFF, 0xAA, 0xAA,
	0xAA, 0xAA, 0xFF, 0xFF, 0xFF, 0xFF, 0xAA, 0xAA,
	0xAA, 0xAA, 0xBF, 0xFF, 0xFF, 0xFF, 0xAA, 0xAA,
	0xAA, 0xAA, 0xBF, 0xFF, 0xFF, 0xFF, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAF, 0xFF, 0xFF, 0xFF, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAF, 0xFF, 0xFF, 0xFE, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAB, 0xFF, 0xFF, 0xFE, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAB, 0xFF, 0xFF, 0xFE, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAA, 0xFF, 0xFF, 0xFA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAA, 0xFF, 0xFF, 0xFA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAA, 0xFF, 0xFF, 0xFA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA
};

void prepareCursor(bool enable) {
	//Chip_LCD_Cursor_Disable(LPC_LCD, 0);
    LPC_LCD->CRSR_CTRL = (CURSOR_NUM << 4);
    
    if (enable) {
        //Chip_LCD_Cursor_Config(LPC_LCD, LCD_CURSOR_32x32, true);
        LPC_LCD->CRSR_CFG = ((true ? 1 : 0) << 1) | CURSOR_SIZE;
        
        //Chip_LCD_Cursor_WriteImage(LPC_LCD, 0, (void *) Cursor);
        {
            int i, j;
            uint32_t *fifoptr, *crsr_ptr = (uint32_t *) Cursor;

            /* Check if Cursor Size was configured as 32x32 or 64x64*/
            if (CURSOR_SIZE == LCD_CURSOR_32x32) {
                i = CURSOR_NUM * 64;
                j = i + 64;
            }
            else {
                i = 0;
                j = 256;
            }
            fifoptr = (uint32_t *) &(LPC_LCD->CRSR_IMG[0]);

            /* Copy Cursor Image content to FIFO */
            for (; i < j; i++) {

                *fifoptr = *crsr_ptr;
                crsr_ptr++;
                fifoptr++;
            }
        }
        
        //Chip_LCD_Cursor_SetClip(LPC_LCD, CURSOR_H_OFS, CURSOR_V_OFS);
        LPC_LCD->CRSR_CLIP = (CURSOR_H_OFS & 0x3F) | ((CURSOR_V_OFS & 0x3F) << 8);
        
        //Chip_LCD_Cursor_SetPos(LPC_LCD, cursor_x, cursor_y);
        
        //Chip_LCD_Cursor_Enable(LPC_LCD, 0);
        LPC_LCD->CRSR_CTRL = (CURSOR_NUM << 4) | 1;
    }
}


#define CIRCBUFF_SIZE 256
static volatile uint8_t circbuff[CIRCBUFF_SIZE];
static volatile uint32_t circbuff_read = 0;
static uint32_t circbuff_write = 0;
void keyEvent(uint8_t key)
{
    circbuff[circbuff_write%CIRCBUFF_SIZE] = key;
    circbuff_write++;
}

bool msdConnected = false;
bool keyboardConnected = false;
bool mouseConnected = false;

#define USB_TASK_PREFIX  "[USB] "
#define USB_CONNECTION_EVENT   (1<<4)
void usbTask(void const* args)
{
  USBHostMSD* msd = new USBHostMSD("usb");
  USBHostKeyboard* keyboard = new USBHostKeyboard();
  USBHostMouse* mouse = new USBHostMouse();
  USBHost* host = USBHost::getHostInst();
  host->signalOnConnections(Thread::gettid(), USB_CONNECTION_EVENT);

  RtosLog* log = DMBoard::instance().logger();
    
  log->printf(USB_TASK_PREFIX"usbTask started\n");
    
  prepareCursor(false);

  while (true) {
    // wait for connect/disconnect message from USBHost
    Thread::signal_wait(USB_CONNECTION_EVENT);
    log->printf(USB_TASK_PREFIX"got USB event\n");
      
    if (msd->connected()) {
      if (!msdConnected) {
        msdConnected = true;
        haveUSBMSD = true;
        log->printf(USB_TASK_PREFIX"USB MassStorage Device - Connected\n");
      }
    } else {
      if (msdConnected) {
        msdConnected = false;
        haveUSBMSD = false;
        log->printf(USB_TASK_PREFIX"USB MassStorage Device - Ejected\n");
      }
      
      if (msd->connect()) {
        msdConnected = true;
        haveUSBMSD = true;
        log->printf(USB_TASK_PREFIX"USB MassStorage Device - Connected\n");
      }      
    }
    
    if (keyboard->connected()) {
      if (!keyboardConnected) {
        keyboardConnected = true;
        log->printf(USB_TASK_PREFIX"USB Keyboard - Connected\n");
        keyboard->attach(keyEvent);
      }
    } else {
      if (keyboardConnected) {
        keyboardConnected = false;
        log->printf(USB_TASK_PREFIX"USB Keyboard - Ejected\n");
      }
      if (keyboard->connect()) {
        keyboardConnected = true;
        log->printf(USB_TASK_PREFIX"USB Keyboard - Connected\n");
        keyboard->attach(keyEvent);
      }
    }
    
    if (mouse->connected()) {
      if (!mouseConnected) {
        mouseConnected = true;
        log->printf(USB_TASK_PREFIX"USB Mouse - Connected\n");
        mouse->attachEvent(mouseEvent);
        prepareCursor(true);
      }
    } else {
      if (mouseConnected) {
        prepareCursor(false);
        mouseConnected = false;
        log->printf(USB_TASK_PREFIX"USB Mouse - Ejected\n");
      }
      if (mouse->connect()) {
        mouseConnected = true;
        log->printf(USB_TASK_PREFIX"USB Mouse - Connected\n");
        mouse->attachEvent(mouseEvent);
        prepareCursor(true);
      }
    }
  }
}



/******************************************************************************
 * Main function
 *****************************************************************************/
int main()
{
  DMBoard::BoardError err;
  DMBoard* board = &DMBoard::instance();
  RtosLog* log = board->logger();
  err = board->init();
  if (err != DMBoard::Ok) {
    log->printf("Failed to initialize the board, got error %d\r\n", err);
    log->printf("\nTERMINATING\n");
    wait_ms(2000); // allow RtosLog to flush messages
    mbed_die();
  }
  
  // select which images to use based on display size
  prepareResources();
  
  board->buzzer(440,50);
  wait_ms(30);
  board->buzzer(660,50);
  wait_ms(30);
  board->buzzer(440,50);
  
  log->printf("\n\n---\nDemo flashed on new Display Modules.\nBuilt: " __DATE__ " at " __TIME__ "\n\n");
  
 
  Thread tAlive(aliveTask);
#if defined(DM_BOARD_USE_DISPLAY)
  Thread tSwim(swimTask, NULL, osPriorityNormal, 8192);
#endif  
  Thread tNetwork(netTask, NULL, osPriorityNormal, 8192);
  Thread tUSBHandler(usbTask, NULL, osPriorityNormal, 8192);
  
  while(1) { 
    // Wait forever (in 1h increments) to prevent the tAlive, tSwim,
    // tNetwork and tUSBHandler thread from being garbage collected.
    Thread::wait(3600*1000);
  }
}
