// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void FieldDataCSVWriter::preflight()
{
  setErrorCondition(0);
  std::stringstream ss;

  if (getFieldDataFile().empty() == true)
  {
    ss <<  ": The output file must be set before executing this filter.";
    addErrorMessage(getHumanLabel(), ss.str(), -1);
    setErrorCondition(-1);
  }

  std::string parentPath = MXAFileInfo::parentPath(getFieldDataFile());
  if (MXADir::exists(parentPath) == false)
  {
    ss.str("");
    ss <<  "The directory path for the output file does not exist.";
    addWarningMessage(getHumanLabel(), ss.str(), -1);
  }

  if (MXAFileInfo::extension(getFieldDataFile()).compare("") == 0)
  {
    setFieldDataFile(getFieldDataFile().append(".dx"));
  }

}
/** @brief (handel RESW,RESB directive and its operands and update location counter)
 */
void PassOne::handelRes(vector<OperandValidator::Operand> args, string &msg, string &operation) {
    if (args.size() < 1) {
        addErrorMessage(msg, "messing operand");
        return;
    } else if (args.size() > 1) {
        addWarningMessage(msg, "args more than required for " + operation);
    }
    if (!args[0].isPosNumber()) {
        addErrorMessage(msg, "operand must be positive numerical value");
    } else {
        int arrSize = autalities::toInteger(args[0].operand);
        int elementSize = operation == "resw" ? 3 : 1;
        locator = addToLocator(locator, arrSize * elementSize);
    }
}
/** @brief (handel BYTE directive and its operands and update location counter)
 */
void PassOne::handelByte(vector<OperandValidator::Operand> args, string &msg) {
    if (args.size() < 1) {
        addErrorMessage(msg, "messing operand");
        return;
    } else if (args.size() > 1) {
        addWarningMessage(msg, "args more than required for byte");
    }
    if (args[0].type == OperandValidator::OperandType::CBYTES) {
        int arrSize = args[0].operand.size();
        locator = addToLocator(locator, arrSize);
    } else if (args[0].type == OperandValidator::OperandType::XBYTES) {
        int arrSize = args[0].operand.size();
        if (arrSize % 2)
            addErrorMessage(msg, "odd length for hex string");
        else
            locator = addToLocator(locator, arrSize / 2);
    } else {
        addErrorMessage(msg, "invalid operand for byte");
    }
}
/** @brief (validate operation field and its operands and update location counter)
 */
