# -*- coding: latin-1 -*-

import os
import platform
import logging

from PySide2 import QtWidgets, QtCore, QtGui

from . import constants
from . import utils
from . import core

LOG = logging.getLogger(__name__)

def newIcon():
    mayaWindow = utils.mayaMainWindow()

    dialog = NewPortalIconDialog(mayaWindow)
    dialog.show()

def openIcon():
    mayaWindow = utils.mayaMainWindow()

    dialog = OpenIconDialog(mayaWindow)
    dialog.show()

class LogoWidget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(LogoWidget, self).__init__(parent=parent)

        mainLayout = QtWidgets.QHBoxLayout(self)
        mainLayout.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)

        self.logo = QtWidgets.QLabel('')
        self.logo.setPixmap(utils.magicLeapLogoIcon().pixmap(296/2, 46/2))
        mainLayout.addWidget(self.logo)
        self.pressPos = None
        self.window = None

    # Support moving the window when clicking and dragging the widget.

    def setWindow(self, window):
        self.window = window

    def mousePressEvent(self, event):
        if not self.window:
            return

        self.pressPos = event.pos()
        self.isMoving = True

    def mouseMoveEvent(self, event):
        if self.window and self.isMoving:
            diff = event.pos() - self.pressPos
            self.window.move(self.window.pos()+diff)

    def mouseReleaseEvent(self, event):
        if not self.window:
            return
        self.isMoving = False

class CopyrightFooterWidget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(CopyrightFooterWidget, self).__init__(parent=parent)

        spacer = QtWidgets.QWidget()

        self.info = QtWidgets.QLabel(text='Copyright © 2018 - 2020 Magic Leap, Inc.')

        mainLayout = QtWidgets.QVBoxLayout(self)
        mainLayout.setAlignment(QtCore.Qt.AlignRight)
        mainLayout.setContentsMargins(0, 0, 0, 0)
        mainLayout.addWidget(self.info, 0)

class NewPortalIconDialog(QtWidgets.QDialog):
    def __init__(self, parent=None):
        super(NewPortalIconDialog, self).__init__(parent=parent)

        # Configure the QWidget properties.
        self.setObjectName('NewPortalIconDialog')
        self.setWindowTitle('New Portal Icon')
        self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint)

        mainLayout = QtWidgets.QVBoxLayout(self)
        mainLayout.setAlignment(QtCore.Qt.AlignTop)
        mainLayout.setContentsMargins(10, 10, 10, 10)
        mainLayout.setSpacing(5)

        self.logoWidget = LogoWidget()
        self.logoWidget.setWindow(self)
        mainLayout.addWidget(self.logoWidget)

        # Try adding a gif
        self.sampleIconGIF = QtWidgets.QLabel('')
        self.sampleIconGIFMovie = utils.samplePortalIconMovie()
        self.sampleIconGIFMovie.start()
        self.sampleIconGIF.setMovie(self.sampleIconGIFMovie)
        mainLayout.addWidget(self.sampleIconGIF)

        subNewIconLayout = QtWidgets.QHBoxLayout()
        subNewIconLayout.setContentsMargins(0, 0, 0, 0)
        mainLayout.addLayout(subNewIconLayout)

        subNewIconLayout.addStretch(1)
        subNewIconLayout.addWidget(self.sampleIconGIF, 0)

        subTextLayout = QtWidgets.QVBoxLayout()
        subTextLayout.setContentsMargins(0,0,0,0)
        subTextLayout.addStretch(1)

        self.title = QtWidgets.QLabel(text='New Portal Icon')
        self.title.setStyleSheet('font: 18pt;')
        subTextLayout.addWidget(self.title)

        self.info = QtWidgets.QLabel(text="What's a portal icon?")
        self.info.setTextFormat(QtCore.Qt.RichText);
        self.info.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction);
        self.info.setOpenExternalLinks(True)
        subTextLayout.addWidget(self.info)

        subTextLayout.addStretch(1)
        subNewIconLayout.addLayout(subTextLayout, 0)
        subNewIconLayout.addStretch(1)
        mainLayout.addLayout(subNewIconLayout)
        mainLayout.addSpacing(6)

        self.filePathField = FilePathWidget(title='Save Maya File:', displayText='', saveFile=True, getFile=False, getDirectory=False)
        mainLayout.addWidget(self.filePathField)

        self.okButton = QtWidgets.QPushButton('Create')
        self.okButton.setDefault(True);
        self.cancelButton = QtWidgets.QPushButton('Cancel')

        subWidget = QtWidgets.QWidget()
        mainLayout.addWidget(subWidget)
        subLayout = QtWidgets.QHBoxLayout(subWidget)
        subLayout.setContentsMargins(0, 0, 0, 0)

        subLayout.addWidget(self.okButton)
        subLayout.addWidget(self.cancelButton)

        mainLayout.addSpacing(12)

        copyrightWidget = CopyrightFooterWidget()
        mainLayout.addWidget(copyrightWidget)

        self.okButton.clicked.connect(self.onOK)
        self.cancelButton.clicked.connect(self.onCancel)

    def sizeHint(self):
        desktopWidget = QtWidgets.QApplication.desktop()
        geometry = desktopWidget.screenGeometry()
        return QtCore.QSize(geometry.width()/4, geometry.height()/8)

    def onOK(self):
        filePath = self.filePathField.filePath
        scene = utils.newScene(filePath)
        LOG.info(scene)

        LOG.info('Importing Icon template.')

        core.importTemplate()

        panel = PortalIconTool.run()
        panel.setupWidget.setFPS()
        panel.setupWidget.setUnits()

        self.accept()

    def onCancel(self):
        LOG.info('User cancelled creating a new Icon.')
        self.reject()

class OpenIconDialog(QtWidgets.QDialog):
    def __init__(self, parent=None):
        super(OpenIconDialog, self).__init__(parent=parent)

        # Configure the QWidget properties.
        self.setObjectName('OpenIconDialog')
        self.setWindowTitle('Open Icon')
        self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint)

        mainLayout = QtWidgets.QVBoxLayout(self)
        mainLayout.setAlignment(QtCore.Qt.AlignTop)
        mainLayout.setContentsMargins(5, 5, 5, 5)
        mainLayout.setSpacing(5)

        self.logoWidget = LogoWidget()
        mainLayout.addWidget(self.logoWidget)

        self.title = QtWidgets.QLabel(text='Open Icon')
        self.title.setStyleSheet('font: 24pt;')
        mainLayout.addWidget(self.title)

        self.info = QtWidgets.QLabel(text='Open an existing Icon created by this tool to edit.')
        mainLayout.addWidget(self.info)

        self.filePathField = FilePathWidget(title='Open Maya File:', displayText='', saveFile=False, getFile=True, getDirectory=False)
        mainLayout.addWidget(self.filePathField)

        self.okButton = QtWidgets.QPushButton('Open')
        self.cancelButton = QtWidgets.QPushButton('Cancel')

        subWidget = QtWidgets.QWidget()
        mainLayout.addWidget(subWidget)
        subLayout = QtWidgets.QHBoxLayout(subWidget)

        subLayout.addWidget(self.okButton)
        subLayout.addWidget(self.cancelButton)

        copyrightWidget = CopyrightFooterWidget()
        mainLayout.addWidget(copyrightWidget)

        self.okButton.clicked.connect(self.onOK)
        self.cancelButton.clicked.connect(self.onCancel)

    def sizeHint(self):
        desktopWidget = QtWidgets.QApplication.desktop()
        geometry = desktopWidget.screenGeometry()
        return QtCore.QSize(geometry.width()/4, geometry.height()/8)

    def onOK(self):
        filePath = self.filePathField.filePath

        sceneOpened = utils.openScene(filePath)

        if not sceneOpened:
            return

        PortalIconTool.run()

        self.accept()

    def onCancel(self):
        LOG.info('User cancelled opening an Icon.')
        self.reject()

class PortalIconTool(QtWidgets.QWidget):
    @staticmethod
    def run():
        mayaWindow = utils.mayaMainWindow()

        if not core.iconTemplateExists():
            QtWidgets.QMessageBox.critical(mayaWindow, '', 'Icon Error\nUse the Magic Leap menu to start a new Icon or open an existing one.')
            return

        widget = PortalIconTool(parent=mayaWindow)
        widget.move(
            mayaWindow.frameGeometry().center() - QtCore.QRect(QtCore.QPoint(),
            widget.sizeHint()).center()
        )
        widget.show()

        return widget

    def __init__(self, parent=None):
        super(PortalIconTool, self).__init__(parent=parent)

        # Configure the QWidget properties.
        self.setObjectName('PortalIconToolWidget')

        self.setWindowTitle('Portal Icon')
        self.setWindowFlags(self.windowFlags() | QtCore.Qt.Window)
        if utils.isMacOS():
            # MacOS is special, and the QtCore.Qt.Window flag does not sort the windows properly,
            # so instead QtCore.Qt.Tool is used.
            self.setWindowFlags(self.windowFlags() | QtCore.Qt.Tool)

        logoWidget = LogoWidget()
        self.setupWidget = IconSetupWidget(self)
        self.materialsWidget = IconMaterialsWidget(self)
        self.animationsWidget = AnimationWidget(self)
        self.finalizeWidget = IconFinalizeWidget(self)
        self.previewWidget = IconPreviewWidget(self)
        copyrightWidget = CopyrightFooterWidget()

        # Setup root QLayout object, all children widgets will be children of this QLayout.
        main_layout = QtWidgets.QVBoxLayout(self)
        main_layout.setAlignment(QtCore.Qt.AlignTop)
        main_layout.setContentsMargins(5, 5, 5, 5)
        main_layout.setSpacing(5)

        main_layout.addWidget(logoWidget)
        main_layout.addWidget(self.setupWidget)
        main_layout.addWidget(self.materialsWidget)
        main_layout.addWidget(self.animationsWidget)
        main_layout.addWidget(self.finalizeWidget)
        main_layout.addWidget(self.previewWidget)
        main_layout.addStretch(1)
        main_layout.addWidget(copyrightWidget)

    def sizeHint(self):
        return QtCore.QSize(350, 500)

class FilePathWidget(QtWidgets.QWidget):
    filePathChanged = QtCore.Signal(str)

    def __init__(self, title, displayText, getDirectory=True, getFile=False, saveFile=False, parent=None):
        super(FilePathWidget, self).__init__(parent=parent)

        self._getDirectory = getDirectory
        self._getFile = getFile
        self._saveFile = saveFile
        self._title = title

        main_layout = QtWidgets.QVBoxLayout(self)
        main_layout.setAlignment(QtCore.Qt.AlignTop)
        main_layout.setContentsMargins(0, 0, 0, 0)
        main_layout.setSpacing(1)

        main_layout.addWidget(QtWidgets.QLabel(title))

        sub_layout = QtWidgets.QHBoxLayout()
        sub_layout.setContentsMargins(0, 0, 0, 0)
        sub_layout.setSpacing(5)

        main_layout.addLayout(sub_layout)

        self.outputField = QtWidgets.QLineEdit()
        self.outputField.setMinimumHeight(24)
        self.outputField.setPlaceholderText(displayText)
        sub_layout.addWidget(self.outputField)

        self.openFileBrowserButton = ActionButton('...')
        self.openFileBrowserButton.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
        sub_layout.addWidget(self.openFileBrowserButton)

        self.openFileBrowserButton.clicked.connect(self.openOutputDialog)
        self.outputField.textChanged.connect(self.filePathChanged.emit)

    def openOutputDialog(self):
        if self._getDirectory:
            result = QtWidgets.QFileDialog.getExistingDirectory(self, self._title, utils.getProjectDirectory())
        elif self._getFile:
            result = QtWidgets.QFileDialog.getOpenFileName(self, self._title, utils.getProjectDirectory())[0]
        elif self._saveFile:
            result = QtWidgets.QFileDialog.getSaveFileName(self, self._title, utils.getProjectDirectory(), 'Maya Ascii (*.ma)')[0]

        if not result:
            return

        self.outputField.setText(result)
        self.filePathChanged.emit(self.outputField.text())

    @property
    def filePath(self):
        return self.outputField.text()

    @filePath.setter
    def filePath(self, text):
        self.outputField.setText(text)

