//
//  QSI583TestCamera.m
//  QSICamera
//
//  Copyright (c) 2010, Joe Shimkus
//   All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//  	• 	Redistributions of source code must retain the above copyright
//      	notice, this list of conditions and the following disclaimer.
//  	• 	Redistributions in binary form must reproduce the above copyright
//        notice, this list of conditions and the following disclaimer in the
//        documentation and/or other materials provided with the distribution.
//  	• 	The name of Joe Shimkus may not be used to endorse or promote
//        products derived from this software without specific prior written
//        permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
//  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
//  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
//  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
//  OR CONTRIBUTORS 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.
//

#import "QSI583TestCameraProtected.h"

#import "QSICommand.h"

#include <memory.h>

/***********************************************************************************************
************************************************************************************************
* QSI583TestCamera Private Methods
************************************************************************************************
***********************************************************************************************/

//
// QSI583TestCamera Private Class Methods
//
@interface QSI583TestCamera (QSI583TestCameraPrivateClassMethods)
@end // @interface QSI583TestCamera (QSI583TestCameraPrivateClassMethods)

@implementation QSI583TestCamera (QSI583TestCameraPrivateClassMethods)
@end // @implementation QSI583TestCamera (QSI583TestCameraPrivateClassMethods)

//
// QSI583TestCamera Private Instance Methods
//
@interface QSI583TestCamera (QSI583TestCameraPrivateInstanceMethods)
@end // @interface QSI583TestCamera (QSI583TestCameraPrivateInstanceMethods)

@implementation QSI583TestCamera (QSI583TestCameraPrivateInstanceMethods)
@end // @implementation QSI583TestCamera (QSI583TestCameraPrivateInstanceMethods)


/***********************************************************************************************
************************************************************************************************
* QSI583TestCamera Infrastructure Methods
************************************************************************************************
***********************************************************************************************/

//
// QSI583TestCamera Infrastructure Class Methods
//
@implementation QSI583TestCamera (QSI583TestCameraInfrastructureClassMethods)
@end // @implementation QSI583TestCamera (QSI583TestCameraInfrastructureClassMethods)

//
// QSI583TestCamera Infrastructure Instance Methods
//
@implementation QSI583TestCamera (QSI583TestCameraInfrastructureInstanceMethods)
@end // @implementation QSI583TestCamera (QSI583TestCameraInfrastructureInstanceMethods)

/***********************************************************************************************
************************************************************************************************
* QSI583TestCamera Overridden Methods
************************************************************************************************
***********************************************************************************************/

//
// QSI583TestCamera Overridden Class Methods
//
@implementation QSI583TestCamera (QSI583TestCameraOverriddenClassMethods)
@end // @implementation QSI583TestCamera (QSI583TestCameraOverriddenClassMethods)

//
// QSI583TestCamera Overridden Instance Methods
//
@implementation QSI583TestCamera (QSI583TestCameraOverriddenInstanceMethods)
- (QSIStatus) checkConnected
{
  return (QSISuccess);
} // end -checkConnected

- (QSIStatus) connect
{
  QSIStatus status  = QSISuccess;

  status = [super connect];

  _isConnected = (status == QSISuccess);

  return (status);
} // end -connect

- (void) dealloc
{
  [_rawImage release];
  [_readoutImage release];
  
  [super dealloc];
} // end -dealloc

- (QSIStatus) disconnect
{
  QSIStatus status  = QSISuccess;

  status = [super disconnect];

  _isConnected = (! (status == QSISuccess));

  return (status);
} // end -disconnect

- (QSIStatus) executeCommand : (QSICommand *) aCommand
{
  QSIStatus status  = QSISuccess;

  // Synchronize the execution of the command so that
  // there's only one outstanding command at a time.

  @synchronized(self)
  {
  }

  return (status);
} // end -executeCommand:

