/* -------------------------------------------------------------------------- */
/* -                 Astronomical CCD Camera Control                        - */
/* -                   Apogee AltaF Wrapper for C                           - */
/* -------------------------------------------------------------------------- */
/*                                                                            */
/* Copyright (c) 2016 John Kielkopf                                           */
/* kielkopf@louisville.edu                                                    */
/*                                                                            */
/* Library to make Apoggee Alta operations available to xmccd in C            */
/* This file is part of XmCCD.                                                */
/*                                                                            */
/* -------------------------------------------------------------------------- */
/*                                                                            */
/*  The program is based on SimpleExposure.cpp and others                     */
/*    distributed with the Apogee camera library Version 3.0,                 */
/*    David Mills, 2015.                                                      */
/*                                                                            */
/* -------------------------------------------------------------------------- */

/* Needed for the system */

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <unistd.h>
#include <time.h>

/* Needed by cpp namespaces */

#include <iostream>

/* Needed for library */

#include "AltaF.h"
#include "FindDeviceUsb.h"
#include "CameraInfo.h"
#include "apgSampleCmn.h"

/* Needed for xmccd */

#include "xmccd_alta_wrap.h"




/* Local storage */

int exp_width = 0;
int exp_height = 0;

/* Declare the master camera object */

/* All camera functions and parameters are accessed  using   */
/*   The methods and instance variables of this object       */
/* Their declarations were found in ApogeeCamera.h           */


static AltaF cam;

/* Connect to the camera and perform any one-time setup */
/* The returned ccd_id is used by xmccd for barebones diagnostics */
/* Use 0 for USB camera and -1 on failure */

int apogee_open(unsigned int ccd_id)
{

  /* Define quantities and look for the device    */
  /* This is from David Mills SimpleExposure.cpp  */ 
  /* Requires <iostream>                          */
  
  ccd_id = -1;
  std::string ioInterface("usb");
  FindDeviceUsb look4cam;
  std::string msg = look4cam.Find();
          
  std::string addr = apgSampleCmn::GetUsbAddress( msg );
  uint16_t id = apgSampleCmn::GetID( msg );
  uint16_t frmwrRev = apgSampleCmn::GetFrmwrRev( msg );
    
  /* Connect to the camera */
  
  cam.OpenConnection( ioInterface, addr, frmwrRev, id );
  
  /* Set camera to default state or return fail flag */
  
  cam.Init();

            
  /* Print camera information to stdout */
  
  std::cout << "Connected to an AltaF camera " << std::endl;
  apgSampleCmn::printInfo( cam.GetModel(),  cam.GetMaxImgRows(), cam.GetMaxImgCols() );
  
  ccd_id = 0;
  
  return (ccd_id);

}

/* Return the camera operating parameters */

void apogee_get_values(int *roiw, int *roih, int *osw, int *osh, 
  int *binw, int *binh, double *mintemp)
{
  *roiw = cam.GetMaxImgCols();
  *roih = cam.GetMaxImgRows();
  *osw =  (int) cam.GetNumOverscanCols();
  *osh =  0;
  *binw = cam.GetMaxBinCols();
  *binh = cam.GetMaxBinRows();
  *mintemp = -60.; 
}


/* Start an exposure without error checking */

int apogee_expose(double exposure, int x, int y, int height, int width, 
  int shutter_flag)
{
  int status;
  
  exp_width = width;
  exp_height = height;
  cam.SetRoiStartRow(y);
  cam.SetRoiStartCol(x);
  cam.SetRoiNumCols(width);
  cam.SetRoiNumRows(height);
  
  /* No overscan */
  cam.SetDigitizeOverscan(0);
  
  /* Single image per download */
  cam.SetImageCount(1);

  /* Start an exposure */
  cam.StartExposure(exposure,shutter_flag);
  
  /* Check the status of the camera */
  
  status = cam.GetImagingStatus();
  
  /* Error checking would go here */
      
  return(1);
}


/* Stop an exposure without reading out */

int apogee_stop_exposure(void)
{
  cam.StopExposure(false);
  return(1);
}


/* Check camera status */
/* Return 2 if the current exposure is complete */
/* Return 1 if exposure is integrating */
/* Return 0 otherwise */

int apogee_get_status(void)
{

  int status = 0;

  /*   Apg::Status_Exposing           */
  /*   Apg::Status_ImagingActive      */
  /*   Apg::Status_ImageReady         */

  
  /* Test the apogee internal flag for whether an image is in progress */
  
  if (cam.GetImagingStatus() == Apg::Status_Exposing)
  {
    status = 1;
  }  
  
  /* We detect when an image is ready and change the flag */

  if  (cam.GetImagingStatus() == Apg::Status_ImageReady)
  {
    status = 2;
  }
  
  /* Return the relevant flag */
  
  return(status);  
}

/* Return the image based on data size from exposure request */

int apogee_get_image(unsigned short int *image)
{  

  /* Define the image locally */
  
  std::vector<uint16_t> pImageData;
  
  /* Get the image from the camera in driver format */
  
  cam.GetImage (pImageData);
  
  /* Copy the image to our array */
  
  copy(pImageData.begin(), pImageData.end(), image );
  

  return(1);
}


/* Set cooler target temperature */

void apogee_set_cooler_temperature(double newtemp)
{    

  cam.SetCoolerSetPoint(newtemp);

}


/* Set cooler state */

void apogee_set_cooler_state(int state)
{
  
  if (state == 0) 
  {
    cam.SetCooler( (bool) 0 );
  } 
  else 
  {
    cam.SetCooler( (bool) 1 );
  }
}


/* Report cooler state                      */
/*   0 = off                                */
/*   1 = on and not at set point            */              
/*   2 = at set point                       */                     

int apogee_get_cooler_temperature(double *apogeetemp)
{
  int cooler_status;
  int cooler_flag = 0;
  
  cooler_status = cam.GetCoolerStatus();
  
  if (cooler_status == Apg::CoolerStatus_AtSetPoint)
  {
    cooler_flag = 2;
  }
  else if (cooler_status == Apg::CoolerStatus_RampingToSetPoint)
  {
    cooler_flag = 1;
  }
  else if (cam.IsCoolerOn())
  {
    cooler_flag = 1;
  }
  else
  {
    cooler_flag = 0;
  }      

  *apogeetemp = cam.GetTempCcd();

  return (cooler_status);
}

/* Set the fan speed */

void apogee_set_fan(int speed)
{

  if (speed >= 3 )
  {
    cam.SetFanMode(Apg::FanMode_High, true);
  }
  else if (speed == 2 )
  {
    cam.SetFanMode(Apg::FanMode_Medium, true);
  }
  else
  {
    /* This is default on startup */
    
    cam.SetFanMode(Apg::FanMode_Low, true);
  }      

}

/* Actuate a guider relay */

int apogee_guide(int relay, double ontime)
{
  return(0);
}


/* Return pointers to sensor and camera model */

void apogee_get_info(char *sensor, char *camera)
{
  const char * ap_sensor;
  const char * ap_camera;
  
  ap_sensor = cam.GetSensor().c_str();
  ap_camera = cam.GetModel().c_str();
  strcpy( sensor, ap_sensor );
  strcpy( camera, ap_camera );

}

/* Set preflash state  */

void apogee_set_preflash(int preflash)
{

  if (preflash == 1 )
  {
    cam.SetPreFlash(true);
  }
  else
  {
    cam.SetPreFlash(false);
  }

}

/* Get preflash state */

int apogee_get_preflash()
{
  int preflash = 0;
  
  if (cam.GetPreFlash())
  {
    preflash = 1;
  }
  
  return(preflash);
}    