class AnimationWidget(QtWidgets.QWidget):
    clipChanged = QtCore.Signal(object)
    statusChecked = QtCore.Signal()

    defaultStepData = dict(message='Set the animation clips', status=False, required=False)

    def __init__(self, parent=None):
        super(AnimationWidget, self).__init__(parent=parent)

        self._stepData = IconStepObject()
        self._stepData.statusChanged.connect(self.updateProgress)
        self._stepData['callback'] = self.check
        for k, v in self.defaultStepData.items():
            self._stepData[k] = v

        self.data = dict()

        main_layout = QtWidgets.QVBoxLayout(self)
        main_layout.setAlignment(QtCore.Qt.AlignTop)
        main_layout.setContentsMargins(0, 0, 0, 0)
        main_layout.setSpacing(1)

        contentLayout = QtWidgets.QVBoxLayout()

        # Add editable animation clips.
        clipOffset = 1
        animData = core.getAnimationData()

        for clipName, duration in constants.ICON_ANIMATION_CLIPS:
            newClipWidget = AnimationClipWidget(clipName, clipOffset, (clipOffset + duration - 1))
            newClipWidget.playClipToggled.connect(self.stopOtherClips)
            self.data[clipName] = newClipWidget
            contentLayout.addWidget(newClipWidget)

            if clipName in animData:
                newClipWidget.clipEnabled = True
                newClipWidget.startFrame = animData[clipName]['startFrame']
                newClipWidget.endFrame = animData[clipName]['endFrame']

            clipOffset += duration + 1

        self.frameWidget = CollapseWidget(title='Setup Animations (Optional)', stepData=self._stepData.serialized, parent=parent)
        self.frameWidget.setContentLayout(contentLayout)
        main_layout.addWidget(self.frameWidget)

        for clipWidget in self.data.values():
            clipWidget.clipChanged.connect(self.updateClipData)

    def showEvent(self, event):
        self._stepData.checkStepStatus.emit()
        super(AnimationWidget, self).showEvent(event)

    def enterEvent(self, event):
        self._stepData.checkStepStatus.emit()
        super(AnimationWidget, self).enterEvent(event)

    @property
    def stepData(self):
        return self._stepData

    def stopOtherClips(self):
        for clipWidget in self.data.values():
            if clipWidget._isPlaying:
                clipWidget.playClip()

    def updateClipData(self, payload):
        core.updateClipData(payload)

        self._stepData.checkStepStatus.emit()

    def updateProgress(self, checked):
        self.frameWidget.completed.emit(checked)
        self.statusChecked.emit()

    def check(self):
        checked = False

        for clipWidget in self.data.values():
            if not clipWidget.clipEnabled:
                continue

            checked = True

            currentRange = clipWidget.endFrame - clipWidget.startFrame
            if currentRange > clipWidget.maxClipSize:
                LOG.exception('%s take requires (%s), current (%s)', clipWidget.clipName, clipWidget.maxClipSize, currentRange)
                checked = False

            clipChecked = core.checkTakeForAnimation(clipWidget.startFrame, clipWidget.endFrame)
            if not clipChecked:
                LOG.exception('%s take missing keyframe at start or end.', clipWidget.clipName)
                checked = False

        return checked

class AnimationClipWidget(QtWidgets.QWidget):
    setRangeIcon = utils.setRangeIcon()
    playIcon = utils.playIcon()
    pauseIcon = utils.pauseIcon()

    minClipFrame = 0
    maxClipFrame = 10000

    clipChanged = QtCore.Signal(object)
    playClipToggled = QtCore.Signal()

    def __init__(self, clipName, startFrame, endFrame, parent=None):
        super(AnimationClipWidget, self).__init__(parent=parent)

        self._isPlaying = False
        self._maxClipSize = endFrame - startFrame + 1

        main_layout = QtWidgets.QHBoxLayout(self)
        main_layout.setAlignment(QtCore.Qt.AlignLeft)
        main_layout.setContentsMargins(0, 0, 0, 0)
        main_layout.setSpacing(2)

        self.enableClip = QtWidgets.QCheckBox('')
        self.enableClip.setToolTip('Toggle clip export.')
        main_layout.addWidget(self.enableClip)

        self.title = QtWidgets.QLineEdit('')
        self.title.setToolTip('Clip name (Not Editable). Max number of frames - {0}.'.format(self.maxClipSize))
        self.title.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred)
        self.title.setReadOnly(True)
        main_layout.addWidget(self.title)

        self.startField = QtWidgets.QSpinBox()
        self.startField.setToolTip('First frame of clip.')
        self.startField.setMinimum(self.minClipFrame)
        self.startField.setMaximum(self.maxClipFrame)
        self.startField.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)
        self.startField.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
        main_layout.addWidget(self.startField)

        self.endField = QtWidgets.QSpinBox()
        self.endField.setToolTip('Last frame of clip.')
        self.endField.setMinimum(self.minClipFrame)
        self.endField.setMaximum(self.maxClipFrame)
        self.endField.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)
        self.endField.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
        main_layout.addWidget(self.endField)

        self.playButton = QtWidgets.QPushButton(self.playIcon, '')
        self.playButton.setIconSize(QtCore.QSize(10, 10))
        self.playButton.setToolTip('Play the clip.')
        main_layout.addWidget(self.playButton)

        self.setRangeButton = QtWidgets.QPushButton()
        self.setRangeButton.setIcon(self.setRangeIcon)
        self.setRangeButton.setIconSize(QtCore.QSize(10, 10))
        self.setRangeButton.setToolTip('Set timeline to clip start/end frames.')
        main_layout.addWidget(self.setRangeButton)

        # Set default values.
        self.title.setText(clipName)
        self.startField.setValue(startFrame)
        self.endField.setValue(endFrame)

        # Hook up signals.
        self.enableClip.stateChanged.connect(self.updateClipData)
        self.startField.valueChanged.connect(self.updateClipData)
        self.endField.valueChanged.connect(self.updateClipData)
        self.setRangeButton.clicked.connect(self.setActiveClip)
        self.playButton.clicked.connect(self.playClip)

    def paintEvent(self, event):
        if self.clipSize > self.maxClipSize:
            widgetPalette = self.title.palette()
            widgetPalette.setColor(QtGui.QPalette.Base, QtCore.Qt.red)
            self.title.setPalette(widgetPalette)
        else:
            widgetPalette = self.title.palette()
            widgetPalette.setColor(QtGui.QPalette.Base, QtCore.Qt.transparent)
            self.title.setPalette(widgetPalette)

        super(AnimationClipWidget, self).paintEvent(event)

    @property
    def clipName(self):
        return self.title.text()

    @property
    def maxClipSize(self):
        return self._maxClipSize

    @property
    def clipSize(self):
        return self.endFrame - self.startFrame + 1

    @property
    def clipEnabled(self):
        return self.enableClip.isChecked()

    @clipEnabled.setter
    def clipEnabled(self, checked):
        self.enableClip.setChecked(checked)

    @property
    def startFrame(self):
        return self.startField.value()

    @startFrame.setter
    def startFrame(self, value):
        self.startField.setValue(value)

    @property
    def endFrame(self):
        return self.endField.value()

    @endFrame.setter
    def endFrame(self, value):
        self.endField.setValue(value)

    @property
    def payload(self):
        return {
            'name': self.clipName,
            'enabled': self.clipEnabled,
            'startFrame': self.startFrame,
            'endFrame': self.endFrame
        }

    def updateClipData(self, value):
        self.clipChanged.emit(self.payload)

    def playClip(self):
        if not self._isPlaying:
            self.playClipToggled.emit()

        self._isPlaying = not(self._isPlaying)

        utils.playAnimation(self.startFrame, self.endFrame, self._isPlaying)

        if self._isPlaying:
            self.playButton.setIcon(self.pauseIcon)
        else:
            self.playButton.setIcon(self.playIcon)

    def setActiveClip(self):
        utils.setAnimationTimeline(self.startFrame, self.endFrame)

class IconStepObject(QtCore.QObject):
    checkStepStatus = QtCore.Signal()
    statusChanged = QtCore.Signal(bool)

    defaultStepData = dict(message='Templated message', status=False, required=False, callback=None)

    def __init__(self, parent=None):
        super(IconStepObject, self).__init__(parent=parent)

        self._stepData = self.defaultStepData.copy()
        self.checkStepStatus.connect(self.check)

    def __getitem__(self, key):
        return self._stepData[key]

    def __setitem__(self, key, value):
        self._stepData[key] = value

    @property
    def stepData(self):
        return self._stepData

    @property
    def serialized(self):
        return self._stepData

    def check(self):
        if self._stepData['callback'] is not None:
            checked = self._stepData['callback']()
            self._stepData['status'] = checked
            self.statusChanged.emit(checked)

class FieldWidget(QtWidgets.QWidget):
    text = 0
    dropdown = 1
    number = 2

    showError = QtCore.Signal()
    hideError = QtCore.Signal()
    toggleError = QtCore.Signal(bool)
    toolClicked = QtCore.Signal()

    def __init__(self, title, fieldType, required=False, parent=None):
        super(FieldWidget, self).__init__(parent=parent)

        self._title = title
        self._required = required
        self._fieldType = fieldType

        mainLayout = QtWidgets.QVBoxLayout(self)
        mainLayout.setAlignment(QtCore.Qt.AlignTop)
        mainLayout.setContentsMargins(0, 0, 0, 0)
        mainLayout.setSpacing(0)

        labelText = title
        if self._required:
            labelText += ' (required)'
        self.fieldLabel = QtWidgets.QLabel(labelText)
        mainLayout.addWidget(self.fieldLabel)

        subWidget = QtWidgets.QWidget()
        mainLayout.addWidget(subWidget)
        subLayout = QtWidgets.QHBoxLayout(subWidget)
        subLayout.setAlignment(QtCore.Qt.AlignLeft)
        subLayout.setContentsMargins(0, 0, 0, 0)
        subLayout.setSpacing(5)

        self.field = QtWidgets.QLineEdit()
        subLayout.addWidget(self.field, 1)

        self.toolButton = QtWidgets.QPushButton('')
        self.toolButton.setStyleSheet('QPushButton { padding: 0;margin: 0 }')
        self.toolButton.setIcon(utils.wrenchIcon())
        subLayout.addWidget(self.toolButton, 0)

        self.fieldErrorMessage = QtWidgets.QLabel()
        mainLayout.addWidget(self.fieldErrorMessage)
        # self.fieldErrorMessage.hide()

        self.toolButton.clicked.connect(self.toolClicked.emit)
        self.toggleError.connect(self.onToggleError)
        self.showError.connect(self.onShowError)
        self.hideError.connect(self.onHideError)

        self._setErrorMessageColor()

    def _setErrorMessageColor(self):
        palette = self.fieldErrorMessage.palette()
        palette.setColor(self.fieldErrorMessage.foregroundRole(), QtGui.QColor(241, 39, 66))
        self.fieldErrorMessage.setPalette(palette)

    def onToggleError(self, errored):
        if errored:
            self.showError.emit()
        else:
            self.hideError.emit()

    def onShowError(self):
        self.fieldErrorMessage.show()

    def onHideError(self):
        self.fieldErrorMessage.hide()

    def setErrorMessage(self, text):
        self.fieldErrorMessage.setText(text)

    def setDisplayOnly(self, value):
        self.field.setReadOnly(value)

