Public Class frmPayroll

  Private Sub btnDisplay_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDisplay.Click
    Dim empName As String = ""  'Name of employee
    Dim hrWage As Double        'Hourly wage
    Dim hrsWorked As Double     'Hours worked this week
    Dim allowances As Integer   'Number of withholding allowances for employee
    Dim prevPay As Double       'Total pay for year excluding this week
    Dim mStatus As String = ""  'Marital status: S for Single; M for Married
    Dim pay As Double           'This week's pay before taxes
    Dim totalPay As Double      'Total pay for year including this week
    Dim ficaTax As Double       'FICA tax for this week
    Dim fedTax As Double        'Federal income tax withheld this week
    Dim check As Double         'Paycheck this week (take-home pay)
    'Verify and obtain data, compute payroll, display results
    If Not DataOK() Then
      Dim msg As String = "At least one piece of requested data is missing" &
                          " or is provided improperly."
      MessageBox.Show(msg)
    Else
      InputData(empName, hrWage, hrsWorked, allowances, prevPay, mStatus) 'Task 0
      pay = Gross_Pay(hrWage, hrsWorked)                                  'Task 1
      totalPay = Total_Pay(prevPay, pay)                                  'Task 2
      ficaTax = FICA_Tax(pay, prevPay, totalPay)                          'Task 3
      fedTax = Fed_Tax(pay, allowances, mStatus)                          'Task 4
      check = Net_Check(pay, ficaTax, fedTax)                             'Task 5
      ShowPayroll(empName, pay, totalPay, ficaTax, fedTax, check)         'Task 6
    End If
  End Sub

  Private Sub btnNext_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnNext.Click
    'Clear all text boxes and radio buttons for next employee's data
    txtName.Clear()
    txtWage.Clear()
    txtHours.Clear()
    txtAllowances.Clear()
    txtPriorPay.Clear()
    radSingle.Checked = False
    radMarried.Checked = False
    lstResults.Items.Clear()
    txtName.Focus()
  End Sub

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

  Function DataOK() As Boolean
    'Task 0: Validate data
    If (txtName.Text = "") Or (Not IsNumeric(txtWage.Text)) Or
       (Not IsNumeric(txtHours.Text)) Or (Not IsNumeric(txtAllowances.Text)) Or
       (Not IsNumeric(txtPriorPay.Text)) Or
       ((Not radSingle.Checked) And (Not radMarried.Checked)) Then
      Return False
    Else
      Return True
    End If
  End Function

  Sub InputData(ByRef empName As String, ByRef hrWage As Double,
                ByRef hrsWorked As Double, ByRef allowances As Integer,
                ByRef prevPay As Double, ByRef mStatus As String)
    'Task 0: Validate data
    empName = txtName.Text
    hrWage = CDbl(txtWage.Text)
    hrsWorked = CDbl(txtHours.Text)
    allowances = CInt(txtAllowances.Text)
    prevPay = CDbl(txtPriorPay.Text)
    If radMarried.Checked Then
      mStatus = "M"
    Else
      mStatus = "S"
    End If
  End Sub

  Function Gross_Pay(ByVal hrWage As Double,
                     ByVal hrsWorked As Double) As Double
    'Task 1: Compute weekly pay before taxes
    If hrsWorked <= 40 Then
      Return hrsWorked * hrWage
    Else
      Return 40 * hrWage + (hrsWorked - 40) * 1.5 * hrWage
    End If
  End Function

  Function Total_Pay(ByVal prevPay As Double, ByVal pay As Double) As Double
    'Task 2: Compute total pay before taxes
    Return prevPay + pay
  End Function

  Function FICA_Tax(ByVal pay As Double, ByVal prevPay As Double,
                    ByVal totalPay As Double) As Double
    'Task 3: Compute social security and medicare tax
    Dim socialSecurity As Double       'Social Security tax for this week
    Dim medicare As Double             'Medicare tax for this week
    Dim sum As Double                  'Sum of above two taxes
    Const WAGE_BASE As Double = 106800
    If totalPay <= WAGE_BASE Then
      socialSecurity = 0.062 * pay
    ElseIf prevPay < WAGE_BASE Then
      socialSecurity = 0.062 * (WAGE_BASE - prevPay)
    End If
    medicare = 0.0145 * pay
    sum = socialSecurity + medicare
    Return Math.Round(sum, 2)      'Round to nearest cent
  End Function

  Function Fed_Tax(ByVal pay As Double, ByVal allowances As Integer,
                   ByVal mStatus As String) As Double
    'Task 4.1: Compute federal income tax withheld rounded to 2 decimal places 
    Dim adjPay As Double
    Dim tax As Double           'Unrounded federal tax withheld
    adjPay = pay - (70.19 * allowances)
    If adjPay < 0 Then
      adjPay = 0
    End If
    If mStatus = "S" Then
      tax = TaxSingle(adjPay)   'Task 4.2
    Else
      tax = TaxMarried(adjPay)  'Task 4.3
    End If
    Return Math.Round(tax, 2)   'Round to nearest cent
  End Function

  Function TaxSingle(ByVal adjPay As Double) As Double
    'Task 4.2: Compute federal tax withheld for single person
    Select Case adjPay
      Case 0 To 138
        Return 0
      Case 138 To 200
        Return (0.1 * (adjPay - 138))
      Case 200 To 696
        Return 6.2 + 0.15 * (adjPay - 200)
      Case 696 To 1279
        Return 80.6 + 0.25 * (adjPay - 696)
      Case 1279 To 3338
        Return 226.35 + 0.28 * (adjPay - 1279)
      Case 3338 To 7212
        Return 802.87 + 0.33 * (adjPay - 3338)
      Case Is > 7212
        Return 2081.29 + 0.35 * (adjPay - 7212)
    End Select
  End Function

  Function TaxMarried(ByVal adjPay As Double) As Double
    'Task 4.3: Compute federal tax withheld for married person
    Select Case adjPay
      Case 0 To 303
        Return 0
      Case 303 To 470
        Return 0.1 * (adjPay - 303)
      Case 470 To 1455
        Return 16.7 + 0.15 * (adjPay - 470)
      Case 1455 To 2272
        Return 164.45 + 0.25 * (adjPay - 1455)
      Case 2272 To 4165
        Return 368.7 + 0.28 * (adjPay - 2272)
      Case 4165 To 7321
        Return 898.74 + 0.33 * (adjPay - 4165)
      Case Is > 7321
        Return 1940.22 + 0.35 * (adjPay - 7321)
    End Select
  End Function

  Function Net_Check(ByVal pay As Double, ByVal ficaTax As Double,
                     ByVal fedTax As Double) As Double
    'Task 5: Compute amount of money paid to employee
    Return pay - ficaTax - fedTax
  End Function

  Sub ShowPayroll(ByVal empName As String, ByVal pay As Double,
                  ByVal totalPay As Double, ByVal ficaTax As Double,
                  ByVal fedTax As Double, ByVal check As Double)
    'Task 6: Display results of payroll computations
    lstResults.Items.Clear()
    lstResults.Items.Add("Payroll results for " & empName)
    lstResults.Items.Add("")
    lstResults.Items.Add("Gross pay this period:" & "   " &
                         FormatCurrency(pay))
    lstResults.Items.Add("")
    lstResults.Items.Add("Year-to-date earnings:" & "   " &
                         FormatCurrency(totalPay))
    lstResults.Items.Add("")
    lstResults.Items.Add("FICA tax this period:" & "    " &
                         FormatCurrency(ficaTax))
    lstResults.Items.Add("")
    lstResults.Items.Add("Income tax withheld:" & "     " &
                         FormatCurrency(fedTax))
    lstResults.Items.Add("")
    lstResults.Items.Add("Net pay (check amount):" & "  " &
                         FormatCurrency(check))
  End Sub

End Class
