/*
| ==============================================================================
| Copyright (C) 2005-2007 Prosilica.  All Rights Reserved.
|
| Redistribution of this header file, in original or modified form, without
| prior written consent of Prosilica is prohibited.
|
|==============================================================================
|
| This sample code, open the first camera found on the host computer and set it
| for capturing. It then wait for the user to press a key before enqueuing a
| frame and saving it to a TIFF file if the capture was successful
|
|==============================================================================
|
| THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
| WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF TITLE,
| NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR  PURPOSE ARE
| DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
| INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
| OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED  AND ON ANY THEORY OF
| LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
| NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
| EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|==============================================================================
*/

#include "StdAfx.h"
//#include <fstream.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#ifdef _WINDOWS
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#endif

#if defined(_LINUX) || defined(_QNX)
#include <unistd.h>
#include <time.h>
#endif

#include <PvApi.h>
#include <ImageLib.h>

static int Verbose = 0;
#define Dprintf(fmt,...) if (Verbose) printf(fmt, __VA_ARGS__);

// Parameterized camera id
//
static int Id = 0;
static unsigned long UID = 0L;

// Find by camera ip address
//
static char *ip_add = NULL;
 
// camera's data
typedef struct 
{
    unsigned long   UID;
    tPvHandle       Handle;
    tPvFrame        Frame;

} tCamera;

#if defined(_LINUX) || defined(_QNX)
void Sleep(unsigned int time)
{
    struct timespec t,r;
    
    t.tv_sec    = time / 1000;
    t.tv_nsec   = (time % 1000) * 1000000;    
    
    while(nanosleep(&t,&r)==-1)
        t = r;
}
#endif


// wait for a camera to be plugged
void WaitForCamera()
{
    tPvCameraInfo   cameraList[3];

    if (ip_add) {
      uint32_t nbo = (uint32_t)inet_addr(ip_add);
      //nbo = htonl(nbo);
      printf("nbo = %u\n", nbo); fflush(stdout);
      tPvHandle cam;
      int stat = PvCameraOpen(nbo, ePvAccessMonitor, &cam);
      if (ePvErrSuccess == stat) {
        printf("Got it!\n"); fflush(stdout);
      } else {
        printf("Didn't work(%d)\n", stat); fflush(stdout);
      }
      exit(0);
    }
    // printf("waiting for a camera "); fflush(stdout);
    int wtime = 0;
    while(!PvCameraList(cameraList,3,NULL))
    {
      Dprintf("%s", ".");fflush(stdout);
      Sleep(250);
      wtime += 250;
      if (wtime > 10000)
	break;
    }
    Dprintf("%s", "\n");
}

// TODO: Parameterize this so we can ask for a specific camera (Transit cam, etc)
//
// get the first camera found if no UID parameter.
// Otherwise, return true only if we find the specific camera requested.
//
bool CameraGet(tCamera* Camera)
{
  const unsigned int LIST_LENGTH = 3;
    tPvUint32 count,connected;
    tPvCameraInfo list[LIST_LENGTH];

    count = PvCameraList(list,LIST_LENGTH,&connected);
    if(count >= 1)
    {
      // No id parameter to application, use the first camera
      //
      if (!Id)
      {
        Camera->UID = list[0].UniqueId;
        Dprintf("got camera %ld\n",Camera->UID);
        return true;
      }

      // Otherwise, search for the specific camera requested
      //
      else
      {
        for (unsigned int i = 0; i < LIST_LENGTH; i++)
        {
          Camera->UID = list[i].UniqueId;
          if (Camera->UID == UID)
          {
            Dprintf("Camera match found: %ld\n",Camera->UID);
            return true;
          }
        }
      }
    }

    // Camera not found
    return false;
}

// open the camera
bool CameraSetup(tCamera* Camera)
{
  tPvErr stat;

  if (stat = (PvCameraOpen(Camera->UID,ePvAccessMaster,&(Camera->Handle))))
  {
    Dprintf("sorry, there was an error opening the camera: %d\n", stat);
    return false;
  }


  unsigned long MaxStreamSpeed = 4000000L;

  if(stat = (PvAttrUint32Set((Camera->Handle),"StreamBytesPerSecond", MaxStreamSpeed)))
  {
    Dprintf("sorry, there was an error while trying to adjust stream speed: %d\n", stat);
    return false;
  }


  unsigned long MaxSize = 1500L;

  if(stat = PvCaptureAdjustPacketSize((Camera->Handle),MaxSize))
  {
    Dprintf("sorry, there was an error while trying to adjust the packet size: %d\n", stat);
    return false;
  }

  return true;
}

