/**********************************************************************************************
    Copyright (C) 2007 Oliver Eichler oliver.eichler@gmx.de

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA

  Garmin and MapSource are registered trademarks or trademarks of Garmin Ltd.
  or one of its subsidiaries.

**********************************************************************************************/
#ifndef CUSB_H
#define CUSB_H

#include <string>
#ifdef __APPLE__
#  include <pthread.h>
#  include <IOKit/IOTypes.h>
#  include <IOKit/usb/IOUSBLib.h>
#elif WIN32
#  include <windows.h>
#else
#  include <usb.h>
#endif
#include "ILink.h"
#include "Garmin.h"

#define GARMIN_VID 0x91e
/// is this really special for the 60CSx or valid for all devices?
#define G60CSX_PID 0x3

#define GUSB_PROTOCOL_LAYER     0
#define GUSB_APPLICATION_LAYER  20
#define GUSB_SESSION_START      5

namespace Garmin
{
    /// Garmin's USB protocol
    /**
        This should be kept common to all USB devices. However
        if your device does not fit into the protocol implementation
        at all, subclass it and make your fixes.
    */
    class CUSB : public ILink
    {
        public:
            CUSB();
            virtual ~CUSB();

            /// see ILink::open()
            void open();
            /// see ILink::close()
            void close();
            void close2();
            /// see ILink::read()
            int read(Packet_t& data);
            /// see ILink::write()
            void write(const Packet_t& data);
            /// sync. up sequence
            /**
                This must be called prior to any other request.
            */
            virtual void syncup();

            uint16_t getProductId(){return productId;}
            int16_t  getSoftwareVersion(){return softwareVersion;}
            const std::string& getProductString(){return productString;}
            uint16_t getDataType(int data_no, char tag, uint16_t protocol);

            bool bulkReadActive(){return doBulkRead;}

        protected:
#ifdef __APPLE__
            virtual void start(void);
#else
            virtual void start(struct usb_device *dev);
#endif
            virtual void debug(const char * mark, const Garmin::Packet_t& data);

#ifdef __APPLE__
            UInt8 epBulkIn;
            UInt8 epBulkOut;
            UInt8 epIntrIn;
#else
            /// pointer to all bus definitions
            struct usb_bus * busses;
#ifdef WIN32
            /// the device handler for the Garmin unit
            HANDLE udev;
            DWORD gUSBPacketSize;
#else
            struct usb_dev_handle * udev;
#endif

            int theInterface;
            int epBulkIn;
            int epBulkOut;
            int epIntrIn;
#endif

            int max_tx_size;
            /// switch between interrupt or bulk endpoint during receive
            /**
                This is used by usb_read() internally
            */
            bool doBulkRead;

            uint16_t productId;
            int16_t  softwareVersion;
            std::string productString;
            uint32_t protocolArraySize;
            Protocol_Data_t protocolArray[GUSB_PAYLOAD_SIZE];

#ifdef __APPLE__
        private:
            friend void * readIntrPipe(void *);

            // use the IOKit framework USB implementation on MacOS X
            mach_port_t masterPort;
            io_service_t usbDevRef;
            IOUSBDeviceInterface **usbDev;
            io_service_t usbInterfaceRef;
            IOUSBInterfaceInterface182 **usbIntf;

            // run reads from the interrupt pipe in a separate thread
            pthread_t ir_thread;
            pthread_mutex_t ir_mutex;
            pthread_cond_t ir_cond;
            struct readIntrCmnd_t * readIntrCmnd;
#endif
    };

}
#endif                           //CUSB_H