void PassOne::handelOperation(vector<OperandValidator::Operand> args, string &msg, string &operation) {
    int format = opTab->getFormat(operation);
    if (format == 3 && input->isFormatFour()) format = 4;
    else if (input->isFormatFour()) {
        addErrorMessage(msg, "invalid conversion to format 4");
    }
    locator = addToLocator(locator, format);
    int numberOfOperand = opTab->getNumberOfOperands(operation);
    string operandsType = opTab->getOperandsType(operation);

    if (args.size() < numberOfOperand) {
        addErrorMessage(msg, "messing operands");
        return;
    } else if (args.size() > numberOfOperand) {
        addWarningMessage(msg, "args more than required for " + operation);
    }
    for (int i = 0; i < numberOfOperand; ++i) {
        if (!args[i].ofType(operandsType[i])) {
            addErrorMessage(msg, operation + " does not support operand type");
            break;
        }
    }
}
// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void RawBinaryReader::dataCheck(bool preflight, size_t voxels, size_t fields, size_t ensembles)
{
  setErrorCondition(0);
  std::stringstream ss;
  VoxelDataContainer* m = getVoxelDataContainer();

  if (getInputFile().empty() == true)
  {
    ss << ClassName() << " needs the Input File Set and it was not.";
    setErrorCondition(-387);
    addErrorMessage(getHumanLabel(), ss.str(), getErrorCondition());
  }
  else if (MXAFileInfo::exists(getInputFile()) == false)
  {
    ss << "The input file does not exist.";
    setErrorCondition(-388);
    addErrorMessage(getHumanLabel(), ss.str(), getErrorCondition());
  }

  if(m_OutputArrayName.empty() == true)
  {
    ss.str("");
    ss << "The Output Array Name is blank (empty) and a value must be filled in for the pipeline to complete.";
    setErrorCondition(-398);
    addErrorMessage(getHumanLabel(), ss.str(), getErrorCondition());
  }

  if (m_NumberOfComponents < 1)
  {
    ss.str("");
    ss << "The number of components must be larger than Zero";
    setErrorCondition(-391);
    addErrorMessage(getHumanLabel(), ss.str(), getErrorCondition());
  }

  if (m_Dimensionality < 1)
  {
    ss.str("");
    ss << "The dimensionality must be larger than Zero";
    setErrorCondition(-389);
    addErrorMessage(getHumanLabel(), ss.str(), getErrorCondition());
  }

  if (  m_Dimensions.x == 0 || m_Dimensions.y == 0 || m_Dimensions.z == 0)
  {
    ss.str("");
    ss << "One of the dimensions has a size less than or Equal to Zero (0). The minimum size must be greater than One (1).";
    setErrorCondition(-390);
    addErrorMessage(getHumanLabel(), ss.str(), getErrorCondition());
  }

  if (true == preflight)
  {
    size_t allocatedBytes = 0;
    IDataArray::Pointer p = IDataArray::NullPointer();
    if (m_ScalarType == Detail::Int8)
    {
      p = Int8ArrayType::CreateArray(voxels, m_NumberOfComponents, m_OutputArrayName);
      allocatedBytes = sizeof(int8_t) * m_NumberOfComponents * m_Dimensions.x * m_Dimensions.y * m_Dimensions.z;
    }
    else if (m_ScalarType == Detail::UInt8)
    {
      p = UInt8ArrayType::CreateArray(voxels, m_NumberOfComponents, m_OutputArrayName);
      allocatedBytes = sizeof(uint8_t) * m_NumberOfComponents * m_Dimensions.x * m_Dimensions.y * m_Dimensions.z;
    }
    else if (m_ScalarType == Detail::Int16)
    {
      p = Int16ArrayType::CreateArray(voxels, m_NumberOfComponents, m_OutputArrayName);
      allocatedBytes = sizeof(int16_t) * m_NumberOfComponents * m_Dimensions.x * m_Dimensions.y * m_Dimensions.z;
    }
    else if (m_ScalarType == Detail::UInt16)
    {
      p = UInt16ArrayType::CreateArray(voxels, m_NumberOfComponents, m_OutputArrayName);
      allocatedBytes = sizeof(uint16_t) * m_NumberOfComponents * m_Dimensions.x * m_Dimensions.y * m_Dimensions.z;
    }
    else if (m_ScalarType == Detail::Int32)
    {
      p = Int32ArrayType::CreateArray(voxels, m_NumberOfComponents, m_OutputArrayName);
      allocatedBytes = sizeof(int32_t) * m_NumberOfComponents * m_Dimensions.x * m_Dimensions.y * m_Dimensions.z;
    }
    else if (m_ScalarType == Detail::UInt32)
    {
      p = UInt32ArrayType::CreateArray(voxels, m_NumberOfComponents, m_OutputArrayName);
      allocatedBytes = sizeof(uint32_t) * m_NumberOfComponents * m_Dimensions.x * m_Dimensions.y * m_Dimensions.z;
    }
    else if (m_ScalarType == Detail::Int64)
    {
      p = Int64ArrayType::CreateArray(voxels, m_NumberOfComponents, m_OutputArrayName);
      allocatedBytes = sizeof(int64_t) * m_NumberOfComponents * m_Dimensions.x * m_Dimensions.y * m_Dimensions.z;
    }
    else if (m_ScalarType == Detail::UInt64)
    {
      p = UInt64ArrayType::CreateArray(voxels, m_NumberOfComponents, m_OutputArrayName);
      allocatedBytes = sizeof(uint64_t) * m_NumberOfComponents * m_Dimensions.x * m_Dimensions.y * m_Dimensions.z;
    }
    else if (m_ScalarType == Detail::Float)
    {
      p = FloatArrayType::CreateArray(voxels, m_NumberOfComponents, m_OutputArrayName);
      allocatedBytes = sizeof(float) * m_NumberOfComponents * m_Dimensions.x * m_Dimensions.y * m_Dimensions.z;
    }
    else if (m_ScalarType == Detail::Double)
    {
      p = DoubleArrayType::CreateArray(voxels, m_NumberOfComponents, m_OutputArrayName);
      allocatedBytes = sizeof(double) * m_NumberOfComponents * m_Dimensions.x * m_Dimensions.y * m_Dimensions.z;
    }

    // Sanity Check Allocated Bytes versus size of file
    uint64_t fileSize = MXAFileInfo::fileSize(m_InputFile);
    int check = SanityCheckFileSizeVersusAllocatedSize(allocatedBytes, fileSize, m_SkipHeaderBytes);
    if (check == -1)
    {
      ss.str("");
      ss << "The file size is " << fileSize << " but the number of bytes needed to fill the array is " << allocatedBytes << ". This condition would cause an error reading the input file.";
      ss << " Please adjust the input parameters to match the size of the file or select a different data file.";
      setErrorCondition(RBR_FILE_TOO_SMALL);
      addErrorMessage(getHumanLabel(), ss.str(), getErrorCondition());
    }
    else if (check == 1)
    {
      ss.str("");
      ss << "The file size is " << fileSize << " but the number of bytes needed to fill the array is " << allocatedBytes << " which is less than the size of the file.";
      ss << " DREAM3D will read only the first part of the file into the array.";
      addWarningMessage(getHumanLabel(), ss.str(), RBR_FILE_TOO_BIG);
    }

    m->addCellData(p->GetName(), p);

    m->setDimensions(m_Dimensions.x, m_Dimensions.y, m_Dimensions.z);
    m->setResolution(m_Resolution.x, m_Resolution.y, m_Resolution.z);
    m->setOrigin(m_Origin.x, m_Origin.y, m_Origin.z);
  }
}
/** @brief (parse input file validate it and fill symtable)
  * print output file with:
  *  1. location counter beside each statements
  *  2. errors or warnings below each statement if any
  *  3. symtable if successfully assembeled
  */