class IconSetupWidget(QtWidgets.QWidget):
    statusChecked = QtCore.Signal()

    defaultStepData = dict(message='Using the Icon Template', status=False, required=True)

    def __init__(self, parent=None):
        super(IconSetupWidget, self).__init__(parent=parent)

        self._stepData = IconStepObject()
        self._stepData.statusChanged.connect(self.updateProgress)
        self._stepData['callback'] = self.check
        for k, v in self.defaultStepData.items():
            self._stepData[k] = v

        main_layout = QtWidgets.QHBoxLayout(self)
        main_layout.setAlignment(QtCore.Qt.AlignTop)
        main_layout.setContentsMargins(1, 1, 1, 1)
        main_layout.setSpacing(0)

        self.unitsField = FieldWidget('Units', FieldWidget.text, required=True)
        self.unitsField.setDisplayOnly(True)
        self.unitsField.setErrorMessage('Scene units not set to Centimeters(CM)')

        self.fpsField = FieldWidget('Time', FieldWidget.text, required=True)
        self.fpsField.setDisplayOnly(True)
        self.fpsField.setErrorMessage('Scene FPS not set to 60')

        contentLayout = QtWidgets.QVBoxLayout()
        contentLayout.addWidget(self.unitsField)
        contentLayout.addWidget(self.fpsField)

        self.frameWidget = CollapseWidget(title='Configure Scene', stepData=self._stepData.serialized, parent=parent)
        self.frameWidget.setContentLayout(contentLayout)
        main_layout.addWidget(self.frameWidget)

        self.unitsField.toolClicked.connect(self.setUnits)
        self.fpsField.toolClicked.connect(self.setFPS)

    def showEvent(self, event):
        self.unitsField.field.setText(core.getUnitsValue())
        self.fpsField.field.setText(core.getFPSValue())

        self._stepData.checkStepStatus.emit()

        super(IconSetupWidget, self).showEvent(event)

    def enterEvent(self, event):
        sceneChecks = core.checkSceneSettings()

        self.unitsField.toggleError.emit(not sceneChecks['units'])

        self.fpsField.toggleError.emit(not sceneChecks['fps'])

        super(IconSetupWidget, self).enterEvent(event)

    def setUnits(self):
        core.setSceneSettings(units=True, fps=False)

        self.unitsField.field.setText(core.getUnitsValue())

        sceneChecks = core.checkSceneSettings()
        self.unitsField.toggleError.emit(not sceneChecks['units'])

        self._stepData.checkStepStatus.emit()

    def setFPS(self):
        core.setSceneSettings(units=False, fps=True)

        self.fpsField.field.setText(core.getFPSValue())

        sceneChecks = core.checkSceneSettings()
        self.fpsField.toggleError.emit(not sceneChecks['fps'])

        self._stepData.checkStepStatus.emit()

    @property
    def stepData(self):
        return self._stepData

    def updateProgress(self, checked):
        self.frameWidget.completed.emit(checked)
        self.statusChecked.emit()

    def check(self):
        sceneChecks = core.checkSceneSettings()

        return all([x for x in sceneChecks.values()])

class IconMaterialsWidget(QtWidgets.QWidget):
    statusChecked = QtCore.Signal()

    defaultStepData = dict(message='Use up to 5 materials to use across your Icon.', status=False, required=True)

    maxMaterialsForIcon = 5
    portalSkySphereMaterialCount = 1

    def __init__(self, parent=None):
        super(IconMaterialsWidget, self).__init__(parent=parent)

        self._stepData = IconStepObject()
        self._stepData.statusChanged.connect(self.updateProgress)
        self._stepData['callback'] = self.check
        for k, v in self.defaultStepData.items():
            self._stepData[k] = v

        self._materials = []
        self._unsupportedMaterials = []
        self._data = []

        self.errorMessage = QtWidgets.QLabel('')
        self.errorMessage.hide()

        self.refreshButton = ActionButton('Refresh Material List')

        contentLayout = QtWidgets.QVBoxLayout(self)

        contentLayout.addWidget(self.refreshButton)
        contentLayout.addWidget(self.errorMessage)

        for index in range(self.maxMaterialsForIcon + self.portalSkySphereMaterialCount):
            widget = MaterialSelectorWidget()
            widget.materialUpdated.connect(self._stepData.checkStepStatus.emit)
            contentLayout.addWidget(widget)
            self._data.append(widget)

        self.frameWidget = CollapseWidget(title='Assign Materials', stepData=self._stepData.serialized, parent=parent)
        self.frameWidget.setContentLayout(contentLayout)
        self.frameWidget.expand()

        main_layout = QtWidgets.QVBoxLayout(self)
        main_layout.setAlignment(QtCore.Qt.AlignTop)
        main_layout.setContentsMargins(1, 1, 1, 1)
        main_layout.setSpacing(0)

        main_layout.addWidget(self.frameWidget)

        self.loadMaterials()
        self.populateMaterials()

        self.refreshButton.clicked.connect(self.refresh)

    def showEvent(self, event):
        self._stepData.checkStepStatus.emit()
        super(IconMaterialsWidget, self).showEvent(event)

    @property
    def stepData(self):
        return self._stepData

    @property
    def materialCount(self):
        return len(self._materials)

    @property
    def materialCountMax(self):
        return self.maxMaterialsForIcon + self.portalSkySphereMaterialCount

    def updateProgress(self, checked):
        self.frameWidget.completed.emit(checked)
        self.statusChecked.emit()

    def check(self):
        checked = True

        if self.materialCount >= self.materialCountMax or self.materialCount == 0:
            checked = False

        textures = core.getTexturesFromMaterials(self._materials)
        if len(textures) != len(self._materials):
            checked = False

        for texture in textures:
            if not core.getFilePathFromTexture(texture):
                checked = False
                break

        if self._unsupportedMaterials:
            self._setErrorMessage()
            checked = False
        else:
            self.errorMessage.hide()

        return checked

    def refresh(self):
        self.loadMaterials()

        self.populateMaterials()

        self._stepData.checkStepStatus.emit()

    def _setErrorMessage(self):
        self.errorMessage.show()
        err = 'Found unsupported material types on some meshes:'
        for mat in self._unsupportedMaterials:
            err += '\n -{0}'.format(mat)
        err += '\n Lambert, Phong, and Blinn materials are only supported.'
        self.errorMessage.setText(err)
        palette = self.errorMessage.palette()
        palette.setColor(self.errorMessage.foregroundRole(), QtGui.QColor(241, 39, 66))
        self.errorMessage.setPalette(palette)
        self.errorMessage.setMargin(5)

    def loadMaterials(self):
        modelNode = core.getModelComponentNode()
        portalNode = core.getPortalComponentNode()
        componentNodes = [modelNode, portalNode]

        if modelNode is None or portalNode is None:
            return

        materials = []
        unsupportedMaterials = []
        for node in componentNodes:
            meshes = core.getMeshesRecursive(node=node)
            mats, unsupported = core.getMaterialsForMeshes(meshes)
            materials.extend(mats)
            unsupportedMaterials.extend(unsupported)

        self._materials = list(set(materials))
        self._unsupportedMaterials = list(set(unsupportedMaterials))

        LOG.info('unsupported mats on load: {0}'.format(self._unsupportedMaterials))

    def populateMaterials(self):
        self.clear()

        LOG.info('Loading materials %s', self._materials)

        for n, mat in enumerate(self._materials):
            self._data[n].loadMaterial(mat)

    def clear(self):
        for index in reversed(range(self.maxMaterialsForIcon + self.portalSkySphereMaterialCount)):
            widget = self._data[index]
            widget.materialOption.setText('Unused Material')
            widget.openMaterialButton.clearMaterial()
            widget.setEnabled(False)

class MaterialSelectorWidget(QtWidgets.QWidget):
    materialUpdated = QtCore.Signal()

    def __init__(self, materialName='', parent=None):
        super(MaterialSelectorWidget, self).__init__(parent=parent)

        self._materialNodePath = None
        self.setEnabled(True)

        self.materialOption = QtWidgets.QPushButton(materialName)
        self.materialOption.setFlat(True)

        self.openMaterialButton = MaterialButton()

        main_layout = QtWidgets.QHBoxLayout(self)
        main_layout.setAlignment(QtCore.Qt.AlignRight)
        main_layout.setContentsMargins(1, 1, 1, 1)
        main_layout.setSpacing(5)

        main_layout.addWidget(self.materialOption, 1)
        main_layout.addWidget(self.openMaterialButton, 0)

        self.materialOption.clicked.connect(self.selectMaterial)
        self.openMaterialButton.materialChanged.connect(self.setMaterial)

    def setMaterial(self, textureFilePath):
        core.setColorTexture(self._materialNodePath, textureFilePath)
        self.materialUpdated.emit()
        self.materialOption.setToolTip('Material: {0}\n Texture: {1}'.format(self._materialNodePath, textureFilePath))

    def selectMaterial(self):
        core.selectNodes(self._materialNodePath)
        utils.openAttributeEditor()

    def loadMaterial(self, nodePath):
        self.setEnabled(True)
        self._materialNodePath = nodePath
        # This length value is hardcoded so it fits the default size of the widget.
        if len(nodePath) > 30:
            self.materialOption.setText(nodePath[:30] + '...')
        else:
            self.materialOption.setText(nodePath)

        texture = core.getTexture(self._materialNodePath)
        if texture:
            textureFilePath = core.getFilePathFromTexture(texture)
            if textureFilePath:
                self.openMaterialButton.setMaterial(textureFilePath)

class MaterialButton(QtWidgets.QPushButton):
    materialChanged = QtCore.Signal(str)

    def __init__(self, parent=None):
        super(MaterialButton, self).__init__(text='', parent=parent)

        self.setToolTip('Click to set the material color texture.')
        self.clearMaterial()

        self.clicked.connect(self.addNewTexture)

    def sizeHint(self):
        return QtCore.QSize(35, 35)

    def setMaterial(self, filePath):
        icon = QtGui.QIcon(filePath)
        self.setIcon(icon)
        self.setIconSize(self.sizeHint())

        self.materialChanged.emit(filePath)

    def clearMaterial(self):
        icon = utils.materialEmptyIcon()
        self.setIcon(icon)
        self.setIconSize(self.sizeHint())

    def addNewTexture(self):
        result = QtWidgets.QFileDialog.getOpenFileName(
            self,
            'Add Texture',
            utils.getSceneDirectory(),
            'PNG (*.png)'
        )

        if result[0]:
            self.setMaterial(result[0])

class IconPreviewWidget(QtWidgets.QWidget):
    statusChecked = QtCore.Signal()

    defaultStepData = dict(message='Preview on the ML1', status=False, required=False)

    def __init__(self, parent=None):
        super(IconPreviewWidget, self).__init__(parent=parent)

        self._previewed = False

        self._stepData = IconStepObject()
        self._stepData.statusChanged.connect(self.updateProgress)
        self._stepData['callback'] = self.check
        for k, v in self.defaultStepData.items():
            self._stepData[k] = v

        main_layout = QtWidgets.QHBoxLayout(self)
        main_layout.setAlignment(QtCore.Qt.AlignTop)
        main_layout.setContentsMargins(1, 1, 1, 1)
        main_layout.setSpacing(0)

        self.sdkPathWidget = FilePathWidget(title='MLSDK Path', displayText='/path/to/mlsdk')
        self.sdkPathWidget.setToolTip('Where your MLSDK version folder resides.')

        self.previewButton = QtWidgets.QPushButton('Upload to ML Device')
        self.previewButton.setToolTip('Load the Icon you created onto your Magic Leap device.')

        contentLayout = QtWidgets.QVBoxLayout()
        contentLayout.addWidget(self.sdkPathWidget)
        contentLayout.addWidget(self.previewButton)

        self.frameWidget = CollapseWidget(title='Preview On Device', stepData=self._stepData.serialized, parent=parent)
        self.frameWidget.setContentLayout(contentLayout)
        main_layout.addWidget(self.frameWidget)

        self.sdkPathWidget.filePathChanged.connect(self.saveSDKPath)
        self.previewButton.clicked.connect(self.showPreview)

    def showEvent(self, event):
        self.loadData()
        self._stepData.checkStepStatus.emit()
        super(IconPreviewWidget, self).showEvent(event)

    @property
    def stepData(self):
        return self._stepData

    def showPreview(self):
        LOG.info('Start preview Icon on device.')

        settingsData = core.getIconSettings()

        sdkPath = settingsData.get('sdkPath', None)
        if sdkPath is None:
            QtWidgets.QMessageBox.critical(self, '', 'Preview Error\nSet the `MLSDK Path` field before previewing.')
            return

        outputPath = settingsData.get('outputPath', None)
        if outputPath is None:
            QtWidgets.QMessageBox.critical(self, '', 'Preview Error\nSet the `Output Folder` field before previewing.')
            return

        core.buildIcon(validate=True, export=True, cleanup=False)

        iconZipPath = core.createIconZip()

        result = core.openIconPreviewer(iconZipPath, settingsData['sdkPath'])

        if not result:
            self._previewed = True
        else:
            self._previewed = False
            QtWidgets.QMessageBox.critical(self, 'Preview Error', result)

        self._stepData.checkStepStatus.emit()

    def saveSDKPath(self, text):
        core.saveSDKPath(text)
        self._stepData.checkStepStatus.emit()
        self.statusChecked.emit()

    def loadData(self):
        settingsData = core.getIconSettings()
        if 'sdkPath' in settingsData:
            self.sdkPathWidget.filePath = settingsData['sdkPath']

    def updateProgress(self, checked):
        self.frameWidget.completed.emit(checked)
        self.statusChecked.emit()

    def check(self):
        hasMLSDKPath = False

        if self.sdkPathWidget.filePath:
            hasMLSDKPath = True

        return all([x for x in [hasMLSDKPath, self._previewed]])

