'use strict';
define(function(require) {
	let module = require('components/identimetricsFOTM/module');
	module.factory('attService', ['$http', '$httpParamSerializer', '$filter', '$rootScope', '$timeout', 'forms', 'idMService', function($http, $httpParamSerializer, $filter, $rootScope, $timeout, forms, idMService) {
        pss_get_texts("psx.htmlc.idmFOTM")
        var attService = {
            init: function() {
                attService.clearSettings(1)
                if (attService.ctrlData.args.numlimit > 0 || window.location.pathname.includes("Printing.html")) {
                    if (window.location.pathname.includes("Printing.html")) {
                        var setGridScope = setInterval(function() {
                            if (attService.ctrlData.gwCtrlData.gridScope) {
                                attService.attCodeMap()
                                attService.load()
                                clearInterval(setGridScope)
                            }
                        }, 750)
                    }
                    attService.load()
                }
            },
            clearSettings: function(firstRun) {
                $timeout(function() {
                    if (attService.ctrlData.resolvingError) {
                        attService.ctrlData.resolvingError = false
                    } else {
                        attService.ctrlData.resolvingError = false
                        const noDelete = ["firstRun", "attendanceCode", "printTardySlip", "totalRecs"]
                        for (const [key, value] of Object.entries(attService.ctrlData.entry)) {
                            if (!noDelete.includes(key)) {
                                delete attService.ctrlData.entry[key]
                            }
                        }
                        attService.ctrlData.entry.firstRun = true
                        attService.ctrlData.entry.attendanceCode = attService.ctrlData.entry.attendanceCode
                        attService.ctrlData.entry.printTardySlip = attService.ctrlData.entry.printTardySlip
                        attService.ctrlData.entry.totalRecs = 1

                        attService.ctrlData.students = []
                        if (attService.ctrlData.args.shouldReturnToDefaultAfterScan == "1" || firstRun) {
                            attService.ctrlData.entry.printTardySlip = attService.ctrlData.args.shouldPrintTardySlip

                            if (attService.ctrlData.userArgs.defaultAttendanceCode) {
                                attService.ctrlData.entry.attendanceCode = _.findWhere(attService.ctrlData.args.attCodes, {att_code: attService.ctrlData.userArgs.defaultAttendanceCode})
                            } else {
                                attService.ctrlData.entry.attendanceCode = _.findWhere(attService.ctrlData.args.attCodes, {att_code: attService.ctrlData.args.defaultAttendanceCode})
                            }

                            if (!attService.ctrlData.entry.attendanceCode) {
                                //If no other code can be found, just use the first in the list to prevent a blank
                                attService.ctrlData.entry.attendanceCode = attService.ctrlData.args.attCodes[0]
                            }
                            attService.ctrlData.entry.printTardySlip = attService.checkTardySlip()
                        }
                        if (attService.ctrlData.args.exceptionOverlap == "specific") {
                            attService.ctrlData.entry.specificPeriods = attService.ctrlData.args.exceptionOverlapPeriods
                        }
                        if (attService.ctrlData.args.exceptionOverlap == "all") {
                            attService.ctrlData.entry.specificPeriods = _.pluck(attService.ctrlData.args.periods, "period_number")
                        }
                        attService.ctrlData.manualTime = {}
                        attService.ctrlData.errorFix = []
                        attService.ctrlData.errorCodes = []
                        if (attService.ctrlData.temp_clock_in_clock_out) {
                            attService.ctrlData.args.clock_in_clock_out = attService.ctrlData.temp_clock_in_clock_out
                            delete attService.ctrlData.temp_clock_in_clock_out
                        }
                        attService.setFocus()
                    }
                })
            },
            checkFocus: function() {
                if (!attService.ctrlData.errorCodes || attService.ctrlData.errorCodes.length == 0) {
                    let activeElem = angular.element(document.activeElement)
                    if (!["INPUT","SELECT"].includes(activeElem.prop("tagName"))) {
                        attService.setFocus()
                    }
                }
            },
            setFocus: function() {
                if (angular.element("#studentIdentifier").length > 0) {
                    angular.element("#studentIdentifier").focus()
                }
            },
            startManualTime: function() {
                attService.ctrlData.manualTime = {
                    "dObj": new Date(attService.ctrlData.ticker)
                }
                attService.ctrlData.manualTime.manualDateEntry = attService.ctrlData.manualTime.dObj.toLocaleDateString()
                attService.ctrlData.manualTime.manualTimeEntry = attService.ctrlData.manualTime.dObj.toLocaleTimeString()
                timeEntryWidgetInit()
            },
            updateManualTime: function() {
                attService.ctrlData.manualTime.dObj = new Date(`${attService.ctrlData.manualTime.manualDateEntry} ${attService.ctrlData.manualTime.manualTimeEntry}`)
            },
            errorHandler: function(response, overrideMessage) {
                attService.clearSettings()
                closeLoading()
                let title = "Error"
                let message = "There was an issue with your attendance.  Please go to the front office."
                if (response) {
                    let elems = angular.element(response.data)
                    //title = elems.find("h2").text()
                    if (elems.find("div.aborMessage")) {
                        message += `<br><br>The error message was: ${elems.find("div.abortMessage").text()}`
                    }
                }
                if (overrideMessage) {
                    message = "There was an issue with your attendance.  Please go to the front office."
                }
                psAlert({
                    "title": title,
                    "message": message
                })
            },
            studentsdcidSearch: function(studentIdentifier) {
                let studentList = _.find(attService.ctrlData.args.students, {studentIdentifier: String(Number(studentIdentifier))})
                
                if (studentList) {
                    return studentList
                }
            },
            prepAttendance: function(entry) {
                if (attService.ctrlData.entry.studentIdentifier && !attService.ctrlData.entry.studentIdentifier.match(/^\d+$/)) {
                    if (attService.ctrlData.students.length == 1) {
                        entry.studentIdentifier = attService.ctrlData.students[0].studentIdentifier
                    }
                }

                if (entry.firstRun) {
                    entry.validationResult = false
                    let searchStu = attService.studentsdcidSearch(entry.studentIdentifier)
                    if (searchStu) {
                        for (const [key, value] of Object.entries(searchStu)) {
                            entry[key] = value
                        }
                    }
                    let validationData = {
                        "studentsdcid": entry.studentsdcid,
                        "schoolid": entry.schoolid,
                        "localValidationsOnly": true
                    }

                    entry = attService.testValidation(entry, validationData)

                    attService.ctrlData.selectedEntries = []
                    entry.timeData = idMService.prepareTime(entry, attService.ctrlData.manualTime.dObj || attService.ctrlData.ticker)
                    entry.timeData.att_date = entry.timeData.att_date
                    entry.attType = attService.ctrlData.attType
                    entry.firstRun = false
                }

                return entry
            },
            checkTardySlip: function() {
                if (attService.ctrlData.args.shouldPrintTardySlip == "0") {
                    return "0"
                } else {
                    if (attService.ctrlData.args.kioskMode == 1 && attService.ctrlData.args.kioskSuppressTardySlip == "1") {
                        return "0"
                    } else {
                        return attService.ctrlData.entry.attendanceCode.useForPrinting
                    }
                }
            },
            viewSlip: function(entry, clearEntries) {
                if (clearEntries) {
                    attService.slipCount = 1
                    attService.ctrlData.selectedEntries = []
                }
                attService.ctrlData.selectedEntries.push(entry)
                if (attService.ctrlData.selectedEntries.length == attService.slipCount) {
                    window.setTimeout(function() {
                        attService.printContents()
                    }, 250)
                }
            },
            printContents: function() {
                var slips = $j("#viewTardySlip .selectedEntry")
                slips.each(function() {
                    var contents = $j(this).prop("outerHTML")
                    var styling = $j("#idMFOTM_slipStyle").prop("outerHTML")
                    var a = window.open("", "", "height=1000, width=1000")
                    a.document.write("<html><head>")
                    a.document.write(`${styling}`)
                    a.document.write("</head><body onafterprint='window.close()'>")
                    a.document.write(contents)
                    a.document.write("</body></html>")
                    a.document.close()
                    a.print()
        
                    a.onfocus = function() {
                        a.close()
                    }
        
                    a.onclick = function() {
                        a.close()
                    }
        
                    a.afterprint = function() {
                        a.close()
                    }
        
                    a.onafterprint = function() {
                        a.close()
                    }
                })
            },
            dailyCopy: function(entry, dailyRec, exceptionMode) {
                var dailyEntry = _.clone(entry)
                if (exceptionMode) {
                    dailyEntry.dailyException = 1
                    dailyEntry.oldAttType = dailyEntry.attType
                    attService.ctrlData.temp_clock_in_clock_out = attService.ctrlData.args.clock_in_clock_out
                    attService.ctrlData.args.clock_in_clock_out = "0"
                } else {
                    dailyEntry.printTardySlip = "0"
                }
                dailyEntry.attendancedcid = (dailyRec ? dailyRec.attendancedcid : -99)
                dailyEntry.attType = "Daily"
                dailyEntry.att_Time_In = ""
                dailyEntry.att_Time_Out = ""

                return dailyEntry
            },
            prepMeeting: function(entry, currentClass) {
                let retEntry = _.clone(entry)

                retEntry.ccid = currentClass.ccid
                retEntry.bridgePeriod = currentClass.bridgePeriod
                
                if (currentClass.attendancedcid) {
                    retEntry.attendancedcid = currentClass.attendancedcid
                    if (currentClass.att_comment) {
                        retEntry.scanComment = `${currentClass.att_comment}\r\n${retEntry.scanComment || ''}`.trim()
                    }
                }

                return retEntry
            },
            submitAttendance: function(entry) {
                loadingDialog("Submitting attendance...", "identimetricsFOTMEntry")
                if (_.intersection(attService.ctrlData.localValidations,entry.validationResult).length > 0) {
                    attService.failValidation(entry, entry.validationResult)
                } else {
                    attService.validateStudent(entry).then(function(response) {
                        for (const [key, value] of Object.entries(response.data)) {
                            entry[key] = value
                        }
                        if (_.intersection(attService.ctrlData.localValidations,entry.validationResult).length == 0) {
                            entry = attService.testValidation(entry, response.data)
                        }
                        if (entry.validationResult === true) {
                            if (entry.attType == "Meeting") {
                                attService.slipCount = entry.currentClasses.length
                                attService.ctrlData.selectedEntries = []
                                if (attService.ctrlData.args.clock_in_clock_out == "1" && ["B","C"].includes(entry.attendanceCode.clockInOutPlan)) {
                                    attService.createCICO(entry).success(function() {
                                        entry.ccids = "'" + _.pluck(entry.currentClasses, "ccid").join("','") + "'"
                                        entry.periodids = "'" + _.pluck(entry.currentClasses, "periodid").join("','") + "'"
                                        attService.getAttDCID(entry).then(function(response) {
                                            response.data.pop()
                                            response.data.forEach(function(res) {
                                                if (res.att_mode_code == "ATT_ModeMeeting") {
                                                    let assocClass = _.findWhere(entry.currentClasses, {ccid: res.ccid})
                                                    if (assocClass) {
                                                        assocClass.attendancedcid = res.attendancedcid
                                                    }
                                                }
                                            })
                                            attService.processMeetings(entry)
                                        })
                                    }).error(function(errorResponse){
                                        attService.errorHandler(errorResponse)
                                    })
                                } else {
                                    attService.processMeetings(entry)
                                }
                            } else {
                                attService.slipCount = 1
                                attService.ctrlData.selectedEntries = []
                                attService.startSubmit(entry)
                            }
                        } else {
                            attService.failValidation(entry, entry.validationResult)
                        }
                    })
                }
            },
            validateStudent: function(entry) {
                let submitParams = {
                    studentid: entry.studentid,
                    schoolid: entry.schoolid,
                    att_date: entry.timeData.att_date
                }
                return $http({
                    url: "/admin/attendance/functions/idm_helper.html?helperType=validateStudent",
                    method: "GET",
                    params: submitParams,
                    headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" }
                })
            },
            testValidation: function(entry, validationData, noExceptions) {
                let potentialExceptions = ["noClass", "overlap", "multiSection"]
                entry.validationResult = []

                if (!validationData || !validationData.schoolid) {
                    entry.validationResult = ["noStudent"]
                    return entry
                }
                if (!(validationData.schoolid == attService.ctrlData.args.curschoolid)) {
                    entry.validationResult = ["wrongSchool"]
                    return entry
                }

                if (validationData.localValidationsOnly) {
                    entry.validationResult = false
                    return entry
                } else {
                    if (!(validationData.schedule.length > 0)) {
                        entry.validationResult = ["noSchedule"]
                        return entry
                    }
                    if (entry.attType == "Meeting") {
                        entry.currentClasses = []
                        entry.otherClasses = []
                        entry.useSpecificPeriod = false
                        validationData.schedule.forEach(function(enrollment) {
                            let startTime = new Date(`${entry.timeData.att_date}T${enrollment.start_time}:00`)
                            let endTime = new Date(`${entry.timeData.att_date}T${enrollment.end_time}:00`)
                            if (!enrollment.label) {
                                enrollment.label = `${enrollment.external_expression} ${enrollment.course_name} ${enrollment.teacher}`
                                enrollment.period_number = String(enrollment.period_number)
                                enrollment.ccid = String(enrollment.ccid)
                                enrollment.start_timeObj = startTime
                                enrollment.end_timeObj = endTime
                                enrollment.start_timeMS = startTime.getTime()
                                enrollment.end_timeMS = endTime.getTime()
                                enrollment.runTime = endTime-startTime
                                enrollment.totalRunTime = enrollment.runTime
                            }
                            if (enrollment.start_timeMS <= entry.timeData.useTime.getTime() && entry.timeData.useTime.getTime() <= enrollment.end_timeMS) {
                                if (entry.specificPeriods && entry.specificPeriods.includes(enrollment.period_number)) {
                                    entry.useSpecificPeriod = true
                                    if (entry.specificSections && entry.specificSections.length > 0) {
                                        if (entry.specificSections.includes(enrollment.ccid)) {
                                            entry.currentClasses.push(enrollment)
                                        } else if (entry.multiSection && !entry.multiSection[enrollment.period_number]) {
                                            entry.currentClasses.push(enrollment)
                                        }
                                    } else {
                                        entry.currentClasses.push(enrollment)
                                    }
                                } else if (entry.specificSections && entry.specificSections.length > 0) {
                                    if (entry.specificSections.includes(enrollment.ccid)) {
                                        entry.currentClasses.push(enrollment)
                                    }
                                } else {
                                    entry.currentClasses.push(enrollment)
                                }
                            }
                        })

                        if (entry.useSpecificPeriod) {
                            entry.currentClasses = _.filter(entry.currentClasses, function(enrollment) {
                                return entry.specificPeriods.includes(enrollment.period_number)
                            })
                        }
                        entry.currentPeriods = _.uniq(_.pluck(entry.currentClasses, "period_number"))
                        entry.currentPeriods = _.map(entry.currentPeriods, function(period) {
                            return _.findWhere(attService.ctrlData.args.periods, {period_number: period})
                        })
                        entry.otherClasses = _.filter(validationData.schedule, function(enrollment) {
                            return !_.findWhere(entry.currentClasses, enrollment)
                        })

                        entry.currentClasses.forEach(function(currentClass) {
                            let otherMeetings = _.where(entry.schedule, {ccid: currentClass.ccid})
                            if (otherMeetings.length > 1) {
                                otherMeetings = _.without(otherMeetings, currentClass)
                                otherMeetings.forEach(function(otherMeeting) {
                                    currentClass.totalRunTime += otherMeeting.runTime
                                })
                            }
                        })
        
                        switch (entry.currentClasses.length) {
                            case 0:
                                entry.validationResult.push("noClass")
                                break
                            case 1:
                                //Desired result
                                break
                            default:
                                let multiSection = {}

                                entry.currentPeriods.forEach(function(classPeriod) {
                                    let periodClasses = _.where(entry.currentClasses, {period_number: classPeriod.period_number})
                                    if (entry.currentPeriods.length > 1 && (!entry.specificPeriods || !entry.specificPeriods.includes(classPeriod.period_number))) {
                                        periodClasses.forEach(function(enrollment) {
                                            if (!entry.specificSections || !entry.specificSections.includes(enrollment.ccid)) {
                                                if (!entry.validationResult.includes("overlap")) {
                                                    entry.validationResult.push("overlap")
                                                }
                                            }
                                        })
                                    }
                                    if (!entry.specificSections || entry.specificSections.length == 0) {
                                        if (periodClasses.length > 1) {
                                            multiSection[classPeriod.period_number] = periodClasses
                                        }
                                    }
                                })

                                if (!(attService.ctrlData.args.clock_in_clock_out == "1" && ["B","C"].includes(entry.attendanceCode.clockInOutPlan)) && !_.isEmpty(multiSection)) {
                                    entry.multiSection = multiSection
                                    entry.validationResult.push("multiSection")
                                }
                        }
                    }
        
                    if (_.intersection(potentialExceptions,entry.validationResult).length > 0 && !entry.triedExceptions && !noExceptions) {
                        if (!entry.exceptedValidations) {
                            entry.exceptedValidations = []
                        }
                        entry.validationResult.forEach(function(result) {
                            entry.exceptedValidations.push(result)
                        })
                        entry = attService.validationExceptions(entry, entry.validationResult)
                        return attService.testValidation(entry, validationData)
                    } else {
                        if (entry.validationResult.length == 0) {
                            entry.validationResult = true
                            return entry
                        } else {
                            if (entry.triedExceptions && !noExceptions) {
                                let clearOverrides = ["specificSections", "specificPeriods", "attType", "timeData"]
                                clearOverrides.forEach(function(override) {
                                    switch (override) {
                                        case "specificPeriods":
                                            //if there isn't an overlap, then we probably want to keep the specificPeriods that have been configured
                                            if (!entry.validationResult.includes("overlap") && entry.specificPeriods) {
                                                switch (attService.ctrlData.args.exceptionOverlap) {
                                                    case "all":
                                                        entry.specificPeriods = _.pluck(attService.ctrlData.args.periods, "period_number")
                                                        break
                                                    case "any":
                                                        //if (entry.specificPeriods) {delete entry.specificPeriods}
                                                        break
                                                    case "specific":
                                                        entry.specificPeriods = attService.ctrlData.args.exceptionOverlapPeriods
                                                        break
                                                }
                                            }
                                            break
                                        case "attType":
                                            if (override == "attType" && entry.oldAttType) {
                                                entry.attType = entry.oldAttType
                                                delete entry.oldAttType
                                            }
                                            break
                                        case "timeData":
                                            if (entry.timeData.firstTime.getTime() != entry.timeData.useTime.getTime()) {
                                                //entry.timeData = idMService.prepareTime(entry, entry.timeData.firstTime)
                                            }
                                            break
                                        default:
                                            delete entry[override]
                                            break
                                    }
                                })
                                return attService.testValidation(entry, validationData, true)
                            }
                            return entry
                        }
                    }
                }
            },
            validationExceptions: function(entry, reasons, override) {
                reasons.forEach(function(reason) {
                    switch (reason) {
                        case "overlap":
                            switch (attService.ctrlData.args.exceptionOverlap) {
                                case "all":
                                    entry.specificPeriods = _.pluck(entry.currentClasses, "period_number")
                                    break
                                case "any":
                                    entry.specificPeriods = [entry.currentClasses[0].period_number]
                                    break
                                case "daily":
                                    entry = attService.dailyCopy(entry, entry.dailyRecord, true)
                                    break;
                                case "shortest":
                                    let curPeriodsShort = _.uniq(_.pluck(entry.currentClasses, "period_number"))
                                    let leastTime = _.min(_.filter(attService.ctrlData.args.periods, function(period) {
                                            return curPeriodsShort.includes(period.period_number)
                                        }), "runTime")
                                    let leastPeriods
                                    if (leastTime) {
                                        leastPeriods = _.pluck(_.where(attService.ctrlData.args.periods, {runTime: leastTime.runTime}), "period_number")
                                        curPeriodsShort = _.intersection(curPeriodsShort, leastPeriods)
                                        if (curPeriodsShort.length > 0) {
                                            entry.specificPeriods = curPeriodsShort
                                        }
                                    }
                                    break
                                case "longest":
                                    let curPeriodsLong = _.uniq(_.pluck(entry.currentClasses, "period_number"))
                                    let mostTime = _.max(_.filter(attService.ctrlData.args.periods, function(period) {
                                            return curPeriodsLong.includes(period.period_number)
                                        }), "runTime")
                                    let mostPeriods
                                    if (mostTime) {
                                        mostPeriods = _.pluck(_.where(attService.ctrlData.args.periods, {runTime: mostTime.runTime}), "period_number")
                                        curPeriodsLong = _.intersection(curPeriodsLong, mostPeriods)
                                        if (curPeriodsLong.length > 0) {
                                            entry.specificPeriods = curPeriodsLong
                                        }
                                    }
                                    break
                                case "specific":
                                    entry.specificPeriods = attService.ctrlData.args.exceptionOverlapPeriods
                                    break
                                default:
                                    break
                            }
                            break
                        case "multiSection":
                            if (entry.attType != "daily") {
                                switch (attService.ctrlData.args.exceptionMultiSection) {
                                    case "all":
                                        entry.specificSections = _.pluck(entry.currentClasses, "ccid")
                                        break
                                    case "any":
                                        entry.specificSections = [entry.currentClasses[0].ccid]
                                        break
                                    case "daily":
                                        entry = attService.dailyCopy(entry, entry.dailyRecord, true)
                                        break;
                                    case "shortest":
                                        let leastTime = _.min(entry.currentClasses, "totalRunTime")
                                        if (leastTime) {
                                            leastTime = leastTime.totalRunTime
                                        }
                                        entry.specificSections = _.pluck(_.where(entry.currentClasses, {totalRunTime: leastTime}), "ccid")
                                        break
                                    case "longest":
                                        let mostTime = _.max(entry.currentClasses, "totalRunTime")
                                        if (mostTime) {
                                            mostTime = mostTime.totalRunTime
                                        }
                                        entry.specificSections = _.pluck(_.where(entry.currentClasses, {totalRunTime: mostTime}), "ccid")
                                        break
                                    case "specific":
                                        //If there is a specificCourse, then we need to return...
                                        //Any specficCourse
                                        //Any enrollment not in a period shared with the specificCourse
                                        let courseEnrollments = _.filter(entry.currentClasses, function(enrollment) {
                                            return attService.ctrlData.args.exceptionMultiSectionCourses.includes(enrollment.course_number)
                                        })
                                        let coursePeriods = _.pluck(courseEnrollments, "period_number")
                                        entry.specificSections = _.pluck(_.filter(entry.currentClasses, function(enrollment) {
                                            return !coursePeriods.includes(enrollment.period_number) || attService.ctrlData.args.exceptionMultiSectionCourses.includes(enrollment.course_number)
                                        }), "ccid")
                                        break
                                    default:
                                        entry.specificSections = []
                                        break
                                }
                            }
                            break
                        case "noClass":
                            let prevPeriods = _.filter(entry.otherClasses, function(otherClass) {
                                return otherClass.end_timeMS < entry.timeData.useTime.getTime()
                            })
                            let nextPeriods = _.filter(entry.otherClasses, function(otherClass) {
                                return otherClass.start_timeMS > entry.timeData.useTime.getTime()
                            })

                            let prevPeriod = _.max(prevPeriods, "end_timeMS")
                            let nextPeriod = _.min(nextPeriods, "start_timeMS")

                            if (prevPeriod != -Infinity) {
                                prevPeriods = _.filter(prevPeriods, function(otherClass) {
                                    return otherClass.end_timeMS == prevPeriod.end_timeMS
                                })
                            }
                            if (nextPeriod != Infinity) {
                                nextPeriods = _.filter(nextPeriods, function(otherClass) {
                                    return otherClass.start_timeMS == nextPeriod.start_timeMS
                                })
                            }

                            let newTime = new Date(entry.timeData.useTime)
                            switch (override || attService.ctrlData.args.exceptionNoClass) {
                                case "daily":
                                    entry = attService.dailyCopy(entry, entry.dailyRecord, true)
                                    break
                                case "begPrev":
                                    if (prevPeriod != -Infinity && prevPeriods.length > 0) {
                                        newTime = new Date(prevPeriod.start_timeMS)
                                        entry.timeData = idMService.prepareTime(entry, newTime)
                                        entry.specificPeriods = _.pluck(prevPeriods, "period_number")
                                    }
                                    break
                                case "begNext":
                                    if (nextPeriod != Infinity && nextPeriods.length > 0) {
                                        newTime = new Date(nextPeriod.start_timeMS)
                                        entry.timeData = idMService.prepareTime(entry, newTime)
                                        entry.specificPeriods = _.pluck(nextPeriods, "period_number")
                                    }
                                    break
                                case "endPrev":
                                    if (prevPeriod != -Infinity && prevPeriods.length > 0) {
                                        newTime = new Date(prevPeriod.end_timeMS)
                                        entry.timeData = idMService.prepareTime(entry, newTime)
                                        entry.specificPeriods = _.pluck(prevPeriods, "period_number")
                                    }
                                    break
                                case "endNext":
                                    if (nextPeriod != Infinity && nextPeriods.length > 0) {
                                        newTime = new Date(nextPeriod.end_timeMS)
                                        entry.timeData = idMService.prepareTime(entry, newTime)
                                        entry.specificPeriods = _.pluck(nextPeriods, "period_number")
                                    }
                                    break
                                case "code":
                                    return attService.validationExceptions(entry, [reason], entry.attendanceCode.exceptionNoClass)
                                    break
                            }
                            break
                    }
                })
                entry.triedExceptions = true
                return entry
            },
            failValidation: function(entry, errorCodes) {
                if (attService.ctrlData.args.kioskMode) {
                    attService.errorHandler(false, "There was an unhandled validation error with your attendance.  Please go to the front office.")
                } else {
                    attService.ctrlData.errorCodes = errorCodes
                    attService.ctrlData.errorFix = []
                    attService.ctrlData.scanComment = ""
                    if (_.isEmpty(attService.ctrlData.manualTime)) {
                        attService.startManualTime()
                    }
                    attService.ctrlData.manualTime.manualDateEntry_override = attService.ctrlData.manualTime.manualDateEntry
                    attService.ctrlData.manualTime.manualTimeEntry_override = attService.ctrlData.manualTime.manualTimeEntry
                    attService.ctrlData.entry.attendanceCode_override = _.clone(attService.ctrlData.entry.attendanceCode)
                    attService.ctrlData.entry.studentIdentifier_override = attService.ctrlData.entry.studentIdentifier
                    attService.ctrlData.currentErrors = []
                    attService.ctrlData.localValidationsOnly = true
                    errorCodes.forEach(function(errorCode) {
                        if (!attService.ctrlData.localValidations.includes(errorCode)) {
                            attService.ctrlData.localValidationsOnly = false
                        }
                        let errorMessage = {
                            "message": "",
                            "details": "",
                            "period": ""
                        }
                        switch (errorCode) {
                            case "noClass":
                                errorMessage.message = `${entry.lastfirst} ${pss_text('psx.htmlc.idmFOTM.error_noClass')} ${entry.timeData.useTime.toLocaleTimeString()} ${pss_text('psx.htmlc.idmFOTM.error_noClass_on')} ${get_localized_date(new Date(`${entry.timeData.att_date}T00:00:00`))}`
                                break
                            case "noSchedule":
                                errorMessage.message = `${entry.lastfirst} ${pss_text('psx.htmlc.idmFOTM.error_noSchedule')} ${get_localized_date(new Date(`${entry.timeData.att_date}T00:00:00`))}`
                                break
                            case "wrongSchool":
                                errorMessage.message = `${entry.lastfirst} ${pss_text('psx.htmlc.idmFOTM.error_wrongSchool')}`
                                break
                            case "noStudent":
                                errorMessage.message = `${pss_text('psx.htmlc.idmFOTM.error_noStudent')} ${entry.studentIdentifier}`
                                break
                            case "overlap":
                                errorMessage.message = `There are multiple periods that overlap at ${entry.timeData.useTime.toLocaleTimeString()} ${pss_text('psx.htmlc.idmFOTM.error_noClass_on')} ${get_localized_date(new Date(`${entry.timeData.att_date}T00:00:00`))}`
                                if (attService.ctrlData.args.exceptionOverlap == "specific") {
                                    errorMessage.details = `Overlapping attendance is configured to save to period(s): ${attService.ctrlData.args.exceptionOverlapPeriods.join(", ")}.  These period(s) are likely not scheduled.`
                                }
                                break
                            case "multiSection":
                                errorMessage.period = _.keys(entry.multiSection)
                                errorMessage.message = `There are multiple sections that meet at ${entry.timeData.useTime.toLocaleTimeString()} ${pss_text('psx.htmlc.idmFOTM.error_noClass_on')} ${get_localized_date(new Date(`${entry.timeData.att_date}T00:00:00`))} during period(s): ${errorMessage.period}`
                                if (attService.ctrlData.args.exceptionMultiSection == "specific") {
                                    errorMessage.details = `Conflicting sections are configured to save to course(s): ${attService.ctrlData.args.exceptionMultiSectionCourses.join(", ")}.  These course(s) are likely not scheduled or do not meet at this time.`
                                }
                                break
                            default:
                                errorMessage.message = pss_text('psx.htmlc.idmFOTM.error_unknown')
                                break
                        }
                        attService.ctrlData.currentErrors.push(errorMessage)
                    })

                    var psDialogHolder = $j("#idMErrorDialog").detach()

                    psDialog({
                        content: psDialogHolder,
                        type: "dialogM",
                        title: `${pss_text('psx.htmlc.idmFOTM.error_resolution_title')} - ${entry.lastfirst} - ${entry.attendanceCode.att_code}`,
                        position: {my: "center", at: "center", of: angular.element("#idM_AT .button-row")},
                        open: function() {
                            if (attService.ctrlData.localValidationsOnly) {
                                attService.ctrlData.errorFix.push("editStudent")
                                $timeout(function() {
                                    $j("#studentIdentifierOverride").focus()
                                }, 100)
                            }
                        },
                        close: function() {
                            attService.clearSettings()
                            $j("#idMFOTMErrorHolder").append(psDialogHolder)
                        },
                        buttons: [
                            {
                                id: "cancelButton",
                                text: pss_text('psx.htmlc.idmFOTM.error_resolution_cancel'),
                                click: function() {
                                    attService.ctrlData.errorCodes = []
                                    delete attService.ctrlData.currentErrors
                                    psDialogClose()
                                }
                            },
                            {
                                id: "resolveButton",
                                text: pss_text('psx.htmlc.idmFOTM.error_resolution_resolve'),
                                click: function() {
                                    attService.ctrlData.resolvingError = true
                                    attService.resolveAttendance(entry, attService.ctrlData.errorFix)
                                    psDialogClose()
                                }
                            }
                        ]
                    })
                }
            },
            resolveAttendance: function(entry, errorFix) {
                entry.triedExceptions = false
                attService.ctrlData.errorFix.forEach(function(fix) {
                    switch (fix) {
                        case "editTime":
                            attService.ctrlData.manualTime.manualDateEntry = attService.ctrlData.manualTime.manualDateEntry_override
                            attService.ctrlData.manualTime.manualTimeEntry = attService.ctrlData.manualTime.manualTimeEntry_override
                            attService.updateManualTime()
                            entry.timeData = idMService.prepareTime(entry, attService.ctrlData.manualTime.dObj)
                            break
                        case "editStudent":
                            entry.studentIdentifier = entry.studentIdentifier_override
                            entry.firstRun = true
                            break
                        case "changeCode":
                            entry.attendanceCode = entry.attendanceCode_override
                            break
                        case "saveDaily":
                            entry.dailyException = 1
                            entry.attType = "Daily"
                            attService.ctrlData.temp_clock_in_clock_out = attService.ctrlData.args.clock_in_clock_out
                            attService.ctrlData.args.clock_in_clock_out = "0"
                            break
                        case "allPeriods":
                            entry.specificPeriods = _.pluck(attServicescope.ctrlData.args.periods, "period_number")
                            break
                        case "specificPeriods":
                            break
                        case "allSections":
                            entry.specificSections = _.pluck(entry.currentClasses, "ccid")
                            entry.ccids = _.pluck(entry.currentClasses, "ccid")
                            break
                        case "specificSections":
                            entry.ccids = []
                            entry.specificSections.forEach(function(ccid) {
                                entry.ccids.push(ccid)
                            })
                            break
                    }
                })
                attService.submitAttendance(entry)
            },
            attCodeMap: function() {
                if (attService.ctrlData.gwCtrlData["gridScope_ctrlData.scanEntries"] && attService.ctrlData.changedCodes) {
                    var codeFilter = _.findWhere(attService.ctrlData.gwCtrlData["gridScope_ctrlData.scanEntries"].gridWidgetCtrlData.filterTracker.filterFieldsInOrder, {fieldKey: "attCode"})
                    if (codeFilter) {codeFilter.initialize()}
                    attService.ctrlData.changedCodes = false
                }
            },
            createCICO: function(entry) {
                if (entry.attendanceCode.clockInOutPlan == "B") {
                    var newType = "in"
                } else {
                    var newType = "out"
                }
                
                if (!entry.timeEntries) {
                    entry.timeEntries = []
                }
                entry.timeEntries.forEach(function(timeEntry) {
                    if (timeEntry.type == "unknown") {
                        entry.timeEntries = _.without(entry.timeEntries, timeEntry)
                    }
                })
                //var existingEntry = _.findWhere(timeEntries, {attendanceId: String(entry.attendanceid), type: newType})
                entry.timeEntries.push({
                    attendanceId: 0,
                    attendanceTimeDcid: 0,
                    comment: null,
                    delete: 0,
                    time: entry.timeData.timeSeconds, //scanTime,
                    type: newType //if plan B: in, if plan C: out
                })

                entry.timeEntries = _.sortBy(entry.timeEntries, function(timeEntry) {
                    return timeEntry.time
                })

                var submitTimeData = {
                    entryDate: attService.ctrlData.args.isoDate,
                    schoolId: attService.ctrlData.args.curschoolid,
                    studentId: entry.studentid,
                    timeEntries: entry.timeEntries
                }
                return $http({
                    url: "/ws/attendance/time",
                    method: "POST",
                    data: JSON.stringify(submitTimeData),
                    headers: {"Content-Type": "application/json", "Accept": "application/json"}
                })
            },
            getAttDCID: function(entry) {
                var submit = {
                    ccids: entry.ccids,
                    periodids: entry.periodids,
                    studentid: entry.studentid,
                    att_date: entry.timeData.att_date
                }
                return $http({
                    url: "/admin/attendance/functions/idm_helper.html?helperType=attdcidHelper",
                    method: "POST",
                    data: $httpParamSerializer(submit),
                    headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" }
                })
            },
            processMeetings: function(entry) {
                entry.currentClasses.forEach(function(currentClass) {
                    let subEntry = attService.prepMeeting(entry, currentClass)
                    
                    attService.startSubmit(subEntry)
                    if (currentClass.bridgePeriod == "1" ) {
                        attService.startSubmit(attService.dailyCopy(entry, entry.dailyRecord))
                    }
                })
            },
            startSubmit: function(entry) {
                if (entry.attType == "Daily") {
                    if (entry.dailyRecord.attendancedcid) {
                        entry.attendancedcid = entry.dailyRecord.attendancedcid
                        entry.attendanceid = entry.dailyRecord.attendanceid
                        if (entry.dailyRecord.att_comment) {
                            entry.scanComment = `${entry.dailyRecord.att_comment}\r\n${entry.scanComment || ''}`.trim()
                        }
                    }
                    attService.prepDailyCICO(entry)
                }
                attService.createAllAtt(entry)
            },
            prepDailyCICO: function(entry) {
                if (attService.ctrlData.attType == "Daily" && attService.ctrlData.args.clock_in_clock_out == "1" && ["B","C"].includes(entry.attendanceCode.clockInOutPlan)) {
                    entry.dailyCICO = 1
                    let dayStartTimeData = idMService.prepareTime(entry, attService.ctrlData.args.daystartTime)
                    let dayEndTimeData = idMService.prepareTime(entry, attService.ctrlData.args.dayendTime)

                    if (entry.timeData.useTime.getTime() < attService.ctrlData.args.daystartTime.getTime()) {
                        entry.timeData = dayStartTimeData
                    }

                    if (entry.attendanceCode.clockInOutPlan == "B") {
                        entry.att_Time_In = entry.timeData
                        entry.att_Time_Out = dayEndTimeData
                    } else {
                        entry.att_Time_In = dayStartTimeData
                        entry.att_Time_Out = entry.timeData
                    }

                    if (entry.dailyRecord.attendance_times && entry.dailyRecord.attendance_times.length > 0) {
                        entry.attendance_timedcid = "-1"
                        entry.dailyRecord.attendance_times.forEach(function(attendance_time) {
                            let startTime = new Date(`${entry.timeData.att_date}T${attendance_time.time_in}:00`)
                            let endTime = new Date(`${entry.timeData.att_date}T${attendance_time.time_out}:00`)
                            if (startTime.getTime() <= entry.timeData.useTime.getTime() && entry.timeData.useTime.getTime() <= endTime.getTime()) {
                                if (entry.attendanceCode.clockInOutPlan == "C") {
                                    entry.att_Time_In = idMService.prepareTime(entry, startTime)
                                    entry.att_Time_Out = entry.timeData
                                    entry.attendance_timedcid = attendance_time.attendance_timedcid
                                } else {
                                    console.log("Time Error.  Cannot make a new record.")
                                }
                            }
                        })
                        entry.att_Time_In = entry.att_Time_In.timeString
                        entry.att_Time_Out = entry.att_Time_Out.timeString
                    } else {
                        entry.att_Time_In = entry.att_Time_In.timeString
                        entry.att_Time_Out = entry.att_Time_Out.timeString
                    }
                }
            },
            createAllAtt: function(entry) {
                if (entry.attendancedcid) {
                    entry.frn = `157${entry.attendancedcid}`
                }
                attService.authAtt(entry).then(function(response2) {
                    attService.createAtt(entry, response2).then(function(response3) {
                        let attData = forms.read(angular.element(response3.data).filter("input"))
                        if (attData && attData.attendancedcid) {
                            if (!entry.attendancedcid) {
                                entry.attendancedcid = attData.attendancedcid.value
                            }
                            attService.createExt(entry, response3).then(function() {
                                if (attService.ctrlData.attType == entry.attType || entry.printTardySlip == "1") {
                                    attService.load(angular.copy(entry))
                                }
                                attService.clearSettings()
                                closeLoading("identimetricsFOTMEntry")
                            })
                        } else {
                            attService.errorHandler(response3)
                        }
                    })
                })
            },
            authAtt: function(entry) {
                let submitObj = {
                    studentid: entry.studentid,
                    studentsdcid: entry.studentsdcid,
                    attType: entry.attType,
                    att_date: entry.timeData.att_date,
                    submitHour24: entry.timeData.submitHour24,
                    submitMinute: entry.timeData.submitMinute,
                    ccid: entry.ccid,
                    printTardySlip: entry.printTardySlip,
                    dailyCICO: entry.dailyCICO,
                    att_Time_In: entry.att_Time_In,
                    att_Time_Out: entry.att_Time_Out
                }
                if (entry.attendancedcid) {
                    submitObj.attendanceid = entry.attendanceid
                    submitObj.frn = `157${entry.attendancedcid}`
                    if (entry.attType == "Daily" && attService.ctrlData.attType == "Daily") {
                        if (entry.attendance_timedcid) {
                            submitObj.time_inAuth = attService.ctrlData.args.dailyCICO.time_in + entry.attendance_timedcid
                            submitObj.time_outAuth = attService.ctrlData.args.dailyCICO.time_out + entry.attendance_timedcid
                            submitObj.attendance_timedcid = entry.attendance_timedcid
                            if (entry.attendance_timedcid == "-1") {
                                submitObj.attendanceidAuth = attService.ctrlData.args.dailyCICO.attendanceid + entry.attendance_timedcid
                            }
                        }
                    }
                }

                return $http({
                    url: "/admin/attendance/functions/idm_helper.html?helperType=attAuth",
                    method: "POST",
                    data: $httpParamSerializer(submitObj),
                    headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" }
                })
            },
            createAtt: function(entry, response) {
                let form = forms.read(angular.element(response.data).filter("input"))

                form.att_date.value = get_localized_date(new Date(`${entry.timeData.att_date}T00:00:00`))
                form.attendance_codeid.value = entry.attendanceCode.id
                if (attService.ctrlData.args.shouldIncludeTimeInComment == "1") {
                    entry.scanText = `${pss_text('psx.htmlc.idmFOTM.scanned')} ${entry.timeData.useTime.toLocaleTimeString()}`.trim()
                    if (entry.timeData.firstTime.getTime() != entry.timeData.useTime.getTime()) {
                        entry.scanText = `${pss_text('psx.htmlc.idmFOTM.scanned')} ${entry.timeData.firstTime.toLocaleTimeString()}`.trim()
                        entry.scanText = `${entry.scanText}\r\n(Saved as ${entry.timeData.useTime.toLocaleTimeString()})`.trim()
                    }
                }
                entry.scanComment = `${entry.scanComment || ''}\r\n${entry.scanText}`.trim()

                form.att_comment.value = `${entry.scanComment}`.trim()
                form.att_mode_code.value = `ATT_Mode${entry.attType}`

                form.studentidQ = {
                    "value": entry.studentid,
                    "name": "studentid"
                }

                form.att_dateQ = {
                    "value": entry.timeData.att_date,
                    "name": "att_date"
                }

                if (entry.printTardySlip == "1") {
                    form.printTardySlip = {
                        "value": "1",
                        "name": "printTardySlip"
                    }
                }

                if (entry.dailyException == 1) {
                    form.dailyExceptionQ = {
                        "value": "1",
                        "name": "dailyException"
                    }
                } 

                if (entry.attendancedcid) {
                    form.attendancedcid = {
                        "value": entry.attendancedcid,
                        "name": "attendancedcid"
                    }
                }

                if (entry.ccid) {
                    form.ccidQ = {
                        "value": entry.ccid,
                        "name": "ccid"
                    }
                }

                if (entry.attType == "Meeting" && form.OH_studentsdcid) {
                    form.OH_studentsdcid.value = entry.studentsdcid
                    form.OH_class_absent.value = form.period_number.value
                    form.OH_course_absent.value = form.course_number.value
                    form.OH_date_absent.value = form.att_date.value
                    if (entry.attendanceCode.clockInOutPlan == "B") {
                        //clock in
                        form.OH_time_in.value = String(entry.timeData.timeSeconds)
                        form.OH_time_out.value = form.end_time.value
                        form.OH_hours_absent.value = Math.round((form.OH_time_in.value - form.start_time.value)/60/60*100)/100
                    } else {
                        //clock out
                        form.OH_time_in.value = form.start_time.value
                        form.OH_time_out.value = String(entry.timeData.timeSeconds)
                        form.OH_hours_absent.value = Math.round((form.start_time.value - form.OH_time_out.value)/60/60*100)/100
                    }
                    form.OH_hours_absent.value = Math.abs(form.OH_hours_absent.value)
                }

                return forms.saveNewAtt(form)
            },
            createExt: function(entry, response) {
                let form = forms.read(angular.element(response.data).filter("input"))
                return forms.save(form)
            },
            load: function(specificEntry) {
                loadingDialog(pss_text('psx.htmlc.idmFOTM.loading'), "identimetricsFOTMReport")

                if (specificEntry) {
                    attService.ctrlData.params.attendancedcid = specificEntry.attendancedcid
                    attService.ctrlData.params.studentid = specificEntry.studentid
                }

                if (attService.ctrlData.reportControls && attService.ctrlData.reportControls.dateStart) {
                    attService.ctrlData.params.dateStart = attService.ctrlData.args.isoFormat(new Date(attService.ctrlData.reportControls.dateStart))
                    attService.ctrlData.params.dateEnd = attService.ctrlData.args.isoFormat(new Date(attService.ctrlData.reportControls.dateEnd))
                }

                $http({
                    url: "/admin/attendance/functions/idm_helper.html?helperType=scanListing",
                    params: attService.ctrlData.params,
                    method: "GET",
                    headers: {"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"}
                }).then(function(response) {
                    response.data.pop()
                    let useData = []
                    response.data.forEach(function(att_date) {
                        let attData = []
                        try {attData = JSON.parse(att_date.attData)}
                        catch {}
                        attData.forEach(function(rec) {
                            rec.forEach(function(dataPoint, index) {
                                if (typeof(dataPoint) == "number") {
                                    rec[index] = String(dataPoint)
                                }
                            })
                            let student = _.findWhere(attService.ctrlData.args.students, {studentid: rec[1]})
                            let attCode = _.findWhere(attService.ctrlData.args.attCodes, {id: rec[3]})

                            if (!student) {
                                //console.log("no student")
                                //console.log(rec)
                            }
                            if (!attCode) {
                                //console.log("no attCode")
                                //console.log(rec)
                            }
                            let dataModel = {
                                scanDate: att_date.att_date,
                                attendancedcid: rec[0],
                                studentid: rec[1],
                                scanTime: rec[2],
                                term: rec[4],
                                codeType: rec[5],
                                typeCount: rec[6],
                                overallCount: rec[7],
                                expression: rec[8],
                                course_name: rec[9],
                                att_comment: rec[10],
                                submittedByFOTM: rec[11],
                                tardySlip: rec[12],
                                R: Number(rec[13]),
                                timeSource: rec[14],
                                selected: true
                            }

                            if (student) {
                                dataModel.lastfirst = student.lastfirst
                                dataModel.student_number = student.student_number
                                dataModel.studentsdcid = student.studentsdcid
                                dataModel.grade_level = student.grade_level
                            }
                            if (dataModel.scanDate && dataModel.scanTime) {
                                var tempDate = new Date(`${dataModel.scanDate}T${dataModel.scanTime}:00`)
                                if (dataModel.timeSource) {
                                    tempDate.setHours(tempDate.getHours()+idmFOTMArgs[dataModel.timeSource])
                                    dataModel.scanTime = tempDate.toLocaleTimeString("en-US", {timeStyle: "short"})
                                    dataModel.scanDateJS = new Date(tempDate)
                                }
                                tempDate.setDate(tempDate.getDate()-((tempDate.getDay()+6) % 7))
                                dataModel.weekStart = new Date(tempDate)
                                tempDate.setDate(tempDate.getDate()+4)
                                dataModel.weekEnd = new Date(tempDate)
                            }
                            if (attCode) {
                                dataModel.attCode = attCode.att_code || attCode.description
                                dataModel.description = attCode.description
                                dataModel.attCodeDetails = attCode
                                if (!attService.ctrlData.map.codes[dataModel.attCode]) {
                                    attService.ctrlData.map.codes[dataModel.description] = dataModel.attCode
                                    attService.ctrlData.changedCodes = true
                                }
                            }
                            if (dataModel.course_name) {
                                dataModel.course_name = dataModel.course_name.replace("&amp;", "&")
                            }
                            
                            useData.push(dataModel)
                        })  
                    })

                    if (attService.ctrlData.params.scanType == "scanPage") {
                        useData = _.sortBy(useData, "scanDateJS").reverse()
                    } else {
                        useData = _.sortBy(useData, "R")
                    }

                    if (attService.ctrlData.changedCodes) {
                        attService.attCodeMap()
                    }

                    if (specificEntry) {
                        var exist = _.findWhere(attService.ctrlData.scanEntries, {attendancedcid: specificEntry.attendancedcid})
                        var newRec = _.findWhere(useData, {attendancedcid: specificEntry.attendancedcid})
                        if (newRec) {
                            if (exist) {
                                attService.ctrlData.scanEntries = _.without(attService.ctrlData.scanEntries, exist)
                                attService.ctrlData.scanEntries.unshift(newRec)
                            } else {
                                attService.ctrlData.scanEntries.unshift(newRec)
                            }
                            
                            if (specificEntry.printTardySlip == "1" && attService.ctrlData.args.shouldPrintTardySlip == "1") {
                                attService.viewSlip(newRec)
                            }
                        }

                        delete attService.ctrlData.params.attendancedcid
                        delete attService.ctrlData.params.studentsdcid
                    } else {
                        attService.ctrlData.scanEntriesUnfiltered = useData
                        attService.ctrlData.scanEntries = useData
                    }
                    attService.setFocus()
                    closeLoading()
                })
            }
        }

        return attService
	}])
});