- (QSIStatus) exposeForDuration : (uint32_t) aDuration
                withShutterOpen : (bool) aShutterOpen
{
  QSIStatus status  = QSISuccess;

  status = [super exposeForDuration:aDuration withShutterOpen:aShutterOpen];

  uint16_t  columnBinning;
  uint16_t  rowBinning;
  uint16_t  startingColumn;
  uint16_t  startingRow;
  uint16_t  imageWidth;
  uint16_t  imageHeight;

  if (status == QSISuccess)
  {
    status = [self columnBinning:&columnBinning];
  }

  if (status == QSISuccess)
  {
    status = [self rowBinning:&rowBinning];
  }

  if (status == QSISuccess)
  {
    status = [self exposureStartingColumn:&startingColumn];
  }

  if (status == QSISuccess)
  {
    status = [self exposureStartingRow:&startingRow];
  }

  if (status == QSISuccess)
  {
    status = [self exposureWidth:&imageWidth];
  }

  if (status == QSISuccess)
  {
    status = [self exposureHeight:&imageHeight];
  }

  if (status == QSISuccess)
  {
    [_readoutImage release];
    
    _readoutImage = [[NSMutableData alloc] initWithLength:imageWidth * imageHeight * sizeof(uint16_t)];
    if (_readoutImage == nil)
    {
      status = QSIAllocationFailed;
    }
  }

  if (status == QSISuccess)
  {
    uint16_t  *readoutBuffer  = [_readoutImage mutableBytes];
    uint16_t  *rawBuffer      = [_rawImage mutableBytes];
    
    for (uint32_t row = 0; row < imageHeight; row ++)
    {
      uint32_t  startingPixel = (((startingRow + row) * rowBinning) * 3326) + (startingColumn * columnBinning);

      for (uint32_t column = 0; column < imageWidth; column ++)
      {
        uint16_t  binnedPixel = 0;

        for (uint32_t horizontal = 0; horizontal < columnBinning; horizontal ++)
        {
          for (uint32_t vertical = 0; vertical < rowBinning; vertical ++)
          {
            binnedPixel += *(rawBuffer + startingPixel + (vertical * 3326) + horizontal);
          }
        }

        *(readoutBuffer + ((row * imageWidth) + column)) = binnedPixel;

        startingPixel += columnBinning;
      }
    }

    _readoutImageCurrentPixel = 0;
  }

  return (status);
} // end -exposeForDuration:withShutterOpen:

- (QSIStatus) getAdvancedDetails : (QSICameraAdvancedDetails *) anAdvancedDetails
{
  QSIStatus status  = QSISuccess;

  [self removeAllFilters];

  for (uint8_t filter = 0; filter < QSIMaxFilterCount; filter ++)
  {
    [self addFilter:[[[QSIFilter alloc] initWithName:[NSString stringWithFormat:@"Filter %d", filter + 1]] autorelease]];
  }

  anAdvancedDetails._cameraGainIndex = gainHigh;
  anAdvancedDetails._cameraGainEnabled = YES;

  return (status);
} // end -getAdvancedDetails:

- (QSIStatus) getDetails : (QSICameraDetails *) aDetails
{
  QSIStatus status  = QSISuccess;

  aDetails._arrayColumns              = 3326;
  aDetails._arrayRows                 = 2504;
  aDetails._hasCamera                 = no;
  aDetails._hasFilterWheel            = yes;
  aDetails._hasRelays                 = no;
  aDetails._hasShutter                = yes;
  aDetails._hasTemperatureRegulator   = yes;
  aDetails._maxHorizontalBinning      = 4;
  aDetails._maxVerticalBinning        = 4;
  aDetails._modelName                 = @"QSI 500 Series";
  aDetails._modelNumber               = @"583Test";
  aDetails._numberOfFilters           = 5;
  aDetails._numberOfRowsPerBlock      = 0;
  aDetails._perBlockControl           = no;
  aDetails._powerOfTwoBinning         = no;
  aDetails._serialNumber              = @"123456";
  aDetails._supportsAsymmetricBinning = yes;
  aDetails._pixelWidth                = 670;
  aDetails._pixelHeight               = 670;

  return (status);
} // end -getDetails:

- (QSIStatus) getDeviceState : (QSICameraDeviceState *) aDeviceState
{
  aDeviceState._cameraState = deviceStateIdle;

  return (QSISuccess);
} // end -getDeviceState:

- (QSIStatus) imageIsReady : (bool *) anImageIsReady
{
  QSIStatus status  = QSISuccess;

//   [NSThread sleepForTimeInterval:5];

  *anImageIsReady = yes;

  status = [self fillImageBuffer];

  return (status);
}

- (id) initWithComms : (QSIComms *) aCommsObject
{
  self = [super initWithComms:aCommsObject];
  if (self != nil)
  {
    _rawImage     = nil;
    _readoutImage = nil;

    _rawImageByteCount  = (3326 * 2504) * sizeof(int16_t);
    _rawImage           = [[NSMutableData alloc] initWithLength:_rawImageByteCount];

    if (_rawImage == nil)
    {
      [self release];
      self = nil;
    }

    if (self != nil)
    {
      uint16_t  *rawBuffer  = [_rawImage mutableBytes];
      
      int fd  = open("/dev/urandom", O_RDONLY);
      if (fd != -1)
      {
        read(fd, rawBuffer, _rawImageByteCount);

        close(fd);

        for (uint32_t index = 0; index < (_rawImageByteCount / sizeof(uint16_t)); index ++)
        {
          if ((*(rawBuffer + index) % 1024) == 0)
          {
            *(rawBuffer + index) %= 32768;
          }
          else
          {
            *(rawBuffer + index) %= 256;
          }
        }
      }
      else
      {
        memset(rawBuffer, '\0', _rawImageByteCount);
      }
    }
  }

  return (self);
}