class IconFinalizeWidget(QtWidgets.QWidget):
    statusChecked = QtCore.Signal()

    defaultStepData = dict(message='Validate the Icon', status=False, required=True)

    def __init__(self, parent=None):
        super(IconFinalizeWidget, self).__init__(parent=parent)

        self._validated = False

        self._stepData = IconStepObject()
        self._stepData.statusChanged.connect(self.updateProgress)
        self._stepData['callback'] = self.check
        for k, v in self.defaultStepData.items():
            self._stepData[k] = v

        main_layout = QtWidgets.QHBoxLayout(self)
        main_layout.setAlignment(QtCore.Qt.AlignTop)
        main_layout.setContentsMargins(1, 1, 1, 1)
        main_layout.setSpacing(0)

        self.outputWidget = FilePathWidget(title='Output Folder', displayText='/path/to/icon')
        self.outputWidget.setToolTip('Which directory the Icon will be exported to.')

        self.validateButton = ActionButton('Validate')
        self.validateButton.setToolTip('Check that the Icon you made is publish ready.')

        self.exportButton = ActionButton('Export')
        self.exportButton.setToolTip('Export the Icon to the set Output Folder.')

        contentLayout = QtWidgets.QVBoxLayout()
        contentLayout.addWidget(self.outputWidget)
        contentLayout.addWidget(self.validateButton)
        contentLayout.addWidget(self.exportButton)

        self.frameWidget = CollapseWidget(title='Validate And Export', stepData=self._stepData.serialized, parent=parent)
        self.frameWidget.setContentLayout(contentLayout)
        self.frameWidget.expand()
        main_layout.addWidget(self.frameWidget)

        self.outputWidget.filePathChanged.connect(self.saveOutputPath)
        self.validateButton.clicked.connect(self.validate)
        self.exportButton.clicked.connect(self.export)

    def showEvent(self, event):
        self.loadData()
        self._stepData.checkStepStatus.emit()
        super(IconFinalizeWidget, self).showEvent(event)

    @property
    def stepData(self):
        return self._stepData

    def saveOutputPath(self, text):
        core.saveOutputPath(text)
        self._stepData.checkStepStatus.emit()
        self.statusChecked.emit()

    def loadData(self):
        settingsData = core.getIconSettings()
        if 'outputPath' in settingsData:
            self.outputWidget.filePath = settingsData['outputPath']

    def updateProgress(self, checked):
        self.frameWidget.completed.emit(checked)
        self.statusChecked.emit()

    def check(self):
        hasOutputPath = False
        rootPathExists = False

        if self.outputWidget.filePath:
            hasOutputPath = True

        if os.path.exists(os.path.dirname(self.outputWidget.filePath)):
            rootPathExists = True

        return all([x for x in [hasOutputPath, rootPathExists, self._validated]])

    def validate(self):
        LOG.info('Start Icon validation.')

        result = core.buildIcon(validate=True, cleanup=True)
        self._validated = result

        self._stepData.checkStepStatus.emit()

        settingsData = core.getIconSettings()

        if not result:
            QtWidgets.QMessageBox.critical(self, '', 'Icon Validation Failed\nCheck the script editor for details.')
            LOG.info('Icon failed validation - {0}'.format(settingsData['outputPath']))
            return

        LOG.info('Icon passed validation - {0}'.format(settingsData['outputPath']))
        QtWidgets.QMessageBox.information(self, '', 'Icon Validation Passed\nThe Icon is ready for submission!')

    def export(self):
        if not utils.isSceneSaved():
            QtWidgets.QMessageBox.critical(self, '', 'File Not Saved\nSave your file first, and click Export again.')
            return

        LOG.info('Start Icon export.')
        result = core.buildIcon(validate=True, export=True, cleanup=False)
        self._validated = result

        self._stepData.checkStepStatus.emit()

        if not result:
            settingsData = core.getIconSettings()
            LOG.info('Icon failed to export - {0}'.format(settingsData['outputPath']))
            QtWidgets.QMessageBox.critical(self, '', 'Export Failed\nSomething went wrong!')
            return

        zipFile = core.createIconZip()
        LOG.info('Icon export Completed: %s', zipFile)
        QtWidgets.QMessageBox.information(self, '', 'Icon Export Completed\n{0}'.format(zipFile))

class CollapseWidget(QtWidgets.QWidget):
    completed = QtCore.Signal(bool)

    def __init__(self, title, stepData, parent=None):
        super(CollapseWidget, self).__init__(parent=parent)

        self._stepData = stepData
        self.stepStatusWidget = IconStatusStepWidget(stepData)

        self.toggleButton = QtWidgets.QToolButton(
            text=title, checkable=True, checked=False
        )
        self.toggleButton.setStyleSheet("QToolButton { border: none; }")
        self.toggleButton.setToolButtonStyle(
            QtCore.Qt.ToolButtonTextBesideIcon
        )
        self.toggleButton.setArrowType(QtCore.Qt.RightArrow)
        self.toggleButton.setChecked(False)
        self.toggleButton.released.connect(self.onReleased)

        self.contentArea = QtWidgets.QScrollArea(
            maximumHeight=0, minimumHeight=0
        )
        self.contentArea.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
        self.contentArea.setWidgetResizable(True)
        self.contentArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.contentArea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.contentArea.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
        self.contentArea.setFrameShape(QtWidgets.QFrame.Box)

        headerLayout = QtWidgets.QHBoxLayout()
        headerLayout.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
        headerLayout.setSpacing(0)
        headerLayout.setContentsMargins(0, 0, 0, 0)
        headerLayout.addWidget(self.stepStatusWidget)
        headerLayout.addWidget(self.toggleButton)

        lay = QtWidgets.QVBoxLayout(self)
        lay.setSpacing(0)
        lay.setContentsMargins(0, 0, 0, 0)
        lay.addLayout(headerLayout)
        lay.addWidget(self.contentArea)

        self.completed.connect(self.stepStatusWidget.setStatus)

    def expand(self):
        self.toggleButton.setChecked(True)
        self.onReleased()

    def collapse(self):
        self.toggleButton.setChecked(False)
        self.onReleased()

    def onReleased(self):
        checked = self.toggleButton.isChecked()

        if not checked:
            self.toggleButton.setArrowType(QtCore.Qt.RightArrow)
            self.contentArea.setMaximumHeight(self.collapsedHeight)
            self.setMaximumHeight(self.collapsedHeight)
            self.contentArea.setFrameShape(QtWidgets.QFrame.NoFrame)
            self.contentArea.hide()
        else:
            self.toggleButton.setArrowType(QtCore.Qt.DownArrow)
            self.contentArea.setMaximumHeight(self.contentHeight + self.collapsedHeight)
            self.setMaximumHeight(self.contentHeight + self.collapsedHeight)
            self.contentArea.setFrameShape(QtWidgets.QFrame.Box)
            self.contentArea.show()

    @property
    def collapsedHeight(self):
        return self._collapsedHeight

    @collapsedHeight.setter
    def collapsedHeight(self, value):
        self._collapsedHeight = value

    @property
    def contentHeight(self):
        return self._contentHeight

    @contentHeight.setter
    def contentHeight(self, value):
        self._contentHeight = value

    def setContentLayout(self, layout):
        oldWidget = self.contentArea.widget()
        if  oldWidget is not None:
            oldWidget.deleteLater()

        widget = QtWidgets.QWidget()
        widget.setLayout(layout)

        self.contentArea.setWidget(widget)

        self.collapsedHeight = self.sizeHint().height() - self.contentArea.maximumHeight()
        self.contentHeight = layout.sizeHint().height()

class IconStatusStepWidget(QtWidgets.QWidget):
    def __init__(self, stepData, parent=None):
        super(IconStatusStepWidget, self).__init__(parent=parent)

        self.setToolTip(stepData.get('message', ''))

        self._required = stepData.get('required', False)

        layout = QtWidgets.QHBoxLayout(self)
        layout.setAlignment(QtCore.Qt.AlignLeft)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(5)

        self.icon = QtWidgets.QLabel('')
        self.setStatus(stepData.get('status', False))
        layout.addWidget(self.icon)

    def setStatus(self, status):
        if self._required:
            if status:
                self.setStatusComplete()
            else:
                self.setStatusIncomplete()
        else:
            if status:
                self.setStatusComplete()
            else:
                self.setStatusOk()

    def setStatusComplete(self):
        iconPath = utils.mergePaths(os.path.dirname(__file__), 'resources', 'statusCompleted_icon.png')
        pixmap = QtGui.QPixmap(iconPath)
        pixmap = pixmap.scaled(16, 16, QtCore.Qt.KeepAspectRatio)
        self.icon.setPixmap(pixmap)

    def setStatusIncomplete(self):
        iconPath = utils.mergePaths(os.path.dirname(__file__), 'resources', 'statusIncompleted_icon.png')
        pixmap = QtGui.QPixmap(iconPath)
        pixmap = pixmap.scaled(16, 16, QtCore.Qt.KeepAspectRatio)
        self.icon.setPixmap(pixmap)

    def setStatusOk(self):
        iconPath = utils.mergePaths(os.path.dirname(__file__), 'resources', 'statusOk_icon.png')
        pixmap = QtGui.QPixmap(iconPath)
        pixmap = pixmap.scaled(16, 16, QtCore.Qt.KeepAspectRatio)
        self.icon.setPixmap(pixmap)

class ActionButton(QtWidgets.QPushButton):
    def __init__(self, title, parent=None):
        super(ActionButton, self).__init__(title, parent=parent)

        self.setMinimumHeight(24)
        self.setMaximumHeight(24)
        self.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)

    def sizeHint(self):
        return QtCore.QSize(24, 24)


# USAGE
# python recognize_faces_video.py --encodings encodings.pickle
# python recognize_faces_video.py --encodings encodings.pickle --output output/jurassic_park_trailer_output.avi --display 0

# import the necessary packages
import os
from datetime import date

from tensorflow.keras.applications.mobilenet_v2 import preprocess_input
from tensorflow.keras.preprocessing.image import img_to_array
from tensorflow.keras.models import load_model
from imutils.video import VideoStream
import numpy as np
import argparse
import imutils
import time
import cv2
from face_detection import FaceDetection

