Public Class frmAccount

  'class-level named constants and variables
  Const INIT_FILE As String = "InitialInfo.txt"
  Const TRANS_FILE As String = "Transactions.txt"
  'variables used for each entry
  Dim isCheck As Boolean
  Dim nameOnChk As String   'name to appear on checks and deposit slips
  Dim lastCkNum As Integer  'number of last check written
  Dim lastDpNum As Integer  'number of last deposit slip written
  Dim curBal As Double      'current balance in account

  Private Sub frmAccount_Load(ByVal sender As System.Object,
                              ByVal e As System.EventArgs) Handles MyBase.Load
    'Set the class-level variables.
    InitializeData()
    'Set the name and balance labels.
    lblName.Text = nameOnChk
    txtBalance.Text = FormatCurrency(curBal)
    'Set the date field to the current date.
    txtDate.Text = CStr(Today)
    SetupCheck()
    ResetInput()
  End Sub

  Private Sub InitializeData()
    If IO.File.Exists(INIT_FILE) Then
      Dim data() As String   'holds the data read from a line of a file
      Dim initFileContents() As String = IO.File.ReadAllLines(INIT_FILE)
      'split the single line of INIT_FILE using the delimiter
      data = initFileContents.First.Split(","c)
      'Recover name to appear on checks, current balance, number of 
      'last check written, and number of last deposit slip processed.
      nameOnChk = data(0)
      curBal = CDbl(data(1))
      lastCkNum = CInt(data(2))
      lastDpNum = CInt(data(3))
      'Possibly update numeric values above by looking at last record of TRANS_FILE
      If IO.File.Exists(TRANS_FILE) Then
        Dim transFileContents() As String = IO.File.ReadAllLines(TRANS_FILE)
        data = transFileContents.Last.Split(","c)
        curBal = CDbl(data(2))
        lastCkNum = CInt(data(3))
        lastDpNum = CInt(data(4))
      End If
    Else
      'INIT_FILE does not exist, so get initial data from user
      Dim sw As IO.StreamWriter
      nameOnChk = InputBox("Name to appear on checks and deposit slips:")
      Try
        curBal = CDbl(InputBox("Starting Balance:"))
        'get numbers of last check and deposit slip
        lastCkNum = CInt(InputBox("Number of first check:")) - 1
        lastDpNum = CInt(InputBox("Number of first deposit slip:")) - 1
        'The single record in the text file records the name to 
        'appear on checks plus the initial data for the account.
        Dim outputLine As String = nameOnChk & "," & curBal & "," &
                                   lastCkNum & "," & lastDpNum
        sw = IO.File.CreateText(INIT_FILE)
        sw.WriteLine(outputLine)
      Catch
        'If a number cannot be converted, then display message and quit.
        MessageBox.Show("Invalid number. Program terminating.", "Error")
        Me.Close()
      Finally
        'Close the writer no matter what happens above.
        sw.Close()
      End Try
    End If
  End Sub

  Private Sub btnRecord_Click(ByVal sender As System.Object,
                              ByVal e As System.EventArgs) Handles btnRecord.Click
    'Store the input into the transactions file.
    Dim amt As Double
    Dim transType As String
    'store only if all required fields are filled and valid
    If DataValid() Then
      amt = CDbl(txtAmount.Text)
      'adjust balance by amount depending on check or deposit slip mode
      If isCheck Then
        curBal = curBal - amt
        lastCkNum = CInt(txtNum.Text)
        transType = "Check"
      Else
        curBal += amt
        lastDpNum = CInt(txtNum.Text)
        transType = "Deposit"
      End If
      txtBalance.Text = FormatCurrency(curBal)
      'string array contains information to be stored
      Dim transOutput() As String = {transType, txtToFrom.Text,
        CStr(curBal), CStr(lastCkNum), CStr(lastDpNum), CStr(amt),
        txtMemo.Text, txtDate.Text}
      Dim sw As IO.StreamWriter = IO.File.AppendText(TRANS_FILE)
      'append the info to the text file, separated by the delimiter
      sw.WriteLine(Join(transOutput, ","c))
      sw.Close()
      'reset input text boxes to blank for next entry
      ResetInput()
    End If
  End Sub

  Function DataValid() As Boolean
    'return True if all data are valid, or display a message if not
    Dim errorMessage As String = ""
    'If one of the two essential pieces of information
    'is missing, assign its name to errorMessage.
    If txtToFrom.Text.Trim = "" Then
      If isCheck Then
        errorMessage = "Pay To"
      Else
        errorMessage = "Source"
      End If
      txtToFrom.Focus()
    ElseIf txtAmount.Text.Trim = "" Then
      errorMessage = "Amount"
      txtAmount.Focus()
    End If
    'if no errors yet, then check syntax of the two numerical fields
    If errorMessage = "" Then
      'check syntax of the amount field (Double)
      Try
        If CDbl(txtAmount.Text) <= 0 Then
          errorMessage = "The amount must be greater than zero."
          txtAmount.Focus()
        End If
      Catch exc As InvalidCastException
        errorMessage = "The amount " & txtAmount.Text & " is invalid."
        txtAmount.Focus()
      End Try
    Else
      errorMessage = "The '" & errorMessage & "' field must be filled."
    End If
    'display error message if available
    If errorMessage = "" Then
      'all required data fields have been filled; recording can proceed
      Return True
    Else
      'advise user of invalid data
      MessageBox.Show(errorMessage & " Please try again.")
      Return False
    End If
  End Function

  Private Sub btnMode_Click(ByVal sender As System.Object,
                            ByVal e As System.EventArgs) Handles btnMode.Click
    'toggle mode between Check and Deposit Slip
    If isCheck Then
      SetupDeposit()
    Else
      SetupCheck()
    End If
    'set fields for next entry
    ResetInput()
  End Sub

  Sub SetupCheck()
    'prepare form for the entry of a check
    isCheck = True
    Me.Text = "Check"  'Sets the title bar of the form
    lblToFrom.Text = "Pay To"
    btnRecord.Text = "&Record This Check"
    btnMode.Text = "&Switch to Deposits"
    Me.BackColor = Color.LightBlue
  End Sub

  Sub SetupDeposit()
    'prepare form for the entry of a deposit
    isCheck = False
    Me.Text = "Deposit Slip"   'Sets the title bar of the form
    lblToFrom.Text = "Source"
    btnRecord.Text = "&Record This Deposit"
    btnMode.Text = "&Switch to Checks"
    Me.BackColor = Color.LightYellow
  End Sub

  Sub ResetInput()
    'reset all text entry fields except date
    txtToFrom.Clear()
    txtAmount.Clear()
    txtMemo.Clear()
    If isCheck Then
      'make txtNum text box reflect next check number
      txtNum.Text = CStr(lastCkNum + 1)
    Else
      'make txtNum text box reflect next deposit slip number
      txtNum.Text = CStr(lastDpNum + 1)
    End If
    'set focus on To/From text box for the next entry
    txtToFrom.Focus()
  End Sub

  Private Sub btnReport_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnReport.Click
    If IO.File.Exists(TRANS_FILE) Then
      Dim transFileContents() As String = IO.File.ReadAllLines(TRANS_FILE)
      Dim query = From trans In transFileContents
                  Let data = trans.Split(","c)
                  Let transDate = CDate(data(7))
                  Let number = FormNumber(data(0), data(3), data(4))
                  Let toFrom = data(1)
                  Let Memo = data(6)
                  Let Amount = FormatCurrency(data(5))
                  Let Balance = FormatCurrency(data(2))
                  Select transDate, number, toFrom, Memo, Amount, Balance
      dgvTransactions.DataSource = query.ToList
      dgvTransactions.CurrentCell = Nothing
      dgvTransactions.Columns("transDate").HeaderText = "Transaction Date"
      dgvTransactions.Columns("number").HeaderText = "Description"
      dgvTransactions.Columns("toFrom").HeaderText = "Recipient or Source"
    Else
      MessageBox.Show("There are no transactions to report.")
    End If
  End Sub

  Function FormNumber(ByVal type As String, ByVal checkNumber As String,
                      ByVal depositNumber As String) As String
    If type = "Check" Then
      Return "Check #" & checkNumber
    Else
      Return "Deposit #" & depositNumber
    End If
  End Function

  Private Sub btnQuit_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnQuit.Click
    Me.Close()     'Exit the program
  End Sub

End Class
