/*
 * This file is part of the EasyQuantis application
 *
 * Copyright (C) 2004-2011 ID Quantique SA, Carouge/Geneva, Switzerland
 * All rights reserved.
 *
 * ----------------------------------------------------------------------------
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer,
 *    without modification.
 * 2. 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.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
 *
 * ----------------------------------------------------------------------------
 *
 * Alternatively, this software may be distributed under the terms of the
 * GNU General Public License version 2 as published by the Free Software 
 * Foundation.
 *
 * 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-1307 USA
 *
 * ----------------------------------------------------------------------------
 *
 * For history of changes, see ChangeLog.txt
 */

#include <limits>

#include "Quantis/Conversion.h"

#include "Quantis2File.hpp"

using namespace std;

/* Set to a multiple of 64 to avoid any problem */
const size_t idQ::EasyQuantis::Quantis2File::CHUNK_SIZE = 8192u;


idQ::EasyQuantis::Quantis2File::Quantis2File() :
    remaining(0u)
{
}


idQ::EasyQuantis::Quantis2File::~Quantis2File()
{
}


void idQ::EasyQuantis::Quantis2File::CancelRead2File()
{
  canRead = false;
}


unsigned long long idQ::EasyQuantis::Quantis2File::GetRemainingSize() const
{
  return remaining;
}


unsigned long long idQ::EasyQuantis::Quantis2File::GenerateBinaryFile(
    QuantisDeviceType deviceType,
    unsigned int deviceNumber,
    const std::string& filename,
    bool discardContent,
    unsigned long long size)
    throw(std::runtime_error)
{
  idQ::Quantis quantis(deviceType, deviceNumber);
  BinaryFileWriter outputFile(filename, discardContent);

  std::string randomData;
  size_t chunkSize = CHUNK_SIZE;
  remaining = size;

  canRead = true;

  while((remaining > 0u) && canRead)
  {
    // Chunk size
    if (remaining < chunkSize)
    {
      chunkSize = static_cast<size_t>(remaining);
    }

    // Read data
    randomData = quantis.Read(chunkSize);

    // Write data to file
    outputFile.Write(randomData);

    // Update info
    remaining -= chunkSize;
  }

  canRead = false;

  return outputFile.GetSize();
}


unsigned long long idQ::EasyQuantis::Quantis2File::GenerateIntsFile(
    QuantisDeviceType deviceType,
    unsigned int deviceNumber,
    const std::string& filename,
    bool discardContent,
    unsigned long long count,
    const std::string& dataSeparator)
    throw(std::runtime_error)
{
  return GenerateFormattedFile<int>(deviceType,
                                    deviceNumber,
                                    filename,
                                    discardContent,
                                    count,
                                    dataSeparator,
                                    &Quantis::ReadInt);
}


unsigned long long idQ::EasyQuantis::Quantis2File::GenerateIntsFile(
    QuantisDeviceType deviceType,
    unsigned int deviceNumber,
    const std::string& filename,
    bool discardContent,
    unsigned long long count,
    const std::string& dataSeparator,
    int min,
    int max)
    throw(std::runtime_error)
{
  if ((min >= static_cast<int>(numeric_limits<short>::min())) &&
      (max <= static_cast<int>(numeric_limits<short>::max())))
  {
    return GenerateFormattedFile<short>(deviceType,
                                        deviceNumber,
                                        filename,
                                        discardContent,
                                        count,
                                        dataSeparator,
                                        NULL,
                                        &Quantis::ReadShort,
                                        static_cast<short>(min),
                                        static_cast<short>(max));
  }
  else
  {
    return GenerateFormattedFile<int>(deviceType,
                                      deviceNumber,
                                      filename,
                                      discardContent,
                                      count,
                                      dataSeparator,
                                      NULL,
                                      &Quantis::ReadInt,
                                      min,
                                      max);
  }
}


unsigned long long idQ::EasyQuantis::Quantis2File::GenerateFloatsFile(
    QuantisDeviceType deviceType,
    unsigned int deviceNumber,
    const std::string& filename,
    bool discardContent,
    unsigned long long count,
    const std::string& dataSeparator)
    throw(std::runtime_error)
{
  return GenerateFormattedFile<float>(deviceType,
                                      deviceNumber,
                                      filename,
                                      discardContent,
                                      count,
                                      dataSeparator,
                                      &Quantis::ReadFloat);
}


unsigned long long idQ::EasyQuantis::Quantis2File::GenerateFloatsFile(
    QuantisDeviceType deviceType,
    unsigned int deviceNumber,
    const std::string& filename,
    bool discardContent,
    unsigned long long count,
    const std::string& dataSeparator,
    float min,
    float max)
    throw(std::runtime_error)
{
  return GenerateFormattedFile<float>(deviceType,
                                      deviceNumber,
                                      filename,
                                      discardContent,
                                      count,
                                      dataSeparator,
                                      NULL,
                                      &Quantis::ReadFloat,
                                      min,
                                      max);
}


void idQ::EasyQuantis::Quantis2File::GenerateRandomFile(
  idQ::EasyQuantis::Quantis2File* quantis2File,
  const idQ::EasyQuantis::RandomDataGenerationInfo* randomDataGenerationInfo,
  const std::string& filename,
  std::string* errorMessage)
{
  try
  {
    switch (randomDataGenerationInfo->dataType)
    {
      // Binary data
      case RANDOM_DATA_TYPE_BINARY:
        quantis2File->GenerateBinaryFile(randomDataGenerationInfo->deviceType,
                                         randomDataGenerationInfo->deviceNumber,
                                         filename,
                                         true,
                                         randomDataGenerationInfo->count);
        break;

      // Integers
      case RANDOM_DATA_TYPE_INTEGERS:
        if (randomDataGenerationInfo->scaleData)
        {
          quantis2File->GenerateIntsFile(randomDataGenerationInfo->deviceType,
                                         randomDataGenerationInfo->deviceNumber,
                                         filename,
                                         true,
                                         randomDataGenerationInfo->count,
                                         randomDataGenerationInfo->dataSeparator,
                                         static_cast<int>(randomDataGenerationInfo->min),
                                         static_cast<int>(randomDataGenerationInfo->max));
        }
        else
        {
          quantis2File->GenerateIntsFile(randomDataGenerationInfo->deviceType,
                                         randomDataGenerationInfo->deviceNumber,
                                         filename,
                                         true,
                                         randomDataGenerationInfo->count,
                                         randomDataGenerationInfo->dataSeparator);
        }
        break;

      // Floats
      case RANDOM_DATA_TYPE_FLOATS:
        if (randomDataGenerationInfo->scaleData)
        {
          quantis2File->GenerateFloatsFile(randomDataGenerationInfo->deviceType,
                                           randomDataGenerationInfo->deviceNumber,
                                           filename,
                                           true,
                                           randomDataGenerationInfo->count,
                                           randomDataGenerationInfo->dataSeparator,
                                           static_cast<float>(randomDataGenerationInfo->min),
                                           static_cast<float>(randomDataGenerationInfo->max));
        }
        else
        {
          quantis2File->GenerateFloatsFile(randomDataGenerationInfo->deviceType,
                                           randomDataGenerationInfo->deviceNumber,
                                           filename,
                                           true,
                                           randomDataGenerationInfo->count,
                                           randomDataGenerationInfo->dataSeparator);
        }
        break;
    }
  }
  catch (runtime_error& ex)
  {
    errorMessage->assign(ex.what());
  }
}