import os
from imutils.video import VideoStream
import face_recognition
import argparse
import imutils
import pickle
import time
import cv2
import time
import pandas as pd
from collections import Counter
import numpy as np
import time
# from detect_mask_video import detect_and_predict_mask

# writer = cv2.VideoWriter(args["output"], fourcc, 20,
#     (frame.shape[1], frame.shape[0]), True)

Fcount = 500
 #enter the number of frames you wanna save
vs = cv2.VideoCapture(0)
global gMask
global gBox
ret,frame = vs.read()
# video = cv2.VideoCapture("/dev/video1") # check this and uncommetn if you are using a jetson nano

# We need to check if camera
# is opened previously or not
if (vs.isOpened() == False):
    print("Error reading video file")

# We need to set resolutions.
# so, convert them from float to integer.
frame_width = int(vs.get(3))
frame_height = int(vs.get(4))

size = (frame_width, frame_height)

# Below VideoWriter object will create
# a frame of above defined The output
# is stored in 'filename.avi' file.
result = cv2.VideoWriter('videos/filename.avi',
                         cv2.VideoWriter_fourcc(*'MJPG'),
                         10, size)

details=pd.read_excel('details.xlsx',index_col=0)
atte=pd.read_excel('attendance.xlsx',index_col=0)

def spo2Func():
    final_sig = []

    frameCount = 1

    def dethreding(z):

        return signal.detrend(z)

        # T=len(z)
        # lamba=10
        # I  = np.identity(T)
        # filt = [1,-2,1]* np.ones((1,T-2),dtype=np.int).T
        # D2 = scipy.sparse.spdiags(data.T, (range(0,3)),T-2,T)
        # detrended_sig =
        # return z_stat

    def face_detect_and_thresh(frame):
        # lower = np.array([0, 1,0], dtype = "uint8")
        # upper = np.array([179, 255, 255], dtype = "uint8")
        lower = np.array([0, 58, 50] , dtype="uint8")
        upper = np.array([30, 255, 255], dtype="uint8")
        # for detecting skin lower and upper are used for creating a mask
        fd = FaceDetection()
        frame, face_frame, ROI1, ROI2, status, mask, extra, box = fd.face_detect(frame)
        frame = extra
        # cv2.imshow("face0",extra)
        converted = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
        skinMask = cv2.inRange(converted, lower, upper)
        cv2.imshow("face0", converted)
        cv2.imshow("face3", skinMask)
        # apply a series of erosions and dilations to the mask
        # using an elliptical kernel
        kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11, 11))
        skinMask = cv2.erode(skinMask, kernel, iterations=2)
        skinMask = cv2.dilate(skinMask, kernel, iterations=2)
        # blur the mask to help remove noise, then apply the
        # mask to the frame
        skinMask = cv2.GaussianBlur(skinMask, (3, 3), 0)
        skin = cv2.bitwise_and(frame, frame, mask=skinMask)
        cv2.imshow("skin2", skin)
        gMask = skinMask
        global gBox
        gBox = box
        # show the skin in the image along with the mask
        # cv2.imshow("images", np.hstack([frame, skin]))
        # cv2.imshow("mask",np.hstack([mask]))

        for _ in range(np.shape(mask)[0]):
            for j in range(np.shape(mask)[1]):
                if mask[_][j] == 1:
                    mask[_][j] = 1
                else:
                    mask[_][j] = 0
        mask = mask.astype(int)
        return skin, frame, mask

    def spartialAverage(thresh, frame):
        a = list(np.argwhere(maskT == 1))
        # x=[i[0] for i in a]
        # y=[i[1] for i in a]
        # p=[x,y]
        ind_img = (np.vstack((a)))
        sig_fin = np.zeros([np.shape(ind_img)[0], 3])
        test_fin = []
        for i in range(np.shape(ind_img)[0]):
            sig_temp = frame[ind_img[i, 0], ind_img[i, 1], :]
            sig_temp = sig_temp.reshape((1, 3))
            if sig_temp.any() != 0:
                sig_fin = np.concatenate((sig_fin, sig_temp))
        # print(sig_fin)
        for _ in sig_fin:
            if sum(_) > 0:
                # test_fin=np.concatenate((test_fin,_))
                test_fin.append(_)
        # print("min=>")
        a = [item for item in sig_fin if sum(item) > 0]
        # print(min(a, key=sum))
        min_value = sum(min(a, key=sum))
        max_value = sum(max(a, key=sum))
        # print(sum1)
        img_rgb_mean = np.nanmean(test_fin, axis=0)
        print(img_rgb_mean)
        return img_rgb_mean, min_value, max_value

    def MeanRGB(thresh, frame, img_rgb, last_stage, min_value, max_value):
        cv2.imshow("threshh", img_rgb)
        print("==<>>")
        # print(img_rgb)
        # cv2.waitKey()
        # print(img_rgb[0])
        # thresh=thresh.reshape((1,3))
        # img_rgb_mean=np.nanmean(thresh,axis=0)
        a = [item for item in img_rgb[0] if (sum(item) < max_value and sum(item) > 200)]
        # a = filter(lambda (x,y,z) : i+j+k>764 ,frame[0])
        # print(a[1:10])
        # img_temp = [item for item in img_rgb if sum(item)>764]
        # print(frame[0])
        # print(img_temp)
        # print(np.mean(a, axis=(0)))
        if a:
            print("==>")
            # print(a)
            print("==>")
            img_mean = np.mean(a, axis=(0))
            # print(img_mean)

            return img_mean[::-1]
        else:
            return last_stage

    def MeanRGB2(thresh, frame, img_rgb, min_value, max_value):
        cv2.imshow("threshh", img_rgb)
        print("==<>>")
        # print(img_rgb)
        # cv2.waitKey()
        # print(img_rgb[0])
        # thresh=thresh.reshape((1,3))
        # img_rgb_mean=np.nanmean(thresh,axis=0)
        a = [item for item in img_rgb[0] if (sum(item) < max_value and sum(item) > min_value)]
        # a = filter(lambda (x,y,z) : i+j+k>764 ,frame[0])
        # print(a[1:10])
        # img_temp = [item for item in img_rgb if sum(item)>764]
        # print(frame[0])
        # print(img_temp)
        # print(np.mean(a, axis=(0)))

        print("==>")
        # print(a)
        print("==>")
        img_mean = np.mean(a, axis=(0))
        # print(img_mean)

        return img_mean[::-1]

    def MeanRGB(thresh, frame, img_rgb, last_stage, max_value, min_value):
        cv2.imshow("threshh", img_rgb)
        print("==<>>")
        # print(img_rgb)
        # cv2.waitKey()
        # print(img_rgb[0])
        # thresh=thresh.reshape((1,3))
        # img_rgb_mean=np.nanmean(thresh,axis=0)
        a = [item for item in img_rgb[0] if (sum(item) < 764 and sum(item) > 200)]
        # a = filter(lambda (x,y,z) : i+j+k>764 ,frame[0])
        # print(a[1:10])
        # img_temp = [item for item in img_rgb if sum(item)>764]
        # print(frame[0])
        # print(img_temp)
        # print(np.mean(a, axis=(0)))
        if a:
            print("==>")
            # print(a)
            print("==>")
            img_mean = np.mean(a, axis=(0))
            # print(img_mean)

            return img_mean[::-1]
        else:
            return last_stage

    def MeanRGB2(thresh, frame, img_rgb):
        cv2.imshow("threshh", img_rgb)
        print("==<>>")
        # print(img_rgb)
        # cv2.waitKey()
        # print(img_rgb[0])
        # thresh=thresh.reshape((1,3))
        # img_rgb_mean=np.nanmean(thresh,axis=0)
        a = [item for item in img_rgb[0] if (sum(item) < 764 and sum(item) > 200)]
        # 764 being the max value of skin in HSV range

        # 200 being the min value of hsv range

        # a = filter(lambda (x,y,z) : i+j+k>764 ,frame[0])
        # print(a[1:10])
        # img_temp = [item for item in img_rgb if sum(item)>764]
        # print(frame[0])
        # print(img_temp)
        # print(np.mean(a, axis=(0)))

        # print("==>")
        # print(a)
        # print("==>")
        img_mean = np.mean(a, axis=(0))
        # print(img_mean)

        return img_mean[::-1]

    def face_detect_and_thresh2(frame):
        lower = np.array([0, 58, 50] , dtype="uint8")
        upper = np.array([30, 255, 255], dtype="uint8")
        face_frame = frame[gBox[0]:gBox[1], gBox[2]:gBox[3]]
        # cv2.imshow("face_testing",face_frame)
        frame = face_frame
        cv2.imshow("testing", frame)
        converted = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
        skinMask = cv2.inRange(converted, lower, upper)
        # apply a series of erosions and dilations to the mask
        # using an elliptical kernel
        kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11, 11))
        skinMask = cv2.erode(skinMask, kernel, iterations=2)
        skinMask = cv2.dilate(skinMask, kernel, iterations=2)
        # blur the mask to help remove noise, then apply the
        # mask to the frame
        skinMask = cv2.GaussianBlur(skinMask, (3, 3), 0)
        skin = cv2.bitwise_and(frame, frame, mask=skinMask)
        cv2.imshow("skin", skin)
        # print(np.mean(skin, axis=(0, 1)))
        # cv2.imshow("face_testing",skin)
        return skin, frame

    def preprocess(z1, z2, detrended_RGB, window_size, size_video, duration, frame):
        temp = (int(size_video/duration))
        f = frame-2

        main_R = []
        main_B = []
        out = []
        for i in range(len(detrended_RGB)-f+1):

            temp_R = z1[i:i+f-1]
            temp_B = z2[i:i+f-1]
            p = [list(a) for a in zip(temp_R, temp_B)]

            out.append(p)
            # if not main_R:
            #     main_R.append(temp_R)
            # else:
            #     main_R=[main_R,temp_R]
            #
            # if not main_B:
            #     main_B.append(temp_B)
            # else:
            #     main_B=[main_B,temp_B]

        # out=[main_R,main_B]
        # print(out[0])
        return out[0]

    def SPooEsitmate(final_sig, video_size, frames, seconds):
        A = 101.6
        B = 5.834

        #  A and B are empirically determined coefficients, Iac and Idc are respectively the amplitudes of the pulsatile (ac) and dc components
        # the determination of these values were done on the basis of the paper published
        z1 = [item[0] for item in final_sig]
        z3 = [item[2] for item in final_sig]
        #The mean average value of Blue and Red  is taken from the set of frames
        SPO_pre = []
        for _ in range(len(z1)):
            SPO_pre.append([z1[_], z3[_]])
        Spo2 = preprocess(z1, z3, SPO_pre, 10, video_size, 10, frames)

        R_temp = [item[0] for item in Spo2]
        DC_R_comp = np.mean(R_temp)
        AC_R_comp = np.std(R_temp)

        I_r = AC_R_comp/DC_R_comp

        B_temp = [item[1] for item in Spo2]
        DC_B_comp = np.mean(B_temp)
        AC_B_comp = np.std(B_temp)

        I_b = AC_B_comp/DC_B_comp
        SpO2_value = (A-B*((I_b*650)/(I_r*950)))
        # Valye 650 and 950 was chosen as the wavelengths of red and near infrared
        return SpO2_value

    # main code begins from here and all the above helper fuctions are called

    # Enter the path of video you wanna give into the code
    cap = cv2.VideoCapture("videos/filename.avi")

    # Calculates the total number of frames present in the video
    length = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    fps = cap.get(cv2.CAP_PROP_FPS)      # OpenCV2 version 2 used "CV_CAP_PROP_FPS"
    frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    print(frame_count)
    duration = frame_count/fps
    seconds = duration % 60

    print(length)

    if (cap.isOpened() == False):
        print("Error opening video file")  # Catching anykind of error while opening video

    while(cap.isOpened()):
        ret, frame = cap.read()

        # cv2.waitKey()
        if frameCount == 1:
            # cv2.imshow("testing if flipped",frame)  #just run this if you have issue with inverted frames, can result in fail detection
            # cv2.waitKey()
            # cv2.imshow("testing if flipped2",imutils.rotate_bound(frame, 90))  #just run this if you have issue with inverted frames, can result in fail detection
            # cv2.waitKey()
            # frame= imutils.rotate_bound(frame, 90)
            firstFrame = frame
            # The first frame is sent to the fuction to make a face mask
            thresh, img_rgb, maskT = face_detect_and_thresh(firstFrame)
            cv2.imshow("img_rgb", img_rgb)
            # cv2.waitKey()
            frameCount += 1

        if ret == True:
            # cv2.imshow("testing if flipped",frame)  #just run this if you have issue with inverted frames, can result in fail detection
            frameCount += 1
            # frame= imutils.rotate_bound(frame, 90)

            print(frameCount)
            cv2.imshow('Frame', frame)

            start = time.time()
            # rest of frames are sent to a function to have a threshold/overlap with mask
            thresh, img_rgb = face_detect_and_thresh2(frame)
            end = time.time()
            print(end - start)

            # start = time.time()
            #
            # final_sig.append(spartialAverage(maskT,frame)) # The frames are then sent to spartialAverage to find RGB mean values
            # end = time.time()
            # print(end - start)

            start = time.time()
            if final_sig:
                # The frames are then sent to spartialAverage to find RGB mean values
                final_sig.append(MeanRGB(maskT, frame, img_rgb, final_sig[-1], min_value, max_value))
            else:
                temp, min_value, max_value = spartialAverage(maskT, frame)
                final_sig.append(temp)
            end = time.time()
            print(end - start)

            #  Those rgb values are appened to final_sig which will be used for spo2 estimation
            if cv2.waitKey(25) & 0xFF == ord('q'):  # end the loop if your press 'q' or video reaches end
                break
            # if frameCount==32:
            #     break

      # Break the loop
        else:
            break

    # When everything done, release
    # the video capture object
    cap.release()

    # Closes all the frames
    cv2.destroyAllWindows()

    # For debugging you can try saving the signal as a pickle and run usingPick.py
    pickle.dump(final_sig, open("spo2.p", "wb"))
    print(final_sig)
    # the final signal list is sent to SPooEsitmate function with length of the video
    result = SPooEsitmate(final_sig, length, length, seconds)
    print(result)
    return result

