package parsing

import (
  "errors"
  "encoding/csv"
  "reflect"
  "strconv"
)

type Employee struct {
	EmpID                 uint         `csv:"Emp ID"`
	NamePrefix            string       `csv:"Name Prefix"`
	FirstName             string       `csv:"First Name"`
	MiddleInitial         string       `csv:"Middle Initial"`
	LastName              string       `csv:"Last Name"`
	Gender                Gender       `csv:"Gender"`
	Email                 string       `csv:"E Mail"`
	FathersName           string       `csv:"Father's Name"`
	MothersName           string       `csv:"Mothers's Name"`
	MothersMaidenName     string       `csv:"Mother's Maiden Name"`
	Birthdate             string       `csv:"Date of Birth"`
	BirthTime             string       `csv:"Time of Birth"`
	WeightKg              uint16       `csv:"Weight in Kgs."`
	JoiningDate           string       `csv:"Date of Joining"`
	JoiningQuarter        Quarter      `csv:"Quarter of Joining"`
	JoiningHalf           Half         `csv:"Half of Joining"`
	JoiningYear           uint16       `csv:"Year of Joining"`
	JoiningMonth          uint8        `csv:"Month of Joining"`
	JoiningMonthName      Month        `csv:"Month Name of Joining"`
	JoiningMonthNameShort MonthShort   `csv:"Short Month"`
	JoiningMonthDay       uint8        `csv:"Day of Joining"`
	JoiningWeekDay        WeekDay      `csv:"DOW of Joining"`
	JoiningWeekDayShort   WeekDayShort `csv:"Short DOW"`
	YearsOfService        float32      `csv:"Age in Company (Years)"`
	Salary                uint32       `csv:"Salary"`
	LatestHikePercentage  string       `csv:"Last % Hike"`
	SSN                   string       `csv:"SSN"`
	PhoneNumber           string       `csv:"Phone No."`
	PlaceName             string       `csv:"Place Name"`
	County                string       `csv:"County"`
	City                  string       `csv:"City"`
	State                 string       `csv:"State"`
	Zip                   uint32       `csv:"Zip"`
  Region                string       `csv:"Region"`
	Username              string       `csv:"User Name"`
	Password              string       `csv:"Password"`
	// Empty slot
	// Empty slot
	// Empty slot
}

func UnmarshalEmployees(reader *csv.Reader) ([]Employee, error) {
	reader.Comma = ','
	headers, err := reader.Read()
	if err != nil {
		return nil, errors.New("Couldn't read the headers.")
	}

  e := Employee{}
  refet := reflect.TypeOf(e)
	tagMap := make(map[string]int)
  headersLength := len(headers)
  numFields := refet.NumField()
  for i := 0; i < numFields && i < headersLength; i++ {
    field := refet.Field(i)
    if field.Tag.Get("csv") == headers[i] {
      tagMap[field.Name] = i 
    }
	}


	var employees []Employee
	for {
		record, err := reader.Read()
		if err != nil {
			break
		}

		employee := Employee{}
		reflectionValue := reflect.ValueOf(&employee).Elem()

		for fieldName, index := range tagMap {
			field := reflectionValue.FieldByName(fieldName)
			if field.IsValid() {
				switch field.Type().Name() {
				case "string":
					field.SetString(record[index])
				case "uint":
					if intValue, err := strconv.ParseUint(record[index], 10, 64); err == nil {
						field.SetUint(intValue)
					}
				case "uint8":
					if intValue, err := strconv.ParseUint(record[index], 10, 8); err == nil {
						field.SetUint(intValue)
					}
				case "uint16":
					if intValue, err := strconv.ParseUint(record[index], 10, 16); err == nil {
						field.SetUint(intValue)
					}
				case "uint32":
					if intValue, err := strconv.ParseUint(record[index], 10, 32); err == nil {
						field.SetUint(intValue)
					}
				case "float32":
					if floatValue, err := strconv.ParseFloat(record[index], 32); err == nil {
						field.SetFloat(floatValue)
					}
				case "Gender":
					field.Set(reflect.ValueOf(Gender(rune(record[index][0]))))
				case "Quarter":
					field.Set(reflect.ValueOf(Quarter(string(record[index]))))
				case "Half":
					field.Set(reflect.ValueOf(Half(string(record[index]))))
				case "Month":
					field.Set(reflect.ValueOf(Month(string(record[index]))))
				case "MonthShort":
					field.Set(reflect.ValueOf(MonthShort(string(record[index]))))
				case "WeekDay":
					field.Set(reflect.ValueOf(WeekDay(string(record[index]))))
				case "WeekDayShort":
					field.Set(reflect.ValueOf(WeekDayShort(string(record[index]))))
				}
			} 
		}
		employees = append(employees, employee)
	}

	return employees, nil
}