void PassOne::pass() {
    int lineNumber = 1;
    string msg = "";
    bool started = false, noStart = true, noEnd = true;

    /// loop till end statement or no line remains
    while (input->hasNextLine()) {
        /// print line
        outStream << lineNumber << "\t" << autalities::toUp(locator) << "\t";
        outStream << input->getLine() << "\n";
        msg = "";
        // lineNumber++;
        if (noEnd && !input->isCommentLine()) { // not a comment line
            /// handel start statement
            string operation = input->getOperation();
            string operand = input->getOperand();
            auto args = input->getArgs(); // operand subfields

            if (operation == "start") {
                if (!started) {
                    started = true;
                    noStart = false;
                    handelStart(args, msg);
                } else {
                    addErrorMessage(msg, "duplicated start statement");
                }
            } else {
                if (noStart) {
                    noStart = false;
                    started = true;
                    //addErrorMessage(msg, "messing start statement");
                }
                /// handel label and add it to symtable
                string label = input->getLabel();
                if (!label.empty()) {
                    if (symTab->hasLabel(label)) { //duplicate symbol
                        addErrorMessage(msg, "Symbol \'" + label + "\' already defined\n");
                    } else {
                        symTab->insert(label, locator);
                    }
                }
                /// handel operation and operand field
                if (args.empty() && !operand.empty()) { // args empty mean error to match it with any operand types
                    addErrorMessage(msg, "wrong operand field");
                }
                if (!operation.empty()) {
                    if (opTab->hasOperation(operation)) { // valid operation
                        handelOperation(args, msg, operation);
                    } else if (input->isFormatFour()) { // directive with format 4
                        addErrorMessage(msg, "invalid format 4 with directives");
                    } else if (operation == "word") {
                        handelWord(args, msg);
                    } else if (operation == "resw" || operation == "resb") {
                        handelRes(args, msg, operation);
                    } else if (operation == "byte") {
                        handelByte(args, msg);
                    } else if (operation == "end") {
                        noEnd = false;
                    } else if (operation == "org") {
                        handelOrg(args, msg);
                    } else if (operation == "equ") {
                        handelEqu(args, label, msg);
                    } else if (operation == "ltorg") {
                        handelLtorg(msg,lineNumber);
                    } else if (dirTab->contains(operation) && dirTab->notSupported(operation)) {
                        addWarningMessage(msg, "not supported directive");
                    } else if (!dirTab->contains(operation)) {
                        addErrorMessage(msg, "invalid operation code");
                    }
                    if(args.size() > 0 && args[0].isLiteral()){
                        string value = args[0].toHex();
                        literalPool->insert(value);
                    }

                } else {
                    addErrorMessage(msg, "operation field is messing");
                }
            }
            if (!input->isValid())
                addToMessage(msg, input->getErrorMessage());
            outStream << msg;
        }
        lineNumber++;
    }
    if (noEnd) {
        addErrorMessage(msg, "no end found");
        outStream << msg;
    }
    handelLtorg(msg,lineNumber);
    outStream << "****\t**********End of pass 1***********\n";
    if (errorCounter > 0) {
        outStream << ">>> incomplete assembely with " << errorCounter << " errors\n";
    } else {
        printSymTable();
    }
    outStream.close();
    input->close();
}