context: /*
   DBFWriter
   Class for defining a DBF structure and addin data to that structure and
   finally writing it to an OutputStream.
   
   This file is part of DotNetDBF packege.
   
   original author (javadbf): anil@linuxense.com 2004/03/31
   
   license: LGPL (http://www.gnu.org/copyleft/lesser.html)
   
   ported to C# (DotNetDBF): Jay Tuley <jay+dotnetdbf@tuley.name> 6/28/2007
   */
  
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.IO;
  
  namespace DotNetDBF
  {
      public class DBFWriter : DBFBase, IDisposable
      {
          private DBFHeader header;
          private Stream raf;
          private int recordCount;
          private List<object> v_records = new List<object>();
          private Stream _dataMemo;
  
          private string _dataMemoLoc;
  
          /// Creates an empty Object.
          public DBFWriter()
          {
              header = new DBFHeader();
          }
  
  
          /// Creates a DBFWriter which can append to records to an existing DBF file.
          /// @param dbfFile. The file passed in should be a valid DBF file.
          /// @exception Throws DBFException if the passed in file does exist but not a valid DBF file, or if an IO error occurs.
          public DBFWriter(string dbfFile)
          {
              try
              {
                  raf =
                      File.Open(dbfFile,
                          FileMode.OpenOrCreate,
                          FileAccess.ReadWrite);
  
                  DataMemoLoc = Path.ChangeExtension(dbfFile, "dbt");
  
                  /* before proceeding check whether the passed in File object
  				 is an empty/non-existent file or not.
  				 */
                  if (raf.Length == 0)
                  {
                      header = new DBFHeader();
                      return;
                  }
  
                  header = new DBFHeader();
                  header.Read(new BinaryReader(raf));
  
                  /* position file pointer at the end of the raf */
                  raf.Seek(-1, SeekOrigin.End);
                  /* check whether the last byte is 0x1A (end of file marker for dbf files) - in this case move 1 byte back to ignore it when writing new records */ 
                  var lastByte = raf.ReadByte();  /* Advances to end of stream */
                  if (lastByte == DBFFieldType.EndOfData)
                  {
                      raf.Seek(-1, SeekOrigin.End);
                  }
              }
              catch (FileNotFoundException e)
              {
                  throw new DBFException("Specified file is not found. ", e);
              }
              catch (IOException e)
              {
                  throw new DBFException(" while reading header", e);
              }
              recordCount = header.NumberOfRecords;
          }
  
          public DBFWriter(Stream dbfFile)
          {
              raf = dbfFile;
  
              /* before proceeding check whether the passed in File object
  			 is an empty/non-existent file or not.
  			 */
              if (raf.Length == 0)
              {
                  header = new DBFHeader();
                  return;
              }
  
              header = new DBFHeader();
              header.Read(new BinaryReader(raf));
  
              /* position file pointer at the end of the raf */
              raf.Seek(-1, SeekOrigin.End);
              /* check whether the last byte is 0x1A (end of file marker for dbf files) - in this case move 1 byte back to ignore it when writing new records */ 
              var lastByte = raf.ReadByte();  /* Advances to end of stream */
              if (lastByte == DBFFieldType.EndOfData)
              {
                  raf.Seek(-1, SeekOrigin.End);
              }
  
              recordCount = header.NumberOfRecords;
          }
  
          public byte Signature
          {
              get => header.Signature;
              set => header.Signature = value;
          }
  
  
          
          public string DataMemoLoc
          {
              get => _dataMemoLoc;
              set
              {
                  _dataMemoLoc = value;
                  
                  _dataMemo?.Close();
                  _dataMemo = File.Open(_dataMemoLoc,
                      FileMode.OpenOrCreate,
                      FileAccess.ReadWrite);
              }
          }
  
          public Stream DataMemo
          {
              get => _dataMemo;
              set => _dataMemo = value;
          }
  
          public byte LanguageDriver
          {
              set
              {
                  if (header.LanguageDriver != 0x00)
                  {
                      throw new DBFException("LanguageDriver has already been set");
                  }
  
                  header.LanguageDriver = value;
              }
          }
  
  
          public DBFField[] Fields
          {
              get => header.FieldArray;
  
  
              set
              {
                  if (header.FieldArray != null)
                  {
                      throw new DBFException("Fields has already been set");
                  }
  
                  if (value == null
                      || value.Length == 0)
                  {
                      throw new DBFException("Should have at least one field");
                  }
  
                  for (var i = 0; i < value.Length; i++)
                  {
                      if (value[i] == null)
                      {
                          throw new DBFException("Field " + (i + 1) + " is null");
                      }
                  }
  
                  header.FieldArray = value;
  
                  try
                  {
                      if (raf != null
                          && raf.Length == 0)
                      {
                          /*
  						 this is a new/non-existent file. So write header before proceeding
  						 */
                          header.Write(new BinaryWriter(raf));
                      }
                  }
                  catch (IOException e)
                  {
                      throw new DBFException("Error accessing file", e);
                  }
              }
          }
  
          #region IDisposable Members
  
          /// <summary>Performs application-defined tasks associated with freeing, releasing,
          /// or resetting unmanaged resources.</summary>
          /// <filterpriority>2</filterpriority>
          public void Dispose()
          {
              Close();
          }
  
          #endregion
  
          /**
  		 Add a record.
  		 */
  
          public void WriteRecord(params object[] values)
          {
              if (raf == null)
              {
                  throw new DBFException(
                      "Not initialized with file for WriteRecord use, use AddRecord instead");
              }
              AddRecord(values, true);
          }
  
          public void AddRecord(params object[] values)
          {
              if (raf != null)
              {
                  throw new DBFException(
                      "Appending to a file, requires using WriteRecord instead");
              }
              AddRecord(values, false);
          }
  
          private void AddRecord(object[] values, bool writeImmediately)
          {
              if (header.FieldArray == null)
              {
                  throw new DBFException(
                      "Fields should be set before adding records");
              }
  
              if (values == null)
              {
                  throw new DBFException("Null cannot be added as row");
              }
  
              if (values.Length
                  != header.FieldArray.Length)
              {
                  throw new DBFException(
                      "Invalid record. Invalid number of fields in row");
              }
  
              for (var i = 0; i < header.FieldArray.Length; i++)
              {
                  var fld = header.FieldArray[i];
                  var val = values[i];
  
                  if (val is null || val is DBNull)
                  {
                      continue;
                  }
  
                  void ThrowErrorIfNot<T>()
                  {
                      if (!(val is T))
                      {
                          throw new DBFRecordException($"Invalid value '{val}' for field {i}({fld.Name}{fld.DataType})", 0);
                      }
                  }
  
                  switch (fld.DataType)
                  {
                      case NativeDbType.Char:
                          //ignore all objects have ToString()
                          break;
  
                      case NativeDbType.Logical:
                          ThrowErrorIfNot<bool>();
                          break;
  
                      case NativeDbType.Numeric:
                          ThrowErrorIfNot<IConvertible>();
                          break;
  
                      case NativeDbType.Date:
                          ThrowErrorIfNot<DateTime>();
                          break;
  
                      case NativeDbType.Float:
                           ThrowErrorIfNot<IConvertible>();
                          break;
                      case NativeDbType.Memo:
                          ThrowErrorIfNot<MemoValue>();
                          break;
                      default:
                          throw new ArgumentOutOfRangeException();
                  }
              }
  
              if (!writeImmediately)
              {
                  v_records.Add(values);
              }
              else
              {
                  try
                  {
                      WriteRecord(new BinaryWriter(raf), values);
                      recordCount++;
                  }
                  catch (IOException e)
                  {
                      throw new DBFException(
                          "Error occured while writing record. ", e);
                  }
              }
          }
  
          ///Writes the set data to the OutputStream.
          public void Write(Stream tOut)
          {
              try
              {
                  var outStream = new BinaryWriter(tOut);
  
                  header.NumberOfRecords = v_records.Count;
                  header.Write(outStream);
  
                  /* Now write all the records */
                  var t_recCount = v_records.Count;
                  for (var i = 0; i < t_recCount; i++)
                  {
                      /* iterate through records */
  
                      var t_values = (object[]) v_records[i];
  
                      WriteRecord(outStream, t_values);
                  }
  
                  outStream.Write(DBFFieldType.EndOfData);
                  outStream.Flush();
              }
              catch (IOException e)
              {
                  throw new DBFException("Error Writing", e);
              }
          }
  
          public void Close()
          {
              /* everything is written already. just update the header for record count and the END_OF_DATA mark */
              header.NumberOfRecords = recordCount;
              if (raf != null)
              {
                  raf.Seek(0, SeekOrigin.Begin);
                  header.Write(new BinaryWriter(raf));
                  raf.Seek(0, SeekOrigin.End);
                  raf.WriteByte(DBFFieldType.EndOfData);
  
                  raf.Close();
                  _dataMemo?.Close();
  
              } else if (!string.IsNullOrEmpty(DataMemoLoc))
              {
                  DataMemo.Close();
              }
  
          }
  
          private void WriteRecord(BinaryWriter dataOutput, object[] objectArray)
          {
              dataOutput.Write((byte) ' ');
              for (var j = 0; j < header.FieldArray.Length; j++)
              {
                  /* iterate through fields */
  
                  switch (header.FieldArray[j].DataType)
                  {
                      case NativeDbType.Char:
                          if (objectArray[j] != null && objectArray[j] != DBNull.Value)
                          {
                              var str_value = objectArray[j].ToString();
                              dataOutput.Write(
                                  Utils.textPadding(str_value,
                                      CharEncoding,
                                      header.FieldArray[j].
                                          FieldLength
                                  )
                              );
                          }
                          else
                          {
                              dataOutput.Write(
                                  Utils.textPadding("",
                                      CharEncoding,
                                      header.FieldArray[j].
                                          FieldLength
                                  )
                              );
                          }
  
                          break;
  
                      case NativeDbType.Date:
                          if (objectArray[j] != null && objectArray[j] != DBNull.Value)
                          {
                              var tDate = (DateTime) objectArray[j];
  
                              dataOutput.Write(
                                  CharEncoding.GetBytes(tDate.ToString("yyyyMMdd")));
                          }
                          else
                          {
                              dataOutput.Write(
                                  Utils.FillArray(new byte[8], DBFFieldType.Space));
                          }
  
                          break;
  
                      case NativeDbType.Float:
  
                          if (objectArray[j] != null && objectArray[j] != DBNull.Value)
                          {
                              var tDouble = Convert.ToDecimal(objectArray[j]);
                              dataOutput.Write(
                                  Utils.NumericFormating(
                                      tDouble,
                                      CharEncoding,
                                      header.FieldArray[j].FieldLength,
                                      header.FieldArray[j].DecimalCount
                                  )
                              );
                          }
                          else
                          {
                              dataOutput.Write(
                                  Utils.textPadding(
                                      NullSymbol,
                                      CharEncoding,
                                      header.FieldArray[j].FieldLength,
                                      Utils.ALIGN_RIGHT
                                  )
                              );
                          }
  
                          break;
  
                      case NativeDbType.Numeric:
  
                          if (objectArray[j] != null && objectArray[j] != DBNull.Value)
                          {
                              var tDecimal = Convert.ToDecimal(objectArray[j]);
                              dataOutput.Write(
                                  Utils.NumericFormating(
                                      tDecimal,
                                      CharEncoding,
                                      header.FieldArray[j].FieldLength,
                                      header.FieldArray[j].DecimalCount
                                  )
                              );
                          }
                          else
                          {
                              dataOutput.Write(
                                  Utils.textPadding(
                                      NullSymbol,
                                      CharEncoding,
                                      header.FieldArray[j].FieldLength,
                                      Utils.ALIGN_RIGHT
                                  )
                              );
                          }
  
                          break;
                      case NativeDbType.Logical:
  
  
gt:                         if (objectArray[j] != null && objectArray[j] != DBNull.Value)