def append_df_to_excel(filename, df, sheet_name='Sheet1', startrow=None,
                       truncate_sheet=False,
                       **to_excel_kwargs):
    """
    Append a DataFrame [df] to existing Excel file [filename]
    into [sheet_name] Sheet.
    If [filename] doesn't exist, then this function will create it.

    Parameters:
      filename : File path or existing ExcelWriter
                 (Example: '/path/to/file.xlsx')
      df : dataframe to save to workbook
      sheet_name : Name of sheet which will contain DataFrame.
                   (default: 'Sheet1')
      startrow : upper left cell row to dump data frame.
                 Per default (startrow=None) calculate the last row
                 in the existing DF and write to the next row...
      truncate_sheet : truncate (remove and recreate) [sheet_name]
                       before writing DataFrame to Excel file
      to_excel_kwargs : arguments which will be passed to `DataFrame.to_excel()`
                        [can be dictionary]

    Returns: None
    """
    from openpyxl import load_workbook

    # ignore [engine] parameter if it was passed
    if 'engine' in to_excel_kwargs:
        to_excel_kwargs.pop('engine')

    writer = pd.ExcelWriter(filename, engine='openpyxl')

    # Python 2.x: define [FileNotFoundError] exception if it doesn't exist
    try:
        FileNotFoundError
    except NameError:
        FileNotFoundError = IOError

    try:
        # try to open an existing workbook
        writer.book = load_workbook(filename)

        # get the last row in the existing Excel sheet
        # if it was not specified explicitly
        if startrow is None and sheet_name in writer.book.sheetnames:
            startrow = writer.book[sheet_name].max_row

        # truncate sheet
        if truncate_sheet and sheet_name in writer.book.sheetnames:
            # index of [sheet_name] sheet
            idx = writer.book.sheetnames.index(sheet_name)
            # remove [sheet_name]
            writer.book.remove(writer.book.worksheets[idx])
            # create an empty sheet [sheet_name] using old index
            writer.book.create_sheet(sheet_name, idx)

        # copy existing sheets
        writer.sheets = {ws.title:ws for ws in writer.book.worksheets}
    except FileNotFoundError:
        # file does not exist yet, we will create it
        pass

    if startrow is None:
        startrow = 0

    # write out the new sheet
    df.to_excel(writer, sheet_name,header=False, startrow=startrow, **to_excel_kwargs)

    # save the workbook
    writer.save()

def add_to_list(str_name, atte, details,t,Spo2Count):
	# atte = atte
	# t = time.localtime()
    current_time = time.strftime("%H:%M:%S", t)
    d = details[details['Name'] == str_name]
    d['Time-Stamp'] = current_time
    d['SpO2_value']=Spo2Count
    a = d.index[0]
    return atte.append(d)

def detect_and_predict_mask(frame, faceNet, maskNet):
	# grab the dimensions of the frame and then construct a blob
	# from it
	(h, w) = frame.shape[:2]
	blob = cv2.dnn.blobFromImage(frame, 1.0, (300, 300),
		(104.0, 177.0, 123.0))

	# pass the blob through the network and obtain the face detections
	faceNet.setInput(blob)
	detections = faceNet.forward()

	# initialize our list of faces, their corresponding locations,
	# and the list of predictions from our face mask network
	faces = []
	locs = []
	preds = []

	# loop over the detections
	for i in range(0, detections.shape[2]):
		# extract the confidence (i.e., probability) associated with
		# the detection
		confidence = detections[0, 0, i, 2]

		# filter out weak detections by ensuring the confidence is
		# greater than the minimum confidence
		if confidence > args["confidence"]:
			# compute the (x, y)-coordinates of the bounding box for
			# the object
			box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
			(startX, startY, endX, endY) = box.astype("int")

			# ensure the bounding boxes fall within the dimensions of
			# the frame
			(startX, startY) = (max(0, startX), max(0, startY))
			(endX, endY) = (min(w - 1, endX), min(h - 1, endY))

			# extract the face ROI, convert it from BGR to RGB channel
			# ordering, resize it to 224x224, and preprocess it
			face = frame[startY:endY, startX:endX]
			face = cv2.cvtColor(face, cv2.COLOR_BGR2RGB)
			face = cv2.resize(face, (224, 224))
			face = img_to_array(face)
			face = preprocess_input(face)

			# add the face and bounding boxes to their respective
			# lists
			faces.append(face)
			locs.append((startX, startY, endX, endY))

	# only make a predictions if at least one face was detected
	if len(faces) > 0:
		# for faster inference we'll make batch predictions on *all*
		# faces at the same time rather than one-by-one predictions
		# in the above `for` loop
		faces = np.array(faces, dtype="float32")
		preds = maskNet.predict(faces, batch_size=32)

	# return a 2-tuple of the face locations and their corresponding
	# locations
	return (locs, preds)

# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-e", "--encodings", default="encoding2.pickle",required=True,
	help="path to serialized db of facial encodings")
ap.add_argument("-o", "--output", type=str,
	help="path to output video")
ap.add_argument("-y", "--display", type=int, default=1,
	help="whether or not to display output frame to screen")
ap.add_argument("-d", "--detection-method", type=str, default="hog",
	help="face detection model to use: either `hog` or `cnn`")
ap.add_argument("-f", "--face", type=str,
	default="face_detector",
	help="path to face detector model directory")
ap.add_argument("-m", "--model", type=str,
	default="model/mask_detector.model",
	help="path to trained face mask detector model")
ap.add_argument("-c", "--confidence", type=float, default=0.5,
	help="minimum probability to filter weak detections")
args = vars(ap.parse_args())

# load our serialized face detector model from disk
print("[INFO] loading face detector model...")
prototxtPath = os.path.sep.join([args["face"], "deploy.prototxt"])
weightsPath = os.path.sep.join([args["face"],
	"res10_300x300_ssd_iter_140000.caffemodel"])
faceNet = cv2.dnn.readNet(prototxtPath, weightsPath)

# load the face mask detector model from disk
print("[INFO] loading face mask detector model...")
maskNet = load_model(args["model"])

# initialize the video stream and allow the camera sensor to warm up

# load the known faces and embeddings
print("[INFO] loading encodings...")
data = pickle.loads(open(args["encodings"], "rb").read())

model = load_model(args["model"])

# initialize the video stream and pointer to output video file, then
# allow the camera sensor to warm up
print("[INFO] starting video stream...")
# vs = cv2.VideoCapture(0)
writer = None
time.sleep(2.0)

# loop over frames from the video file stream
while True:
	# grab the frame from the threaded video stream
    ret,frame = vs.read()
    while(Fcount):
        ret, frame = vs.read()

        if ret == True:

    # Write the frame into the
    # file 'filename.avi'
            result.write(frame)
            Fcount=Fcount-1
    # Display the frame
    # saved in the file
            # cv2.imshow('Frame', frame)

    # Press S on keyboard
    # to stop the process

    # Break the loop
        else:
            break

    result.release()

    # convert the input frame from BGR to RGB then resize it to have
	# a width of 750px (to speedup processing)
    rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    rgb = imutils.resize(frame, width=750)
    r = frame.shape[1] / float(rgb.shape[1])

	# detect the (x, y)-coordinates of the bounding boxes
	# corresponding to each face in the input frame, then compute
	# the facial embeddings for each face
    boxes = face_recognition.face_locations(rgb,model=args["detection_method"])
    encodings = face_recognition.face_encodings(rgb, boxes)
    names = []

	# loop over the facial embeddings
    for encoding in encodings:
	    # attempt to match each face in the input image to our known
        # encodings
        matches = face_recognition.compare_faces(data["encodings"],	encoding)
        name = "Unknown"

        # check to see if we have found a match
        if True in matches:
	        # find the indexes of all matched faces then initialize a
            # dictionary to count the total number of times each face
            # was matched
            matchedIdxs = [i for (i, b) in enumerate(matches) if b]
            counts = {}

            # loop over the matched indexes and maintain a count for
            # each recognized face face
            for i in matchedIdxs:
                name = data["names"][i]
                counts[name] = counts.get(name, 0) + 1
            print(counts)
			# determine the recognized face with the largest number
			# of votes (note: in the event of an unlikely tie Python
			# will select first entry in the dictionary)
            name = max(counts, key=counts.get)
            print(name)
		# update the list of names
        names.append(name)

	# loop over the recognized faces
    for ((top, right, bottom, left), name) in zip(boxes, names):
        # rescale the face coordinates
        top = int(top * r)
        right = int(right * r)
        bottom = int(bottom * r)
        left = int(left * r)
        face=frame[top:bottom,left:right]
        face = cv2.cvtColor(face, cv2.COLOR_BGR2RGB)
        face = cv2.resize(face, (224, 224))
        face = img_to_array(face)
        face = preprocess_input(face)
        face = np.expand_dims(face, axis=0)
        # pass the face through the model to determine if the face
        # has a mask or not
        # draw the predicted face name on the image
        cv2.rectangle(frame, (left, top), (right, bottom),(0, 255, 0), 2)
        y = top - 15 if top - 15 > 15 else top + 15
        cv2.putText(frame, name, (left, y), cv2.FONT_HERSHEY_SIMPLEX,0.75, (0, 255, 0), 2)
        print(name)
        if counts:
            try:
                if counts[name]>4:
                    while 1:
                        print("in loop")
                        cv2.waitKey(1)
                        ret, frame1 = vs.read()
                        # cv2.imshow("Frame", frame1)
                        face = frame1[top+10:bottom-10, left:right]
                        face = cv2.cvtColor(face, cv2.COLOR_BGR2RGB)
                        face = cv2.resize(face, (224, 224))
                        face = img_to_array(face)
                        face = preprocess_input(face)
                        face = np.expand_dims(face, axis=0)
                        frame1=cv2.rectangle(frame1,(left,top),(right,bottom),(255,0,0),2)
                        cv2.putText(frame1,"Please wear mask in the bounded box",(10,40),cv2.FONT_HERSHEY_SIMPLEX, 0.7,(0,255,255),2)
						# cv2.putText(output, "OpenCV + Jurassic Park!!!", (10, 25),
						# 			cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
                        if args["display"] > 0:
                            cv2.imshow("Frame", frame1)
                            key = cv2.waitKey(1) & 0xFF
                            # if the `q` key was pressed, break from the loop
                            if key == ord("q"):
                                break
                        (mask, withoutMask) = model.predict(face)[0]
                        if mask>withoutMask:
                            frame1 = vs.read()
                            atte = atte
                            t = time.localtime()
                            current_time = time.strftime("%H_%M_%S", t)
                            current_time=str(current_time)
                            today = date.today()
                            if not(str(today) in os.listdir("output/")):
                                os.mkdir("output/"+str(today))
                            cv2.imwrite("output/"+str(today)+"/"+str(name)+"-"+current_time+"non-mask.bmp", frame)
                            cv2.imwrite("output/"+str(today)+"/"+str(name)+"-"+current_time+"mask.bmp", frame)
                            Spo2Count=spo2Func()
                            print(Spo2Count)
                            print("wearing mask")
                            atte = add_to_list(name, atte, details,t,Spo2Count)
                            xl = pd.ExcelFile('attendance.xlsx')
                            today = date.today()

                            if str(today) in xl.sheet_names:
                                append_df_to_excel('attendance.xlsx', atte, sheet_name=str(today))
                            else:
                                with pd.ExcelWriter('attendance.xlsx', mode='A') as writer:
                                    atte.to_excel(writer, sheet_name=str(today))
                                break
                        else:
                            print("not wearing mask")
                            # cv2.imshow("Frame", frame1)
            except Exception as e:
                print(repr(e))
        t = time.localtime()
        current_time = time.strftime("%H:%M:%S", t)
        print(current_time)
	# loop over the detected face locations and their corresponding
	# locations
	# show the output frame
	# cv2.imshow("Frame", frame)
	# if the video writer is None *AND* we are supposed to write
	# the output video to disk initialize the writer
    if writer is None and args["output"] is not None:
        fourcc = cv2.VideoWriter_fourcc(*"MJPG")
        writer = cv2.VideoWriter(args["output"], fourcc, 20,(frame.shape[1], frame.shape[0]), True)

	# if the writer is not None, write the frame with recognized
	# faces t odisk
	# if writer is not None:
	# 	writer.write(frame)

	# check to see if we are supposed to display the output frame to
	# the screen
    if args["display"] > 0:
        cv2.imshow("Frame", frame)
        key = cv2.waitKey(1) & 0xFF
        # if the `q` key was pressed, break from the loop
        if key == ord("q"):
            break