// snap and save a frame from the camera
bool CameraSnap(tCamera* Camera)
{
    if(!PvCaptureQueueFrame(Camera->Handle,&(Camera->Frame),NULL))
    {
      tPvErr stat;
    
      Dprintf("%s", "triggering ...\n");
    
      do
      {  
        stat = PvCommandRun(Camera->Handle,"FrameStartTriggerSoftware");
        if(stat == ePvErrBadSequence)
        {
          Sleep(150);
          Dprintf("%s", "triggering ... (retry)\n");
        }
        
      } while (stat == ePvErrBadSequence);

      Dprintf("%s", "waiting for the frame to be done ...\n");
        
      stat = PvCaptureWaitForFrameDone(Camera->Handle,&(Camera->Frame),4000);
	
	    if (stat == ePvErrTimeout)
	    {
	      Dprintf("%s", "Timeout\n");
	      return false;
	    }
	    else if (stat != ePvErrSuccess)
	    {
	      Dprintf("Other error: %d\n", stat);
	      return false;
	    }

      if(Camera->Frame.Status == ePvErrSuccess)
      {
  	    char *fname = "./ps.b16";
	      unsigned long tsize = sizeof(tPvFrame) + Camera->Frame.ImageSize;
	      Dprintf("got frame, writing %ld bytes to %s\n", tsize, fname);

	      FILE *file = fopen(fname, "wb");
	      if (!file)
  	    {
	        Dprintf("Could not open %s\n", fname);
	        return false;
  	    }
        else
	      {
	        unsigned long nbytes;
	        bool aok = true;

          Dprintf("%s", "Write header...\n");
	        nbytes = fwrite(&(Camera->Frame), 1, sizeof(tPvFrame), file);
          fflush(file);
	        if (nbytes != sizeof(tPvFrame))
	        {
	          Dprintf("Could not write frame header: %ld\n", nbytes);
	          aok = false;
	        }
          if (aok)
          {
            Dprintf("%s", "Write image...\n");
  	        nbytes = fwrite(Camera->Frame.ImageBuffer, 1, Camera->Frame.ImageSize, file);
            fflush(file);
	          if (nbytes != Camera->Frame.ImageSize)
	          {
	            Dprintf("Could not write image: bytes written:%ld error:%d\n", nbytes, errno);
	            aok = false;
	          }
	        }
          Dprintf("%s", "Done\n");
          fflush(stdout);
	        fclose(file);
	        return aok;
  	    }
      }
      else
	    {
	      Dprintf("the frame failed to be captured: Status=%d\n", Camera->Frame.Status);
	      return false;
	    }
    } 
    else
    {
      Dprintf("%s", "failed to enqueue the frame\n");
      return false;
    }

    return true;
}

// setup and start streaming
bool CameraStart(tCamera* Camera)
{
    unsigned long FrameSize = 0;

    // how big should the frame buffers be?
    if(!PvAttrUint32Get(Camera->Handle,"TotalBytesPerFrame",&FrameSize))
    {
        bool failed = false;

        // allocate the buffer for the single frame we need
        Camera->Frame.Context[0]  = Camera;
        Camera->Frame.ImageBuffer = new char[FrameSize];
        if(Camera->Frame.ImageBuffer)
            Camera->Frame.ImageBufferSize = FrameSize;
        else
            failed = true;

        if(!failed)
        {
            // set the camera is capture mode
            if(!PvCaptureStart(Camera->Handle))
            {
		        // set the camera trigger and acquisition mode
		        if(!PvAttrEnumSet(Camera->Handle,"FrameStartTriggerMode","Software") /*&&
			       !PvAttrEnumSet(Camera->Handle,"AcquisitionMode","SingleFrame")*/)
		        {	
        	        // begin image acquisition mode
        	        if(PvCommandRun(Camera->Handle,"AcquisitionStart"))
        	        {
        		        // if that fails, we reset the camera to non capture mode
        		        PvCaptureEnd(Camera->Handle) ;
        		        return false;
        	        }
        	        else
	                    return CameraSnap(Camera);
		        }
		        else
		            return false;
            }
            else
                return false;
        }
        else
            return false;
    }
    else
        return false;
}

// stop streaming
void CameraStop(tCamera* Camera)
{
    PvCommandRun(Camera->Handle,"AcquisitionStop");
    PvCaptureEnd(Camera->Handle);  
}

// unsetup the camera
void CameraUnsetup(tCamera* Camera)
{
    PvCameraClose(Camera->Handle);
    // and free the image buffer of the frame
    delete [] (char*)Camera->Frame.ImageBuffer;
}

int main(int argc, char* argv[])
{

  int c;
  while ((c = getopt(argc, argv, "vi:p:")) != -1)
    switch (c)
    {
    case 'v':
      Verbose = 1;
      break;

    case 'i':
      Id = 1;
      UID = atol(optarg);
      if (UID <= 0)
      {
        Dprintf("bad unique id: %s\n", optarg);
        exit(1);
      }
      break;

    case 'p':
      ip_add = optarg;
      printf("looking for ip: %s...\n", ip_add);
      break;

    default:
      break;
    }

  int retval = 1;

    // initialise the Prosilica API
    if(!PvInitialize())
    { 
      Dprintf("%s", "API initialized, acquiring a camera\n");

        tCamera Camera;

        memset(&Camera,0,sizeof(tCamera));

        // wait for a camera to be plugged
        WaitForCamera();

        // get a camera from the list
        if(CameraGet(&Camera))
        {
            // setup the camera
            if(CameraSetup(&Camera))
            {
                // start streaming from the camera
                if(CameraStart(&Camera))
                {
		            CameraStop(&Camera);
		            retval = 0;
                }
                else
                    Dprintf("%s", "failed to start streaming\n");

                // unsetup the camera
		CameraUnsetup(&Camera);
            }
            else
                Dprintf("%s", "failed to setup the camera\n");
        }
        else
            Dprintf("%s", "failed to find a camera\n");

        // uninitialise the API
        PvUnInitialize();
    }
    else
        Dprintf("%s", "failed to initialise the API\n");

    fflush(NULL);
    return retval;
}
