package parsing

import (
	"encoding/csv"
	"errors"
	"reflect"
	"strconv"
  "git.cems.dev/cdricms/bdooc/proto"
)

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 "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
}

func (e Employee) MapToProto() *proto.Employee {
  
  employee := &proto.Employee{}

  employee.EmpId = e.EmpID
  employee.NamePrefix = e.NamePrefix
  employee.FirstName = e.FirstName
  employee.MiddleInitial = e.MiddleInitial
  employee.LastName = e.LastName
  employee.Gender = proto.Gender(e.Gender)
  employee.Email = e.Email
  employee.FathersName = e.FathersName
  employee.MothersName = e.MothersName
  employee.MothersMaidenName = e.MothersMaidenName
  employee.Birthdate = e.Birthdate
  employee.BirthTime = e.BirthTime
  employee.WeightKg = uint32(e.WeightKg)
  employee.JoiningDate = e.JoiningDate
  employee.JoiningQuarter = proto.Quarter(e.JoiningQuarter.Uint8())
  employee.JoiningHalf = proto.Half(e.JoiningHalf.Uint8())
  employee.JoiningYear = uint32(e.JoiningYear)
  employee.JoiningMonth = uint32(e.JoiningMonth)
  employee.JoiningMonthName = proto.Month(e.JoiningMonthName.Uint8())
  employee.JoiningMonthNameShort = proto.MonthShort(e.JoiningMonthNameShort.Uint8())
  employee.JoiningMonthDay = uint32(e.JoiningMonthDay)
  employee.JoiningWeekDay = proto.WeekDay(e.JoiningWeekDay.Uint8())
  employee.JoiningWeekDayShort = proto.WeekDayShort(e.JoiningWeekDayShort.Uint8())
  employee.YearsOfService = float32(e.YearsOfService)
  employee.Salary = uint32(e.Salary)
  employee.LatestHikePercentage = e.LatestHikePercentage
  employee.Ssn = e.SSN
  employee.PhoneNumber = e.PhoneNumber
  employee.PlaceName = e.PlaceName
  employee.County = e.County
  employee.City = e.City
  employee.State = e.State
  employee.Zip = uint32(e.Zip)
  employee.Region = e.Region
  employee.Username = e.Username
  employee.Password = e.Password

  return employee
}

func MapToProto(es []Employee) *proto.EmployeeList {

  employeeList := &proto.EmployeeList{}

  for _, employee := range es {
    employeeList.Employees = append(employeeList.Employees, employee.MapToProto())
  }

  return employeeList
}

func MapFromProto(e *proto.Employee) Employee {
  employee := Employee{}
  employee.EmpID = e.EmpId
  employee.NamePrefix = e.NamePrefix
  employee.FirstName = e.FirstName
  employee.MiddleInitial = e.MiddleInitial
  employee.LastName = e.LastName
  employee.Gender = Gender(e.Gender)
  employee.Email = e.Email
  employee.FathersName = e.FathersName
  employee.MothersName = e.MothersName
  employee.MothersMaidenName = e.MothersMaidenName
  employee.Birthdate = e.Birthdate
  employee.BirthTime = e.BirthTime
  employee.WeightKg = uint16(e.WeightKg)
  employee.JoiningDate = e.JoiningDate
  employee.JoiningQuarter = Quarter(e.JoiningQuarter.String())
  employee.JoiningHalf = Half(e.JoiningHalf.String())
  employee.JoiningYear = uint16(e.JoiningYear)
  employee.JoiningMonth = uint8(e.JoiningMonth)
  employee.JoiningMonthName = Month(e.JoiningMonthName.String())
  employee.JoiningMonthNameShort = MonthShort(e.JoiningMonthNameShort.String())
  employee.JoiningMonthDay = uint8(e.JoiningMonthDay)
  employee.JoiningWeekDay = WeekDay(e.JoiningWeekDay.String())
  employee.JoiningWeekDayShort = WeekDayShort(e.JoiningWeekDayShort.String())
  employee.YearsOfService = float32(e.YearsOfService)
  employee.Salary = uint32(e.Salary)
  employee.LatestHikePercentage = e.LatestHikePercentage
  employee.SSN = e.Ssn
  employee.PhoneNumber = e.PhoneNumber
  employee.PlaceName = e.PlaceName
  employee.County = e.County
  employee.City = e.City
  employee.State = e.State
  employee.Zip = uint32(e.Zip)
  employee.Region = e.Region
  employee.Username = e.Username
  employee.Password = e.Password
  return employee
}

func MapFromProtoList(el *proto.EmployeeList) []Employee {

  employees := []Employee{}

  for _, pemployee := range el.GetEmployees() {
    employees = append(employees, MapFromProto(pemployee))
  }

  return employees
}