# do a bit of cleanup
cv2.waitKey(1)
cv2.destroyAllWindows()
for i in range (1,5):
    cv2.waitKey(1)

vs.stop()

# check to see if the video writer point needs to be released
if writer is not None:
    writer.release()


import random
import time
import numpy as np
import sys
import pokedex as pok

# delay printing (like in pokemon games)
def delay_print(s: str):
    # prints one char at a time
    for char in s:
        sys.stdout.write(char)
        sys.stdout.flush()
        time.sleep(0.1)

class Pokemon:
    def __init__(self, name, health='===================='):
        # save variables as attributes
        self.name = name
        self.type = pok.types(name)[0]
        self.moves = pok.attacks(name)
        self.attack = pok.atk(name)
        self.defense = pok.defense(name)
        self.speed = pok.speed(name)
        self.health = health
        self.HP = pok.HP(name)
        self.bars = 20  # amount of health bars

    def print_stats(self):
        print(f"Name: {self.name}\nType: {self.type}\nMoves: {self.moves}\nAtk: {self.attack}\nDef: {self.defense}\nSpeed: {self.speed}\nHealth: {self.health}")

    def fight(self, Pokemon2):
        # Two pokemon fight each other
        # Print info on fight
        print("-----POKEMON BATTLE-----")
        print(f"\n{self.name}")
        print(self.type, "TYPE")
        print(self.HP, " | HP")
        print(self.attack, " | ATTACK")
        print(self.defense, " | DEFENSE")
        print(self.speed, " | SPEED")
        print("\n-----VS-----")
        print(f"\n{Pokemon2.name}")
        print(Pokemon2.type, "TYPE")
        print(Pokemon2.HP, " | HP")
        print(Pokemon2.attack, " | ATTACK")
        print(Pokemon2.defense, " | DEFENSE")
        print(Pokemon2.speed, " | SPEED")

        time.sleep(2)

        # type advantages
        coeff = pok.type_weak_or_strong(self.name, Pokemon2.name)
        # if pokemon2 type normal effectiveness
        if coeff == 1:
            str1_atk = "...\n"
            str2_atk = "...\n"

        # Pokemon2 type > pokemon type
        if coeff == 0.5:
            Pokemon2.attack *= 2
            Pokemon2.defense *= 2
            self.attack /= 2
            self.defense /= 2
            str1_atk = "It's not very effective...\n"
            str2_atk = "It's super effective!\n"

        # Pokemon 2 type < pokemon type
        if coeff == 2:
            self.attack *= 2
            self.defense *= 2
            Pokemon2.attack /= 2
            Pokemon2.defense /= 2
            str1_atk = "It's super effective!\n"
            str2_atk = "It's not very effective...\n"

        # fighting loop (while pokemons have health)
        turn = 0
        while (self.bars > 0) and (Pokemon2.bars > 0):
            turn += 1
            print(f"-----------Turn {turn}------------\n")
            # print the health of each pokemon
            print(f"{self.health}\tHP\t{self.name}\n")
            print(f"{Pokemon2.health}\tHP\t{Pokemon2.name}\n")

            print(f"Go {self.name}!")
            for i, x in enumerate(self.moves):
                print(f"{i+1}.", x)
            index = int(input("Pick a move. >>> "))
            cur_move = self.moves[index-1]
            delay_print(f"{self.name} used {cur_move}!\n")
            time.sleep(0.5)
            delay_print(str1_atk)

            # dmg calculation
            dmg = pok.dmg(self.name, self)
            Pokemon2.HP -= dmg
            Pokemon2.bars -= round(dmg/(Pokemon2.HP/20))
            Pokemon2.health = ""

            # add health back and def
            for j in range(int(Pokemon2.bars)):
                Pokemon2.health += "="

            time.sleep(1)
            print(f"{self.health}\tHP\t{self.name}\n")
            print(f"{Pokemon2.health}\tHP\t{Pokemon2.name}\n")
            time.sleep(0.5)

            # Check if pokemon alive
            money = np.random.choice(5000)
            if Pokemon2.bars <= 0:
                delay_print("\n... " + Pokemon2.name + f" fainted.  Opponent paid you ₱{money}.")
                break

            if self.bars <= 0:
                delay_print("\n... " + self.name + f" fainted.  Opponent paid you ₱{money}.")

            # Pokemon2 turn
            print(f"Go {Pokemon2.name}!")
            index = random.randint(1, 4)
            cur_move = Pokemon2.moves[index - 1]
            delay_print(f"{Pokemon2.name} used {cur_move}!\n")
            time.sleep(1)
            delay_print(str2_atk)

            # dmg calculation
            dmg = pok.dmg(self.name, self)
            self.HP -= dmg
            self.bars -= round(dmg/(self.HP/20))
            self.health = ""

            # add health back and def
            for j in range(int(self.bars)):
                self.health += "="

            time.sleep(1)
            print(f"{self.health}\tHP\t{self.name}\n")
            print(f"{Pokemon2.health}\tHP\t{Pokemon2.name}\n")
            time.sleep(0.5)

            # Check if pokemon alive
            if Pokemon2.bars <= 0:
                delay_print("\n... " + Pokemon2.name + f" fainted.  Opponent paid you ₱{money}.\n")
                break

            if self.bars <= 0:
                delay_print("\n... " + self.name + f" fainted.  You paid the opponent ₱{money}.\n")

lis1 = pok.pokemon_list()
apok1 = input("Choose your pokemon! (names only, uppercase at the beginning) >>> ")
apok2 = random.choice(lis1)

Pokemon.fight(Pokemon(apok1), Pokemon(apok2))


# Banana test function for arbitrary dimension

import numpy as np
from scipy.stats import special_ortho_group
import warnings

class Banana:
    def __init__(self, dim: int, a: np.ndarray, seed: bool = None):
        """
        Creates a random rotation of the following function in D dimensions:
            f(x) = exp (- 0.5*(x_1^2 + (a_0 x_1^2 + a_1 x_2 + a_2)^2 + sum_{i=3}^D a_i x_i^2 ))
        :param a: parameters of the banana
        """
        if a.size < dim + 1:
            warnings.warn('parameter vector is too short, remaining ones will be filled with 1.')
            a = np.concatenate([a.flatten(), np.ones((dim+1 - a.size))])

        self.dim = dim
        self.a = a

        # sample a random rotation matrix
        if seed is not None:
            self.R = special_ortho_group(dim, seed).rvs()
        else:
            self.R = np.eye(dim)

    def _x_trafo(self, x):
        x = self.R @ x
        if x.ndim == 1:
            x = x[:, None]
        x[1, :] = self.a[0] * x[0, :] ** 2 + self.a[1] * x[1, :] + self.a[2]
        x[2:, :] = self.a[3:, None] * x[2:, :]
        return x

    def f(self, x):
        "Some unnormalized density"
        return np.exp(-self.pot_energy(x)).squeeze()

    def pot_energy(self, x):
        "The negative log of the above, that is, the potential energy"
        arg = self._x_trafo(x)
        return 0.5 * (arg ** 2).sum(axis=0).squeeze()

    def grad_e(self, x):
        grad = self._x_trafo(x)
        grad[0, :] = grad[0, :] + grad[1, :] * 2 * x[0, :] * self.a[0]
        grad[1, :] = grad[1, :] * self.a[1]
        return grad


import os
import time
import torch

import torchvision.transforms as transforms

from utils import get_device
from data import get_dataset
from losses import ZeroReferenceLoss
from model import ZeroDCE
from config_file import ConfigFile

# ---------------------------------------- DATA --------------------------------------- #

DATA_DIR = 'data\\lol_dataset'
TRAIN_DIR_NAME = 'our485'
VAL_DIR_NAME = 'eval15'

RESIZE_SIZE = (512, 512)
IMAGES_TRANSFORM = transforms.Compose([
    transforms.ToTensor(),
    transforms.Resize(RESIZE_SIZE, antialias=True),
])

CONFIG_FILE_PATH = 'config.yaml'

# ---------------------------------------- TRAIN --------------------------------------- #
def evaluate_data(data, model, loss):
    """ Evaluate the model on a given dataset"""
    model.eval()
    eval_loss = 0.0
    with torch.no_grad():
        print(f'[INFO] VALIDATION START')

        # loop over the validation set
        for batch_num, eval_low_light_images in enumerate(data):
            enhanced, alpha_maps = model(eval_low_light_images)

            loss, losses_dict = loss(enhanced_images=enhanced,
                                     orig_images=eval_low_light_images,
                                     alpha_maps=alpha_maps)
            eval_loss += loss.item()

    # Calculate average loss over an epoch
    eval_loss = round(eval_loss / len(data), 5)
    return eval_loss

def train_epoch(epoch_num, train_data, model, optimizer, loss):
    """ Train the model for one epoch"""
    model.train()
    train_loss = 0.0
    for batch_num, train_low_light in enumerate(train_data):
        print(f'[INFO] EPOCH NUM: {epoch_num + 1}, BATCH NUM: {batch_num + 1}/{len(train_data)}')
        enhanced, alpha_maps = model(train_low_light)

        loss, losses_dict = loss(enhanced_images=enhanced,
                                 orig_images=train_low_light,
                                 alpha_maps=alpha_maps)
        train_loss += loss.item()

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

    # Calculate average loss over an epoch
    train_loss = train_loss / len(train_data)
    return train_loss