- (bool) isConnected
{
  return (_isConnected);
} // end -isConnected

- (QSIStatus) readImage : (void *) aBuffer
          numberOfBytes : (uint32_t) aNumberOfBytes
{
  QSIStatus status  = QSISuccess;

  int fd;

  fd = open("/dev/urandom", O_RDONLY);
  if (fd == -1)
  {
    status = QSIOpenFailed;
  }

  uint16_t  *readoutBuffer = [_readoutImage mutableBytes];
  
  for (uint32_t index = 0; index < (aNumberOfBytes / sizeof(uint16_t)); index ++)
  {
    uint16_t  pixelValue  = *(readoutBuffer + _readoutImageCurrentPixel);

    _readoutImageCurrentPixel ++;;

    uint16_t random  = 0;

    if (status == QSISuccess)
    {
      read(fd, &random, sizeof(random));
    }

    if ((random % 2) == 0)
    {
      *(((uint16_t *) (aBuffer)) + index) = pixelValue * (1 + (((float) (random % 50)) / 100));
    }
    else
    {
      *(((uint16_t *) (aBuffer)) + index) = pixelValue * (1 - (((float) (random % 50)) / 100));
    }
  }

  if (status == QSISuccess)
  {
    close(fd);
  }

  status = QSISuccess;

  return (status);
} // end -readImage:numberOfBytes:

- (QSIStatus) getTemperature : (QSICameraTemperature *) aTemperature
{
  QSIStatus status  = QSISuccess;
  
  int fd;
  
  fd = open("/dev/urandom", O_RDONLY);
  if (fd == -1)
  {
    status = QSIOpenFailed;
  }
  
  uint16_t random  = 0;
  
  if (status == QSISuccess)
  {
    read(fd, &random, sizeof(random));

    aTemperature._ambientTemperature = ((double) (random % 100)) / 10;
    if ((random % 2) == 0)
    {
      aTemperature._ambientTemperature = -(aTemperature._ambientTemperature);
    }
  }

  if (status == QSISuccess)
  {
    read(fd, &random, sizeof(random));
    
    aTemperature._coolerPower = random % 100;
  }

  if (status == QSISuccess)
  {
    read(fd, &random, sizeof(random));
    
    aTemperature._coolerTemperature = ((double) (random % 100)) / 10;
    if ((random % 2) == 0)
    {
      aTemperature._coolerTemperature = -(aTemperature._coolerTemperature);
    }
  }
  
  aTemperature._coolerState = coolerOn;

  if (status == QSISuccess)
  {
    close(fd);
  }
  
  status = QSISuccess;

  return (status);
} // end -getTemperature:
@end // @implementation QSI583TestCamera (QSI583TestCameraOverriddenInstanceMethods)


/***********************************************************************************************
************************************************************************************************
* QSI583TestCamera Public Methods
************************************************************************************************
***********************************************************************************************/

//
// QSI583TestCamera Public Class Methods
//
@implementation QSI583TestCamera (QSI583TestCameraPublicClassMethods)
@end // @implementation QSI583TestCamera (QSI583TestCameraPublicClassMethods)

//
// QSI583TestCamera Public Instance Methods
//
@implementation QSI583TestCamera (QSI583TestCameraPublicInstanceMethods)
@end // @implementation QSI583TestCamera (QSI583TestCameraPublicInstanceMethods)

/***********************************************************************************************
************************************************************************************************
* QSI583TestCamera Protected Methods
************************************************************************************************
***********************************************************************************************/

//
// QSI583TestCamera Protected Class Methods
//
@implementation QSI583TestCamera (QSI583TestCameraProtectedClassMethods)
@end // @implementation QSI583TestCamera (QSI583TestCameraProtectedClassMethods)

//
// QSI583TestCamera Protected Instance Methods
//
@implementation QSI583TestCamera (QSI583TestCameraProtectedInstanceMethods)
@end // @implementation QSI583TestCamera (QSI583TestCameraProtectedInstanceMethods)

/***********************************************************************************************
************************************************************************************************
* The QSI583TestCamera
************************************************************************************************
***********************************************************************************************/

@implementation QSI583TestCamera
@end // @implementation QSI583TestCamera