# ---------------------------------------- MAIN --------------------------------------- #

def train():
    device = get_device()

    # output directory
    output_dir_path = os.path.join('outputs', time.strftime("%Y%m%d-%H%M%S"))
    os.makedirs(output_dir_path)

    # config
    config = ConfigFile.load(config_path=CONFIG_FILE_PATH)
    config_path = os.path.join(output_dir_path, 'config.yaml')
    config.save_config(config_path)

    # model
    model = ZeroDCE(config=config, device=device)

    # Datasets
    batch_size = config.train.batch_size
    train_data = get_dataset(
        dir_path=os.path.join(DATA_DIR, TRAIN_DIR_NAME),
        transform_img=IMAGES_TRANSFORM, batch_size=batch_size, device=device
    )
    eval_data = get_dataset(
        dir_path=os.path.join(DATA_DIR, VAL_DIR_NAME),
        transform_img=IMAGES_TRANSFORM, batch_size=batch_size, device=device
    )
    # loss function and optimizer
    loss_fn = ZeroReferenceLoss()
    optimizer = torch.optim.Adam(model.parameters(),
                                 lr=config.train.learning_rate,
                                 weight_decay=config.train.weight_decay)

    # Train Loop
    min_valid_loss = float('inf')
    for epoch_num in range(config.train.num_epochs):

        # Train the model for one epoch and calculate the train loss (average over epoch).
        train_loss = train_epoch(epoch_num, train_data, model, optimizer, loss_fn)
        print(f'[INFO] EPOCH NUM: {epoch_num + 1}, TRAIN LOSS: {train_loss:.5f}')

        # Evaluate the model on the validation set and calculate the validation loss.
        eval_loss = evaluate_data(eval_data, model, loss_fn)
        print(f'[INFO] EPOCH NUM: {epoch_num + 1}, VALIDATION LOSS: {eval_loss}')

        if min_valid_loss > eval_loss:
            print(f'[INFO] Validation Loss Decreased({min_valid_loss}--->{eval_loss}) '
                  f'\t Saving The Model ({output_dir_path})')
            min_valid_loss = eval_loss

            # Saving State Dict
            model_output_path = os.path.join(output_dir_path, f'model_{eval_loss}.pth')
            torch.save(model.state_dict(), model_output_path)
    print(f'[INFO] Training finished. Output directory: {output_dir_path}')

if __name__ == '__main__':
    train()


from flask import Blueprint, flash, g, redirect, render_template, request, session, url_for

from zip_code_app.models import City
from sqlalchemy import func

from zip_code_app.db import get_db

bp = Blueprint("city", __name__, url_prefix="/cities")

@bp.route("/", methods=['GET'])
def get_cities_groupby():
    cities = []
    db = get_db()
    cities_groupby = City.query.with_entities(City.latitude, City.longitude, func.count(City.id)).group_by(City.latitude, City.longitude).all()

    for city in cities_groupby:
        cities.append({
            'latitude': city[0],
            'longitude': city[1],
            'count': city[2]
        })
    return {
        "data": cities
    }

@bp.route("/all", methods=['GET'])
def get_cities_all():
    cities = []
    db = get_db()

    for city in City.query.all():
        cities.append(city.serialize())
    return {
        "data": cities
    }


#!/usr/bin/env python3
from __future__ import print_function
import sys
if sys.version.startswith('2'):
    range = xrange

def main():
    n = 101
    sum_of_squares = sum(i ** 2 for i in range(1, n))
    square_of_sum = sum(range(1, n)) ** 2
    print(square_of_sum - sum_of_squares)

if __name__ == '__main__':
    main()


import game
import pprint

state = {}

def draw(surface):

    global state

    state["mouse_pos"] = game.pygame.mouse.get_pos()
    state["mouse_rel"] = game.pygame.mouse.get_rel()
    state["mouse_pressed"] = game.pygame.mouse.get_pressed()
    state["mouse_focused"] = game.pygame.mouse.get_focused()
    state["objects"] = len(game.objects)
    state["can_shoot_at"] = game.player.can_shoot_at

    # very ugly temporary fix to text wrapping problem
    textTop = 0
    for key, val in game.on_screen_debugger.state.items():
        font = game.pygame.font.SysFont("Comic Sans MS", 20)
        textsurface = font.render(pprint.pformat({
            key: val
        }), False, (0, 0, 0))
        surface.blit(textsurface, (0, textTop))
        textTop += 25


from node import node
from timer import timer
import statistics
import random
import math

black = (0, 0, 0)

class keeper:
    def __init__(self, bounds, safetyBounds=0.1):
        self.nodes = []
        self.bounds = bounds
        self.c_dis = 0
        self.a_dis = 0
        self.ang_dis = 0
        self.lock_clustering = False
        self.lock_avoidance = False
        self.lock_angleMatching = False
        self.lock_maxAngle = False
        self.safetyBounds = safetyBounds

    #Debugging #################
    def debugHerd(self):
        print("Number of Nodes: " + len(self.nodes))
        print("Clustering: " + str(c_dis > 0))

    #Generation and Updating #################
    def updateHerd(self):
        for nodeNum in range(0, len(self.nodes)):
            node = self.nodes[nodeNum]
            listing = self.sortByDistance(node)
            if(self.c_dis > 0):
                self.clusterNode(node, listing)
            if(self.a_dis > 0):
                self.avoidNode(node, listing)
            if(self.ang_dis > 0):
                self.matchNode(node, listing)

        for nodeNum in range(0, len(self.nodes)):
            node = self.nodes[nodeNum]
            node.updatePosistion()
            node = self.handleEdges(node)
        return(self.nodes)

    def addRandomizedNodes(self, nodeSize, num, speed=10, color=black):
        for i in range(0, num):
            locX = random.randint(0, self.bounds[0])
            locY = random.randint(0, self.bounds[1])
            angle = random.randint(0, 359)
            self.generateNode(nodeSize, [locX, locY], speed=speed, angle=angle,color=color)

    def generateNode(self, nodeSize, pos, speed = 0, angle = 0, color=black):
        newNode = node(nodeSize, pos = pos, speed = speed, angle = angle,  color=color)
        self.nodes.append(newNode)
        return(newNode)

    def handleEdges(self, node):
        #Behavor: when we come to an edge we stop the node
        xBound = self.bounds[0]
        yBound = self.bounds[1]
        xPos = node.pos[0]
        yPos = node.pos[1]
        radius = node.nSize/2

        safteyX = xBound*self.safetyBounds
        safteyY = yBound*self.safetyBounds

        if(xPos < 0):
            node.atEdge = 1
            node.pos[0] = 0
            node.setRestriction(1)
        elif(xPos > xBound - radius):
            node.atEdge = 2
            node.pos[0] = xBound - radius
            node.setRestriction(2)
        elif(yPos < 0):
            node.atEdge = 3
            node.pos[1] = 0
            node.setRestriction(3)
        elif(yPos > yBound - radius):
            node.atEdge = 4
            node.pos[1] = yBound - radius
            node.setRestriction(4)
        else:
            node.atEdge = 0
            rNum = node.restricted
            if((rNum == 1) or (rNum == 2)):
                if((xPos > safteyX) and (xPos < xBound - radius - safteyX)):
                    node.setRestriction(0)
            if((rNum == 3) or (rNum == 4)):
                 if((yPos > safteyY) and (yPos < yBound - radius - safteyY)):
                     node.setRestriction(0)
        return(node)

    ## DISTANCE #################
    def getNumClose(self, distance, listing):
        details = []
        i = 0
        while((i < len(listing)) and (listing[i][0] <= distance)):
            details.append([listing[i][1], listing[i][2], listing[i][3]])
            i+=1
        return(details)

    def sortByDistance(self, iNode):
        listing = []
        initX = iNode.pos[0]
        initY = iNode.pos[1]

        for node in self.nodes:
            if(node is not iNode):
                finalX = node.pos[0]
                finalY = node.pos[1]
                angle = node.angle
                d_calc = math.sqrt((finalX - initX)**2 + (finalY - initY)**2)
                listing.append([d_calc, finalX, finalY, angle])
        listing = sorted(listing)
        return(listing)

    ## CLUSTERING #################
    def enableClustering(self, distance):
        if(self.lock_clustering):
            return
        if(distance < 0):
            distance = 0

        self.c_dis = distance

    def clusterNode(self, node, listing):
        nodeX = node.pos[0]
        nodeY = node.pos[1]
        avgLoc = self.findLocalMean(self.c_dis, listing)
        if(avgLoc is not None):
            node.headToPos(avgLoc)

    def findLocalMean(self, distance, listing):
        listing = self.getNumClose(distance, listing)
        if(len(listing) == 0):
            return None
        xAvg = 0
        yAvg = 0
        for l in listing:
            xAvg += l[0]
            yAvg += l[1]
        xAvg = xAvg/len(listing)
        yAvg = yAvg/len(listing)
        return([xAvg, yAvg])

    ## AVOIDANCE
    def enableAvoidance(self, distance):
        if(self.lock_avoidance):
            return

        if(distance < 0):
            distance = 0

        self.a_dis = distance

    def avoidNode(self, node, listing):
        avgLoc = self.findLocalMean(self.a_dis, listing)
        if(avgLoc is not None):
            node.headToPos(avgLoc, invert=True)

    ## Angle Matching
    def enableAngleMatching(self, distance):
        if(self.lock_angleMatching):
            return

        if(distance < 0):
            distance = 0

        self.ang_dis = distance

    def matchNode(self, node, listing):
        angles = [node.angle]
        listing = self.getNumClose(self.ang_dis,listing)
        if(len(listing) > 1):
            for i in range(0,len(listing)):
                angles.append(listing[i][2])
            node.requestAngleChange(statistics.median(angles))

    #Match Angle
    def enableMaxAngle(self, maxAngle):
        if(self.lock_maxAngle):
            return

        for node in self.nodes:
            node.maxAng = maxAngle


from typing import List
from collections import defaultdict
from functools import cmp_to_key

class Solution:
    def decodeString(self, s: str) -> str:
        idx = 0

        def getString():
            nonlocal idx
            if idx == len(s) or s[idx] == ']':
                return ""
            cur = s[idx]
            repeatTime = 1
            ret = ""
            if cur.isdigit():
                repeatTime = getDigit()
                idx += 1
                repeatStr = getString()
                idx += 1
                for _ in range(repeatTime):
                    ret += repeatStr
            elif cur.isalpha():
                ret = cur
                idx += 1
            return ret + getString()

        def getDigit():
            nonlocal idx
            ret = 0
            while idx < len(s) and s[idx].isdigit():
                ret = ret * 10 + ord(s[idx]) - ord('0')
                idx += 1
            return ret

        return getString()

if __name__ == '__main__':
    sol = Solution()
    print(sol.decodeString("3[a]2[bc]"))
    print(sol.decodeString("3[a2[c]]"))


import setuptools
#
with open("README.md", "r") as fh:
    long_description = fh.read();
#
setuptools.setup(
    name="pySW",
    packages = ['pySW'],
    version="1.0",
    author="Kalyan Inamdar",
    author_email="kalyaninamdar@protonmail.com",
    url = 'https://github.com/kalyanpi4/pySW',
    download_url='https://github.com/kalyanpi4/pySW/archive/V1.0.tar.gz',
    description="A Python wrapper around Solidworks built-in VBA API for modifying Solidworks assemblies and parts.",
    install_requires=[
          'numpy',
          'pandas',
          'pywin32',
    ],
    classifiers=[
		'License :: OSI Approved :: LGPL-2.0',
        'Programming Language :: Python :: 3',
    ],
    python_requires='>=3.6',
)