import FreeCAD
import math
import re
import Part
import ArchCommands
import Draft
import DraftVecUtils
import DraftGeomUtils

MAXLOOP = 10  # the max number of loop before abort

DEBUG = FreeCAD.ParamGet(
    "User parameter:BaseApp/Preferences/Mod/Arch").GetBool("ShowVRMDebug")

DEFAULT_PATTERN_TEMPLATE = """
<pattern
    id="PATTERN_ID"
    patternUnits="userSpaceOnUse"
    x="0" y="0" width="${5}" height="${5}">
        <g>
            <rect width="${5}" height="${5}"
                style="stroke:none; fill:#ffffff" />
            <path style="stroke:PATTERN_COLOR; stroke-width:${0.2}; stroke-linecap:butt; stroke-linejoin:miter; fill:none; opacity:PATTERN_OPACITY"
                  d="M0,0 l${5},${5}" />
        </g>
</pattern>
"""

WOOD_PATTERN_TEMPLATE = """
<pattern
    id="PATTERN_ID"
    patternUnits="userSpaceOnUse"
    x="0" y="0" width="${5}" height="${5}">
        <g>
            <rect width="${5}" height="${5}"
                style="stroke:none; fill:#ffffff" />
            <path style="stroke:PATTERN_COLOR; stroke-width:${0.2}; stroke-linecap:butt; stroke-linejoin:miter; fill:none; opacity:PATTERN_OPACITY"
                  d="M${5},0 L0,${5}" />
        </g>
</pattern>
"""

INSULATION_HARD_PATTERN_TEMPLATE = """
<pattern
    id="PATTERN_ID"
    patternUnits="userSpaceOnUse"
    x="0" y="0" width="${2.5}" height="${2.5}">
        <g>
            <rect width="${2.5}" height="${2.5}"
                style="stroke:none; fill:#ffffff" />
            <path style="stroke:PATTERN_COLOR; stroke-width:${0.2}; stroke-linecap:butt; stroke-linejoin:miter; fill:none; opacity:PATTERN_OPACITY"
                  d="M 0,0 ${2.5},${0.625} 0,${1.25} ${2.5},${1.875} 0,${2.5}" />
        </g>
</pattern>
"""

INSULATION_SOFT_PATTERN_TEMPLATE = """
<pattern
    id="PATTERN_ID"
    patternUnits="userSpaceOnUse"
    x="0" y="0" width="${5}" height="${5}">
        <g>
            <rect width="${5}" height="${5}"
                style="stroke:none; fill:#ffffff" />
            <path style="stroke:PATTERN_COLOR; stroke-width:${0.2}; stroke-linecap:butt; stroke-linejoin:miter; fill:none; opacity:PATTERN_OPACITY"
                  d="M ${1.25},0 ${3.75},${1.25} ${1.25},${2.5} ${3.75},${3.75} ${1.25},${5} M ${1.25},0 C ${1.25},0 0,${0.5} 0,${1.25} 0,${2} ${1.25},${2.5} ${1.25},${2.5} m 0,0 C ${1.25},${2.5} 0,${3} 0,${3.75} 0,${4.5} ${1.25},${5} ${1.25},${5} M ${3.75},${3.75} c 0,0 ${1.25},${0.5} ${1.25},${1.25} C ${5},${5.75} ${3.75},${6.25} ${3.75},${6.25} m 0,-${7.5} c 0,0 ${1.25},${0.5} ${1.25},${1.25} 0,${0.75} -${1.25},${1.25} -${1.25},${1.25} m 0,0 c 0,0 ${1.25},${0.5} ${1.25},${1.25} 0,${0.75} -${1.25},${1.25} -${1.25},${1.25}" />
        </g>
</pattern>
"""

WINDOW_PATTERN_TEMPLATE = """
<pattern
    id="PATTERN_ID"
    patternUnits="userSpaceOnUse"
    x="0" y="0" width="${2.5}" height="${2.5}">
        <g>
            <rect width="${2.5}" height="${2.5}"
                style="stroke:none; fill:#ffffff" />
            <path style="stroke:PATTERN_COLOR; stroke-width:${0.2}; stroke-linecap:butt; stroke-linejoin:miter; fill:none; opacity:PATTERN_OPACITY"
                  d="M 0,${2.5} ${2.5},0 M 0,0 ${2.5},${2.5}" />
        </g>
</pattern>
"""

PATH_TEMPLATE = """
<path d="PATH_DATA" stroke="STROKE_COLOR" stroke-width="STROKE_WIDTH"
      style="fill:PATH_FILL; fill-rule: evenodd; stroke-width:STROKE_WIDTH; stroke-miterlimit:1; stroke-linejoin:round; stroke-dasharray:DASH_ARRAY; fill-opacity:FILL_OPACITY"/>
"""

SECTION_CUT_TEMPLATE = """
<g stroke-width="STROKE_WIDTH"
   style="stroke-width:STROKE_WIDTH; stroke-miterlimit:1; stroke-linejoin:round; stroke-dasharray:105,26,13,26;stroke-dashoffset:0;"
   stroke="#000000">
    <path d="PATH_DATA" />
    <path d="ARROW_START" transform="rotate(ARROW_START_ROTATION)"/>
    <path d="ARROW_END" transform="rotate(ARROW_END_ROTATION)"/>
    TEXT_START
    TEXT_END
</g>
"""

TEXT_TEMPLATE = """
<text
        x="TEXT_POSITION_X"
        y="TEXT_POSITION_Y"
        style="font-size:TEXT_FONT_SIZE;font-family:Arial;letter-spacing:0px;word-spacing:0px;fill:#000000;text-anchor:TEXT_ANCHOR;text-align:center;stroke:none;"
        transform="rotate(TEXT_ROTATION)">
            TEXT_CONTENT
</text>
"""

PATTERN_TEMPLATES = {
    "DEFAULT": DEFAULT_PATTERN_TEMPLATE,
    "WOOD": WOOD_PATTERN_TEMPLATE,
    "INSULATION_HARD": INSULATION_HARD_PATTERN_TEMPLATE,
    "INSULATION_SOFT": INSULATION_SOFT_PATTERN_TEMPLATE,
    "WINDOW": WINDOW_PATTERN_TEMPLATE
}

PATTERN_NUMBER_REGEX = re.compile(r'\$\{([0-9\.]+)\}')

def scalePatterns(patternSVG, scale):
    availableNumbers = set([(n, float(n))
                            for n in PATTERN_NUMBER_REGEX.findall(patternSVG)])

    for text, n in availableNumbers:
        patternSVG = patternSVG.replace(
            '${' + text + '}', toNumberString(n / scale, 6))

    return patternSVG

def toNumberString(val, precision=None):
    if precision is None:
        precision = DraftVecUtils.precision()

    rounded = round(val, precision)

    if precision == 0:
        rounded = int(rounded)

    return str(rounded)

def getProj(vec, plane):
    if not plane:
        return vec

    return plane.getLocalCoords(vec)

def getPatternType(o):
    if not hasattr(o, "Material") or not o.Material:
        return None

    mat = o.Material.Material

    if not "PatternType" in mat:
        return None

    return mat["PatternType"]

def isEdgeOnPlane(edge, plane):
    precision = DraftVecUtils.precision()
    planeBase = plane.CenterOfMass
    planeNormal = plane.normalAt(0.5, 0.5)

    points = [v.Point for v in edge.Vertexes]

    for p in points:
        distance = p.distanceToPlane(planeBase, planeNormal)

        if distance < 0:
            distance = distance * -1

        if distance > precision:
            return False

    return True

class BoundBox():
    def __init__(self, plane):
        self.initialized = False
        self.plane = plane

        self.minx = 0
        self.miny = 0
        self.maxx = 0
        self.maxy = 0

    def calculateOffset(self, scale, width, height):
        scaledWidth = width / scale
        scaledHeight = height / scale

        # So we are in the top left corner of the viewport
        x = self.minx
        y = -self.maxy

        x -= (scaledWidth - self.overallWidth()) / 2
        y -= (scaledHeight - self.overallHeight()) / 2

        return (scaledWidth, scaledHeight, x, y)

    def buildViewbox(self, scale, width, height):
        scaledWidth, scaledHeight, x, y = self.calculateOffset(
            scale, width, height)

        return '%s %s %s %s' % (toNumberString(x), toNumberString(y), toNumberString(scaledWidth), toNumberString(scaledHeight))

    def adaptFromShapes(self, objects):
        for s in objects:
            bb = s.BoundBox

            self.update(bb.XMin, bb.YMin, bb.XMax, bb.YMax)

    def adaptFromDrafts(self, objects):
        for o in objects:
            objectType = Draft.getType(o)
            if objectType == "Dimension":
                start = o.ViewObject.Proxy.p2
                end = o.ViewObject.Proxy.p3
                start = getProj(start, self.plane)
                end = getProj(end, self.plane)

                minx = min(start.x, end.x)
                maxx = max(start.x, end.x)
                miny = min(start.y, end.y)
                maxy = max(start.y, end.y)

                self.update(minx, miny, maxx, maxy)
            else:
                print("Unkown object type " + objectType)

    def update(self, minx, miny, maxx, maxy):
        if not self.initialized:
            self.minx = minx
            self.miny = miny
            self.maxx = maxx
            self.maxy = maxy

            self.initialized = True

            return

        if minx < self.minx:
            self.minx = minx
        if miny < self.miny:
            self.miny = miny
        if maxx > self.maxx:
            self.maxx = maxx
        if maxy > self.maxy:
            self.maxy = maxy

    def overallWidth(self):
        return self.maxx - self.minx

    def overallHeight(self):
        return self.maxy - self.miny

class FaceData:
    def __init__(self, originalFace, color, pattern_type, reorientedFace=None):
        self.originalFace = originalFace
        self.color = color
        self.pattern_type = pattern_type
        self.reorientedFace = reorientedFace
        self.points = None

    def matches(self, otherFace):
        selfPoints = self.getPoints()
        otherPoints = otherFace.getPoints()

        if not selfPoints or not otherPoints:
            return False

        if len(selfPoints) != len(otherPoints):
            return False

        # Find the first point, that is not in the other Faces points.
        # When no point is missing, the faces can be considered as equals
        for p in selfPoints:
            if not p in otherPoints:
                return False

        return True

    def getPoints(self):
        if self.points:
            return self.points

        if not self.reorientedFace:
            return None

        self.points = [(round(v.Point.x, 5), round(v.Point.y, 5))
                        for v in self.reorientedFace.Vertexes]

        return self.points

    def correctlyOriented(self, planeNormal):
        if not self.originalFace:
            return True

        faceNormal = self.originalFace.normalAt(0, 0)
        angle = math.degrees(faceNormal.getAngle(planeNormal))
        angle = round(angle, DraftVecUtils.precision())

        return angle != 90

class SectionCutData:
    def __init__(self, face, text):
        self.face = face
        self.text = text

class MarkerData:
    def __init__(self, face, text, color):
        self.face = face
        self.text = text
        self.color = color

class CutResult:
    def __init__(self, objectShapes, sections, faces, cutvolume, cutface):
        self.objectShapes = objectShapes
        self.sections = sections
        self.faces = faces
        self.cutvolume = cutvolume
        self.cutface = cutface

def indexOfFace(faceList, face):
    if not faceList:
        return None

    for i, f in enumerate(faceList):
        if face.matches(f):
            return i

    return None

class Renderer:
    def __init__(self, placement):
        import WorkingPlane

        self.reset()
        self.wp = WorkingPlane.plane()
        self.wp.setFromPlacement(placement, rebase=True)

        if DEBUG:
            print("Renderer initialized on %s. %s, %s" %
                  (self.wp, self.wp.getPlacement().Base, self.wp.getNormal()))

    def reset(self):
        "removes all faces from this renderer"
        self.objectShapes = []
        self.windowShapes = []
        self.sectionCutShapes = []
        self.markerShapes = []
        self.resetFlags()

    def resetFlags(self):
        "resets all flags of this renderer"
        self.duplicatesRemoved = False
        self.sorted = False
        self.iscut = False
        self.secondaryFaces = []
        self.sections = []
        self.windows = []
        self.hiddenEdges = []
        self.sectionCuts = []

    def addObjects(self, objs):
        "add objects to this renderer"

        for o in objs:
            if o.isDerivedFrom("Part::Feature"):
                color = o.ViewObject.ShapeColor
                if o.Shape.Faces:
                    self.objectShapes.append(
                        [o.Shape, color, getPatternType(o)])

        self.resetFlags()

    def addWindows(self, objs):
        "add objects to this renderer"

        for o in objs:
            if o.isDerivedFrom("Part::Feature"):
                color = o.ViewObject.ShapeColor
                if o.Shape.Faces:
                    self.windowShapes.append(
                        [o.Shape, color, getPatternType(o)])

        self.resetFlags()

    def addSectionCuts(self, sections):
        "add objects to this renderer"

        for s in sections:
            sectionCutCutPlane = s.Proxy.calculateCutPlane(s)
            data = SectionCutData(sectionCutCutPlane, s.Label)

            self.sectionCutShapes.append(data)

        self.resetFlags()

    def addMarkers(self, markers):
        "add objects to this renderer"

        for m in markers:
            color = m.ViewObject.LineColor
            face = m.Shape.Faces[0]
            self.markerShapes.append(MarkerData(face, m.Label, color))

        self.resetFlags()

    def removeDuplicates(self):
        if not self.secondaryFaces:
            return

        newSecondaryFaces = []

        for face in self.secondaryFaces:
            # If the face is already in a section, do not add it to the secondary faces
            if not face or indexOfFace(self.sections, face) is not None:
                continue

            i = indexOfFace(newSecondaryFaces, face)

            # if the face already exists, remove it from the list
            # Adding the new face again preserves the original face order
            if i is not None:
                del newSecondaryFaces[i]

            newSecondaryFaces.append(face)

        self.secondaryFaces = newSecondaryFaces

    def sort(self):
        normal = self.wp.getNormal()

        normalx = round(normal.x, 3)
        normaly = round(normal.y, 3)
        normalz = round(normal.z, 3)

        if self.secondaryFaces:
            self.sortFaces(self.secondaryFaces, normalx, normaly, normalz)
        if self.sections:
            self.sortFaces(self.sections, normalx, normaly, normalz)
        if self.windows:
            self.sortFaces(self.windows, normalx, normaly, normalz)
        if self.hiddenEdges:
            self.sortFaces(self.hiddenEdges, normalx, normaly, normalz)

    def sortFaces(self, faces, normalx, normaly, normalz):
        def sortX(entry):
            shape = entry.originalFace

            return shape.BoundBox.XMax

        def sortY(entry):
            shape = entry.originalFace

            return shape.BoundBox.YMax

        def sortZ(entry):
            shape = entry.originalFace

            return shape.BoundBox.ZMax

        if normalz > 0:
            faces.sort(key=sortZ)
        elif normalx > 0:
            faces.sort(key=sortX)
        elif normaly > 0:
            faces.sort(key=sortY)
        elif normalz < 0:
            faces.sort(key=sortZ, reverse=True)
        elif normalx < 0:
            faces.sort(key=sortX, reverse=True)
        elif normaly < 0:
            faces.sort(key=sortY, reverse=True)

    def projectFace(self, face):
        "projects a single face on the WP"

        wires = []
        if not face.originalFace.Wires:
            if DEBUG:
                print("Error: Unable to project face on the WP")
            return None
        norm = face.originalFace.normalAt(0, 0)
        for w in face.originalFace.Wires:
            verts = []
            edges = Part.__sortEdges__(w.Edges)
            for e in edges:
                v = e.Vertexes[0].Point
                v = self.wp.getLocalCoords(v)
                verts.append(v)
            verts.append(verts[0])
            if len(verts) > 2:
                wires.append(Part.makePolygon(verts))
        try:
            sh = ArchCommands.makeFace(wires)
        except:
            if DEBUG:
                print("Error: Unable to project face on the WP")
            return None
        else:
            # restoring flipped normals
            vnorm = self.wp.getLocalCoords(norm)
            if vnorm.getAngle(sh.normalAt(0, 0)) > 1:
                sh.reverse()

            face.reorientedFace = sh

            return face

    def projectEdge(self, edge):
        "projects a single edge on the WP"
        if len(edge.Vertexes) > 1:
            v1 = self.wp.getLocalCoords(edge.Vertexes[0].Point)
            v2 = self.wp.getLocalCoords(edge.Vertexes[-1].Point)
            return Part.LineSegment(v1, v2).toShape()
        return edge

    def doCut(self, cutplane, hidden, clip, clipDepth, shapes):
        objectShapes = []
        sections = []
        faces = []

        shps = []

        # self.reorient()
        # self.filterWrongOrientedFaces()

        for sh in shapes:
            shps.append(sh[0])

        cutface, cutvolume, invcutvolume = ArchCommands.getCutVolume(
            cutplane, shps, clip=clip)
        planeNormal = self.wp.getNormal()
        planeNormal.normalize()

        if not cutvolume:
            cutface = cutplane
            cutnormal = cutplane.normalAt(0.5, 0.5)
            cutvolume = cutplane.extrude(cutnormal)
            cutnormal = cutnormal.negative()
            invcutvolume = cutplane.extrude(cutnormal)

        if DEBUG:
            print('cutface: %s, cutvolume: %s, invcutvolume: %s' %
                  (cutface, cutvolume, invcutvolume))

        if cutface and cutvolume:
            for sh in shapes:
                for sol in sh[0].Solids:
                    c = sol.cut(cutvolume)
                    objectShapes.append([c]+sh[1:])

                    for f in c.Faces:
                        faceData = FaceData(f, sh[1], sh[2])
                        faceData = self.projectFace(faceData)
                        # TODO: Create temporary face list and filter duplicate faces
                        # Do isCoplanar check later on, when duplicate faces are removed

                        if faceData.correctlyOriented(planeNormal):
                            if DraftGeomUtils.isCoplanar([f, cutface]):
                                sections.append(faceData)
                            else:
                                faces.append(faceData)

                    if hidden:
                        c = sol.cut(invcutvolume)
                        # self.projectEdge(e)
                        self.hiddenEdges.extend(c.Edges)

        if clipDepth > 0:
            faces = [f for f in faces if self.isInRange(
                f.originalFace, clipDepth)]

        return CutResult(objectShapes, sections, faces, cutvolume, cutface)

    def doCutSectionCuts(self, cutvolume, cutface, sectionCutShapes):
        edges = []

        if cutvolume:
            for s in sectionCutShapes:
                sh = s.face
                c = sh.cut(cutvolume)
                normal = sh.normalAt(0.5, 0.5)

                for e in c.Edges:
                    if isEdgeOnPlane(e, cutface):
                        e = self.projectEdge(e)
                        edges.append((e, normal, s.text))

        return edges

    def cut(self, cutplane, hidden=False, clip=False, clipDepth=0):
        "Cuts through the objectShapes with a given cut plane and builds section faces"
        if DEBUG:
            print("\n\n======> Starting cut\n\n")

        if self.iscut:
            return

        objectCutVolume = None
        objectCutFace = None

        if not self.objectShapes:
            if DEBUG:
                print("No objects to make sections")
        else:
            # We always use a clipping cut here. The section plane needs to be big enough
            # But we need it clipping for the sectionCutShapes later on
            result = self.doCut(
                cutplane, hidden, True, clipDepth, self.objectShapes)

            self.objectShapes = result.objectShapes
            self.sections = result.sections
            self.secondaryFaces = result.faces
            objectCutVolume = result.cutvolume
            objectCutFace = result.cutface

            if DEBUG:
                print("Built ", len(self.sections), " sections")

        if not self.windowShapes:
            if DEBUG:
                print("No objects to make windows")
        else:
            result = self.doCut(
                cutplane, hidden, clip, clipDepth, self.windowShapes)

            self.windowShapes = result.objectShapes
            self.windows = result.sections

            if DEBUG:
                print("Built ", len(self.windows), " windows")

        if not self.sectionCutShapes:
            if DEBUG:
                print("No objects to make sectionCuts")
        else:
            self.sectionCuts = self.doCutSectionCuts(
                objectCutVolume, objectCutFace, self.sectionCutShapes)

            if DEBUG:
                print("Built ", len(self.sectionCuts), " sectionCuts")

        self.sort()

        self.iscut = True
        self.sorted = True
        self.duplicatesRemoved = False

        if DEBUG:
            print("\n\n======> Finished cut\n\n")

    def getFill(self, fill):
        "Returns a SVG fill value"
        r = str(hex(int(fill[0]*255)))[2:].zfill(2)
        g = str(hex(int(fill[1]*255)))[2:].zfill(2)
        b = str(hex(int(fill[2]*255)))[2:].zfill(2)

        return "#" + r + g + b

    def getPatternTemplate(self, fill, opacity, pattern_type):
        if pattern_type is None:
            pattern_type = "DEFAULT"

        if not pattern_type in PATTERN_TEMPLATES:
            print("Unknown PatternType " + pattern_type)
            pattern_type = "DEFAULT"

        pattern_id = "%s-%s-%s" % (pattern_type.lower(),
                                   fill.replace("#", ""), str(opacity))

        return (PATTERN_TEMPLATES[pattern_type], pattern_id)

    def getPattern(self, color, pattern_type, opacity=1):
        fill = self.getFill(color)
        pattern, pattern_id = self.getPatternTemplate(
            fill, opacity, pattern_type)

        if not pattern_id in self.patterns:
            pattern = pattern.replace("PATTERN_ID", pattern_id)
            pattern = pattern.replace("PATTERN_COLOR", fill)
            pattern = pattern.replace("PATTERN_OPACITY", str(opacity))

            self.patterns[pattern_id] = pattern

        return pattern_id

    def getPathData(self, w):
        """Returns a SVG path data string from a 2D wire
        The Y Axis in the SVG Coordinate system is reversed from the FreeCAD Coordinate System.
        So we change the y coordinates accordingly
        """
        def toCommand(command, x, y):
            return '%s %s %s ' % (command, toNumberString(x), toNumberString(-y))

        edges = Part.__sortEdges__(w.Edges)

        v = edges[0].Vertexes[0].Point
        svg = toCommand('M', v.x, v.y)

        for e in edges:
            if (DraftGeomUtils.geomType(e) == "Line") or (DraftGeomUtils.geomType(e) == "BSplineCurve"):
                v = e.Vertexes[-1].Point
                svg += toCommand('L', v.x, v.y)
            elif DraftGeomUtils.geomType(e) == "Circle":
                r = e.Curve.Radius
                v = e.Vertexes[-1].Point

                svg += 'A %s %s 0 0 1 %s %s ' % (toNumberString(r),
                                                 toNumberString(r), toNumberString(v.x), toNumberString(-v.y))

        if len(edges) > 1:
            svg += 'Z '

        return svg

    def getPatternSVG(self):
        if not hasattr(self, "patterns"):
            return ''

        patternsvg = ''

        for pattern in self.patterns.values():
            patternsvg += pattern + '\n'

        return patternsvg

    def getSectionSVG(self, linewidth):
        sectionsvg = ''

        for f in self.sections:
            if f:
                fill = 'url(#' + self.getPattern(f.color, f.pattern_type) + ')'

                pathdata = ''

                for w in f.reorientedFace.Wires:
                    pathdata += self.getPathData(w)

                current = PATH_TEMPLATE.replace("PATH_FILL", fill)
                current = current.replace("FILL_OPACITY", "1")
                current = current.replace("DASH_ARRAY", "none")
                current = current.replace("STROKE_COLOR", "#000000")
                current = current.replace("STROKE_WIDTH", str(linewidth))
                current = current.replace("PATH_DATA", pathdata)

                sectionsvg += current + "\n"

        return sectionsvg

    def getMarkerSVG(self, linewidth):
        markersvg = ''

        for m in self.markerShapes:
            # fill = 'url(#' + self.getPattern(f.color, f.pattern_type) + ')'

            pathdata = ''
            fillColor = self.getFill(m.color)

            reorientedFace = self.projectFace(FaceData(m.face, None, None)).reorientedFace
            textPos = reorientedFace.CenterOfMass

            for w in reorientedFace.Wires:
                pathdata += self.getPathData(w)

            path = PATH_TEMPLATE.replace("PATH_FILL", fillColor)
            path = path.replace("FILL_OPACITY", "0.04")
            path = path.replace("DASH_ARRAY", "100,50")
            path = path.replace("STROKE_COLOR", fillColor)
            path = path.replace("STROKE_WIDTH", str(linewidth))
            path = path.replace("PATH_DATA", pathdata)

            text = TEXT_TEMPLATE.replace("TEXT_CONTENT", m.text)
            text = text.replace("TEXT_FONT_SIZE", "SMALL_TEXT_FONT_SIZE")
            text = text.replace("TEXT_ANCHOR", "middle")
            text = text.replace("TEXT_POSITION_X", toNumberString(textPos.x))
            text = text.replace("TEXT_POSITION_Y", toNumberString(-textPos.y))
            text = text.replace("TEXT_ROTATION", "0")

            markersvg += "%s %s\n" % (path, text)

        return markersvg

    def getSectionCutSvg(self, linewidth):
        svg = ''
        arrowSize = 100
        referenceAxis = FreeCAD.Vector(0, -1, 0)

        def rotation(x, y, angle):
            angleString = toNumberString(angle)
            xString = toNumberString(x)
            yString = toNumberString(y)

            return '%s %s %s' % (angleString, xString, yString)

        def arrowPath(basePoint):
            baseX = basePoint.x
            baseY = -basePoint.y
            baseXString = toNumberString(baseX)
            baseYString = toNumberString(baseY)

            return "M %s %s L %s %s L %s %s Z" % (toNumberString(baseX - arrowSize), baseYString, toNumberString(baseX + arrowSize), baseYString, baseXString, toNumberString(baseY + arrowSize))

        def text(basePoint, normal, angle, text):
            offset = FreeCAD.Vector(normal.x, normal.y, normal.z).multiply(100)
            actualBase = FreeCAD.Vector(basePoint.x, basePoint.y, basePoint.z).sub(offset)
            baseX = actualBase.x
            baseY = -actualBase.y
            baseXString = toNumberString(baseX)
            baseYString = toNumberString(baseY)

            svg = TEXT_TEMPLATE.replace("TEXT_POSITION_X", baseXString)
            text = text.replace("TEXT_ANCHOR", "middle")
            svg = svg.replace("TEXT_POSITION_Y", baseYString)
            svg = svg.replace("TEXT_CONTENT", text)
            svg = svg.replace("TEXT_ROTATION", rotation(baseX, baseY, angle))

            return svg

        for s in self.sectionCuts:
            edge = s[0]
            normal = s[1]
            normal = normal.negative()
            label = s[2]

            pathdata = self.getPathData(edge)
            rotationAngle = math.degrees(normal.getAngle(referenceAxis))
            start = edge.Vertexes[0].Point
            end = edge.Vertexes[1].Point

            arrowStart = arrowPath(start)
            arrowEnd = arrowPath(end)

            textStart = text(start, normal, rotationAngle, label)
            textEnd = text(end, normal, rotationAngle, label)

            current = SECTION_CUT_TEMPLATE.replace("PATH_DATA", pathdata)
            current = current.replace("STROKE_WIDTH", str(linewidth))
            current = current.replace(
                "ARROW_START_ROTATION", rotation(start.x, -start.y, rotationAngle))
            current = current.replace(
                "ARROW_END_ROTATION", rotation(end.x, -end.y, rotationAngle))
            current = current.replace("ARROW_START", arrowStart)
            current = current.replace("ARROW_END", arrowEnd)
            current = current.replace("TEXT_START", textStart)
            current = current.replace("TEXT_END", textEnd)

            svg += current + "\n"

        return svg

    def getWindowSVG(self, linewidth):
        windowsvg = ''

        for f in self.windows:
            if f:
                fill = 'url(#' + self.getPattern(f.color, f.pattern_type) + ')'

                pathdata = ''

                for w in f.reorientedFace.Wires:
                    pathdata += self.getPathData(w)

                current = PATH_TEMPLATE.replace("PATH_FILL", fill)
                current = current.replace("FILL_OPACITY", "1")
                current = current.replace("DASH_ARRAY", "none")
                current = current.replace("STROKE_COLOR", "#000000")
                current = current.replace("STROKE_WIDTH", str(linewidth))
                current = current.replace("PATH_DATA", pathdata)

                windowsvg += current + "\n"

        return windowsvg

    def isInRange(self, face, maxDistance):
        if maxDistance <= 0:
            return False

        distance = face.CenterOfMass.distanceToPlane(
            self.wp.getPlacement().Base, self.wp.getNormal())

        if distance < 0:
            distance *= -1

        if distance <= maxDistance:
            return True

        return False

    def getSecondaryFacesSVG(self, linewidth, faceHighlightDistance, highlightLineWith):
        secondaryFacesSvg = ''

        for f in self.secondaryFaces:
            if f:
                patternOpacity = 0.1
                shouldHightlight = self.isInRange(
                    f.originalFace, faceHighlightDistance)

                if shouldHightlight:
                    linewidth = highlightLineWith
                    patternOpacity = 1

                fill = 'url(#' + self.getPattern(f.color,
                                                 f.pattern_type, patternOpacity) + ')'

                pathdata = ''

                for w in f.reorientedFace.Wires:
                    pathdata += self.getPathData(w)

                current = PATH_TEMPLATE.replace("PATH_FILL", fill)
                current = current.replace("FILL_OPACITY", "1")
                current = current.replace("DASH_ARRAY", "none")
                current = current.replace("STROKE_COLOR", "#000000")
                current = current.replace("STROKE_WIDTH", str(linewidth))
                current = current.replace("PATH_DATA", pathdata)

                secondaryFacesSvg += current + "\n"

        return secondaryFacesSvg

    def getSvgParts(self, faceHighlightDistance=0):
        "Returns all svg parts we cut"
        if not self.duplicatesRemoved:
            self.removeDuplicates()

            self.duplicatesRemoved = True

        self.patterns = {}

        sectionSvg = self.getSectionSVG("SECTION_STROKE_WIDTH")
        windowSvg = self.getWindowSVG("WINDOW_STROKE_WIDTH")
        secondaryFacesSvg = self.getSecondaryFacesSVG(
            "SECONDARY_STROKE_WIDTH", faceHighlightDistance, "SECTION_STROKE_WIDTH")
        patternSvg = self.getPatternSVG()
        sectionCutSvg = self.getSectionCutSvg("SECTION_CUT_STROKE_WIDTH")
        markerSvg = self.getMarkerSVG("MARKER_STROKE_WIDTH")
        boundBox = self.buildBoundBox()

        return {
            "patterns": patternSvg,
            "sections": sectionSvg,
            "secondaryFaces": secondaryFacesSvg,
            "windows": windowSvg,
            "boundBox": boundBox,
            "sectionCuts": sectionCutSvg,
            "markers": markerSvg
        }

    def buildBoundBox(self):
        boundBox = BoundBox(self.wp)

        if self.secondaryFaces:
            boundBox.adaptFromShapes(
                [f.reorientedFace for f in self.secondaryFaces if f])
        if self.sections:
            boundBox.adaptFromShapes(
                [f.reorientedFace for f in self.sections if f])
        if self.windows:
            boundBox.adaptFromShapes(
                [f.reorientedFace for f in self.windows if f])
        if self.hiddenEdges:
            boundBox.adaptFromShapes(
                [f.reorientedFace for f in self.hiddenEdges if f])
        if self.sectionCuts:
            boundBox.adaptFromShapes([s[0] for s in self.sectionCuts])

        return boundBox

    # def getHiddenSVG(self, linewidth=0.02):
    #     "Returns a SVG fragment from cut geometry"
    #     if DEBUG:
    #         print("Printing ", len(self.sections), " hidden faces")
    #     if not self.oriented:
    #         self.reorient()
    #     svg = '<g stroke="#000000" stroke-width="' + \
    #         str(linewidth) + '" style="stroke-width:' + str(linewidth)
    #     svg += ';stroke-miterlimit:1;stroke-linejoin:round;stroke-dasharray:0.09,0.05;fill:none;">\n'
    #     for e in self.hiddenEdges:
    #         svg += '<path '
    #         svg += 'd="'
    #         svg += self.getPathData(e)
    #         svg += '"/>\n'
    #     svg += '</g>\n'
    #     return svg

if __name__ == "__main__":
    import time
    import tracemalloc

    def calculateCutPlane(pl, l=18889, h=14706):
        import Part

        p = Part.makePlane(l, h, FreeCAD.Vector(
            l/2, -h/2, 0), FreeCAD.Vector(0, 0, -1))

        # make sure the normal direction is pointing outwards, you never know what OCC will decide...
        if p.normalAt(0, 0).getAngle(pl.Rotation.multVec(FreeCAD.Vector(0, 0, 1))) > 1:
            p.reverse()

        p.Placement = pl

        # Part.show(p)

        return p

    # Top
    # pl = FreeCAD.Placement(
    #     FreeCAD.Vector(9440, 7350, 4450), FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0))
    # Front
    # pl = FreeCAD.Placement(
    #     FreeCAD.Vector(0, -1000, 0), FreeCAD.Rotation(FreeCAD.Vector(1, 0, 0), 90))
    # Right
    # pl = FreeCAD.Placement(
    #     FreeCAD.Vector(16000, 0, 0), FreeCAD.Rotation(FreeCAD.Vector(0.577, 0.577, 0.577), 120))
    # Back
    # pl = FreeCAD.Placement(
    #     FreeCAD.Vector(0, 15000, 0), FreeCAD.Rotation(FreeCAD.Vector(0, -0.71, -0.71), 180))
    # custom
    pl = FreeCAD.Placement(
        FreeCAD.Vector(9280, 7348, 1702), FreeCAD.Rotation(FreeCAD.Vector(0.577, 0.577, 0.577), 120))
    # print(pl)

    cutplane = calculateCutPlane(pl, 14706, 10915)

    # Right
    # opl = FreeCAD.Placement(
    #     FreeCAD.Vector(1000, 0, 0), FreeCAD.Rotation(FreeCAD.Vector(0.577, 0.577, 0.577), 120))
    # otherPlane = calculateCutPlane(opl)

    DEBUG = True

    render = Renderer(pl)
    render.addObjects([FreeCAD.ActiveDocument.Roof001])
    # render.addObjects([FreeCAD.ActiveDocument.Structure108])
    # render.addObjects([FreeCAD.ActiveDocument.Box,
    #                    FreeCAD.ActiveDocument.Wall003])
    # render.addObjects(FreeCAD.ActiveDocument.BuildingPart001.Group)
    # render.addObjects(FreeCAD.ActiveDocument.BuildingPart002.Group)

    # render.addSectionCuts([FreeCAD.ActiveDocument.SectionPlane026, FreeCAD.ActiveDocument.SectionPlane027])
    # render.addMarkers([FreeCAD.ActiveDocument.Rectangle033])

    tracemalloc.start()
    startTime = time.time()

    render.cut(cutplane, clip=True)
    parts = render.getSvgParts(0)

    endTime = time.time()
    top_stats = tracemalloc.take_snapshot().statistics("lineno")

    print("Needed %s s, %s MB" %(endTime - startTime, tracemalloc.get_traced_memory()[0] / 1024 / 1024))
    for stat in top_stats[:10]:
        print(stat)

    tracemalloc.stop()

    boundBox = parts["boundBox"]
    # boundBox.adaptFromDrafts([FreeCAD.ActiveDocument.Dimension005])

    width = 420
    height = 297

    # print("---patterns---")
    # print(parts["patterns"])
    # print("---sections---")
    # print(parts["sections"])
    # print("---windows---")
    # print(parts["windows"])
    # print("---secondaryFaces---")
    # print(parts["secondaryFaces"])

    template = """
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
     xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
     width="WIDTHmm" height="HEIGHTmm" viewBox="VIEWBOX_VALUES"
     version="1.1">
    <sodipodi:namedview
        id="base"
        pagecolor="#ffffff"
        bordercolor="#666666"
        borderopacity="1.0"
        inkscape:pageopacity="1"
        inkscape:pageshadow="2"
        inkscape:document-units="mm"
        inkscape:window-maximized="1" />
    <g
        inkscape:label="Layer 1"
        inkscape:groupmode="layer"
        id="layer1">

        <g id="everything">
            <g id="patterns">
                PATTERN_SVG
            </g>

            <g id="secondary">
                SECONDARY_SVG
            </g>

            <g id="sections">
                SECTION_SVG
            </g>

            <g id="windows">
                WINDOW_SVG
            </g>

            <g id="drafts">
                DRAFT_SVG
            </g>

            <g id="section_cuts">
                SECTION_CUT_SVG
            </g>

            <g id="markers">
                MARKER_SVG
            </g>

            <g id="information">
                INFORMATION_SVG
            </g>
        </g>
    </g>
</svg>
"""
    scale = 1/50

    template = template.replace(
        "VIEWBOX_VALUES", boundBox.buildViewbox(scale, width, height))
    template = template.replace("WIDTH", toNumberString(width))
    template = template.replace("HEIGHT", toNumberString(height))
    template = template.replace(
        "PATTERN_SVG", scalePatterns(parts["patterns"], scale))
    template = template.replace("SECONDARY_SVG", parts["secondaryFaces"])
    template = template.replace("SECTION_SVG", parts["sections"])
    template = template.replace("WINDOW_SVG", parts["windows"])
    template = template.replace("SECTION_CUT_SVG", parts["sectionCuts"])
    template = template.replace("MARKER_SVG", parts["markers"])
    template = template.replace("SMALL_TEXT_FONT_SIZE", str(3 / scale))
    template = template.replace("TEXT_FONT_SIZE", str(240))
    template = template.replace("SECTION_STROKE_WIDTH", str(3))
    template = template.replace("MARKER_STROKE_WIDTH", str(0.12 / scale))
    template = template.replace("WINDOW_STROKE_WIDTH", str(1))
    template = template.replace("SECONDARY_STROKE_WIDTH", str(1))
    template = template.replace("SECTION_CUT_STROKE_WIDTH", str(0.05 / scale))

    file_object = open(
        "C:\\Meine Daten\\freecad\\samples\\SectionPlane\\Export.svg", "w")
    file_object.write(template)
    file_object.close()


#Micobit only (Use mu editor or another IDE which supports Micropython)
from microbit import *
import radio

radio.on()

my_channel = 10
radio.config(channel=my_channel)

state = 'idle'

while True:
    if state == 'idle':
        if accelerometer.was_gesture("3g"):
            radio.send('online!')
            state = 'online!'
    if state == 'online!':
        message = radio.receive()
        if message:
            display.scroll(message)
            sleep(100)
            radio.send('message received!')
            state = 'idle'


from .module_types import (
    single,
    integer,
    idx,
    dword,
    word,
    byte,
    sized_ascii_z,
    float,
    qword,
    Computed,
)
from construct import Struct, this, Probe, Int16sl

color = Struct("R" / byte, "G" / byte, "B" / byte, "A" / byte)

vector = Struct("x" / single, "y" / single, "z" / single)

pointregion = Struct("zone" / idx, "ileaf" / integer, "zonenumber" / byte)

rotator = Struct("yaw" / integer, "roll" / integer, "pitch" / integer)

scale = Struct(
    "x" / single, "y" / single, "z" / single, "sheerrate" / single, "sheeraxis" / byte
)

plane = Struct(*vector.subcons, "w" / single)

sphere = Struct(*plane.subcons)

quat = Struct("x" / single, "y" / single, "z" / single, "w" / single)

polygon = Struct(
    "vertex_array_size" / idx,
    "base" / vector,
    "normal" / vector,
    "texture_u" / vector,
    "texture_v" / vector,
    "vertex" / vector[this.vertex_array_size],
    "poly_flags" / dword,
    "actor" / idx,
    "texture" / idx,
    "item_name" / idx,
    "i_link" / idx,
    "i_brush_poly" / idx,
    "pan_u" / Int16sl,
    "pan_v" / Int16sl,
)

url = Struct(
    "protocol" / sized_ascii_z,
    "host" / sized_ascii_z,
    "map" / sized_ascii_z,
    "size" / idx,
    "options" / sized_ascii_z[this.size],
    "portal" / sized_ascii_z,
    "port" / dword,
    "valid" / dword,
)

named_bone = Struct("name_idx" / idx, "flags" / dword, "parent_index" / dword,)

analog_track = Struct(
    "flags" / dword,
    "num_key_quats" / idx,
    "key_quats" / quat[this.num_key_quats],
    "num_key_pos" / idx,
    "key_pos" / vector,
    "num_key_times" / idx,
    "key_times" / single[this.num_key_times],
)

motion_chunk = Struct(
    "root_speed_vector" / vector,
    "track_time" / single,
    "start_bone" / dword,
    "flags" / dword,
    "num_indeces" / idx,
    "bone_indeces" / dword[this.num_indeces],
    "num_anims" / idx,
    "anim_tracks" / analog_track[this.num_anims],
    "root_track" / analog_track,
)

bounding_box = Struct("min" / vector, "max" / vector, "valid" / byte)

bounding_sphere = Struct(
    "center" / vector, "radius" / float  # only valid if version > 61
)

bsp_node = Struct(
    "plane" / plane,
    "zone_mask" / qword,
    "node_flags" / byte,
    "vert_pool_index" / idx,
    "suface_index" / idx,
    "front_index" / idx,
    "back_index" / idx,
    "plane_index" / idx,
    "collision_bound_index" / idx,
    "render_bound_index" / idx,
    "zone_index" / byte[2],
    "num_vertices" / byte,
    "leaf_index" / dword[2],
)

bsp_surface = Struct(
    "texture_index" / idx,
    "poly_flags" / dword,
    "p_base" / idx,
    "v_normal" / idx,
    "v_texture_u" / idx,
    "v_texture_v" / idx,
    "lightmap_index" / idx,
    "brush_poly_index" / idx,
    "pan_u" / word,
    "pan_v" / word,
    "actor" / idx,
)

f_vertex = Struct("p_vertex" / idx, "side_index" / idx)

mesh_vertex = Struct(
    # if game is not Deus Ex, these will be sized differently
    "x" / Int16sl,
    "y" / Int16sl,
    "z" / Int16sl,
    "trash" / Int16sl,
)

triangle = Struct(
    "vertex1" / word,
    "vertex2" / word,
    "vertex3" / word,
    "u1" / byte,
    "v1" / byte,
    "u2" / byte,
    "v2" / byte,
    "u3" / byte,
    "v3" / byte,
    "flags" / dword,
    "texture_index" / dword,
)

anim_seq_notify = Struct("time" / float, "function_index" / idx)

anim_seq = Struct(
    "name_index" / idx,
    "group_index" / idx,
    "startframe" / dword,
    "numframes" / dword,
    "num_notifys" / idx,
    "notifys" / anim_seq_notify[this.num_notifys],
    "rate" / float,
)

connect = Struct("num_vert_tiangles" / dword, "triangle_list_offset" / dword)

struct_texture = Struct("index" / idx)

face = Struct(
    "wedge_index_1" / word,
    "wedge_index_2" / word,
    "wedge_index_3" / word,
    "material_index" / word,
)

material = Struct("flags" / dword, "texture_index" / dword,)

wedge = Struct("vertex_index" / word, "u" / byte, "v" / byte,)

zone = Struct("actor" / idx, "connectivity" / qword, "visibility" / qword,)

lightmap = Struct(
    "data_offset" / dword,
    "pan" / vector,
    "u_clamp" / idx,
    "v_clamp" / idx,
    "u_scale" / float,
    "v_scale" / float,
    "light_actors_index" / dword,
)

leaf = Struct(
    "zone_index" / idx,
    "permeating_index" / idx,
    "volumetric_index" / idx,
    "visible_zones" / qword,
)

struct_map = {
    "color": color,
    "vector": vector,
    "pointregion": pointregion,
    "rotator": rotator,
    "scale": scale,
    "plane": plane,
    "sphere": sphere,
}


#!/bin/python3
"""
We define a magic square to be an n x n matrix of distinct positive integers from 1 to n^2
where the sum of any row, column, or diagonal of length n is always equal to the same number:
the magic constant.

You will be given a 3 x 3 matrix s of integers in the inclusive range [1, 9]. We can convert
any digit a to any other digit b in the range [1, 9] at cost of |a - b|. Given s, convert it
into a magic square at minimal cost. Print this cost on a new line.
Note: The resulting magic square must contain distinct integers in the inclusive range [1, 9].

Complete the formingMagicSquare function in the editor below.
It should return an integer representing the minimal total cost of converting the input square
to a magic square.
formingMagicSquare has the following parameter(s):
    int s[3][3]: a 3 x 3 array of integers
"""
# import os

# def formingMagicSquare(s):
#     cost = 0
#     return cost
#
#
# if __name__ == '__main__':
#     fptr = open(os.environ['OUTPUT_PATH'], 'w')
#     s = []
#     for _ in range(3):
#         s.append(list(map(int, input().rstrip().split())))
#     result = formingMagicSquare(s)
#     fptr.write(str(result) + '\n')
#     fptr.close()

L = [
    [5, 3, 4],
    [1, 5, 8],
    [6, 4, 2]
]
n = 3
i = 0
items_count = [0]*9
for j in range(n):
    print(L[i][j], end=" ")
    if j == n-1:
        i += 1
        while i < n:
            print(L[i][j], end=" "),
            i += 1
        i -= 1
        if i == n-1:
            j -= 1
            while j > -1:
                print(L[i][j], end=" "),
                j -= 1
        j = 0
        i -= 1
        while i > -1:
            print(L[i][j], end=" "),
            i -= 1


import copy
import csv
import os
import pickle
import librosa
import numpy as np
from scipy import signal
import torch
from PIL import Image
from torch.utils.data import Dataset
from torchvision import transforms
import pdb, cv2
import random

import models.vggish_src.mel_features as mel_features
import models.vggish_src.vggish_params as vggish_params
import numpy as np
import resampy
import soundfile as sf

from sklearn.model_selection import train_test_split
from glob import glob
import pandas as pd

import albumentations as A
from albumentations.pytorch import ToTensorV2
import audiomentations as Au

def process_annotation(Annotation):
    with open(Annotation) as f:
        lines = f.readlines()
    print("totally {} lines".format(len(lines)))

    anno_dict = {}

    for i in lines[1:]:
        i = i.split('&')
        anno_dict[i[1]] = i[0]

    label_list = set(i for i in anno_dict.values())
    labels_double_ref = {}
    for i, label in enumerate(label_list):
        labels_double_ref[i] = label
        labels_double_ref[label] = i

    return anno_dict, labels_double_ref

class x360_Basic_Dataset(Dataset):

    def get_img_info(self, img_path, img, byshortmax=224, label=None):

        H, W, C = img.shape

        if W < H:
            ratio = byshortmax / W    #

        elif H <= W:
            ratio = byshortmax / W

        Wcrop = int(W * ratio)  # size
        Hcrop = int(H * ratio)

        if label == 'crop':
            Hcrop = byshortmax
            Wcrop = int(2 * byshortmax)

        rnd_h = random.randint(0, max(0, H - Hcrop))  # loc
        rnd_w = random.randint(0, max(0, W - Wcrop))

        info = {"H":H, "W":W,
                "Hcrop":Hcrop, "Wcrop":Wcrop,
                "rnd_h":rnd_h, "rnd_w":rnd_w}

        return info

    def crop_by_ratio(self, img, center=False, info=None):

        if center:
            crop = A.Compose([
                A.CenterCrop(height=info['Hcrop'], width=info['Wcrop'], always_apply=True),
            ])
            cropped = crop(image=img)['image']

        else:
            cropped = img[info['rnd_h']:info['rnd_h'] + info['Hcrop'],
                          info['rnd_w']:info['rnd_w'] + info['Wcrop']]

        return cropped

    def waveform_to_examples(self, data, sample_rate, return_tensor=True):
        """Converts audio waveform into an array of examples for VGGish.

    Args:
        data: np.array of either one dimension (mono) or two dimensions
        (multi-channel, with the outer dimension representing channels).
        Each sample is generally expected to lie in the range [-1.0, +1.0],
        although this is not required.
        sample_rate: Sample rate of data.
        return_tensor: Return data as a Pytorch tensor ready for VGGish

    Returns:
        3-D np.array of shape [num_examples, num_frames, num_bands] which represents
        a sequence of examples, each of which contains a patch of log mel
        spectrogram, covering num_frames frames of audio and num_bands mel frequency
        bands, where the frame length is vggish_params.STFT_HOP_LENGTH_SECONDS.

    """
        # Convert to mono.
        if len(data.shape) > 1:
            data = np.mean(data, axis=1)
        # Resample to the rate assumed by VGGish.
        if sample_rate != vggish_params.SAMPLE_RATE:
            data = resampy.resample(data, sample_rate, vggish_params.SAMPLE_RATE)

        # Compute log mel spectrogram features.
        log_mel = mel_features.log_mel_spectrogram(
            data,
            audio_sample_rate=vggish_params.SAMPLE_RATE,
            log_offset=vggish_params.LOG_OFFSET,
            window_length_secs=vggish_params.STFT_WINDOW_LENGTH_SECONDS,
            hop_length_secs=vggish_params.STFT_HOP_LENGTH_SECONDS,
            num_mel_bins=vggish_params.NUM_MEL_BINS,
            lower_edge_hertz=vggish_params.MEL_MIN_HZ,
            upper_edge_hertz=vggish_params.MEL_MAX_HZ)

        # Frame features into examples.
        features_sample_rate = 1.0 / vggish_params.STFT_HOP_LENGTH_SECONDS
        example_window_length = int(round(
            vggish_params.EXAMPLE_WINDOW_SECONDS * features_sample_rate))
        example_hop_length = int(round(
            vggish_params.EXAMPLE_HOP_SECONDS * features_sample_rate))
        log_mel_examples = mel_features.frame(
            log_mel,
            window_length=example_window_length,
            hop_length=example_hop_length)

        return log_mel_examples

    def handle_audio_sample(self, sample, rate):

        start_point = random.randint(a=0, b=np.max([0, len(sample) - rate]))

        sample = self.au_transform(samples=sample, sample_rate=rate)
        sample[sample > 1.] = 1.
        sample[sample < -1.] = -1.

        tuple_spec = []

        l = sample.shape[0] // self.tuple_len

        sample = np.concatenate([sample, sample])

        for i in range(self.tuple_len):
            new_sample = sample[start_point: start_point + l]

            start_point = start_point + l

            spectrogram = self.waveform_to_examples(new_sample, sample_rate=rate)[:5]

            if spectrogram.shape[0] < 5:
                s = spectrogram.shape[0]
                try:
                    ratio = (5 // s + 1) // 2 + 1
                except:
                    print("failed to get ratio:",
                          spectrogram.shape, new_sample.shape, sample.shape)

                for i in range(ratio):
                    spectrogram = np.concatenate([spectrogram, spectrogram], axis=0)

            spectrogram = self.spec_transform(spectrogram)

            tuple_spec.append(torch.from_numpy(spectrogram[:5]))

        return tuple_spec

    def read_audio(self, audio_path, sr_in):

        base_sr = 16000 // sr_in
        samples, sr = librosa.load(audio_path, offset=0.0, mono=False, sr=base_sr)  # sr=16000,   - > 220618,)

        left_spectrogram = self.handle_audio_sample(samples[0], sr)
        right_spectrogram = self.handle_audio_sample(samples[:, 1], sr)

        return [left_spectrogram, right_spectrogram, audio_path]

    def read_audio_tracking(self, at):
        # read in directional audio time delay
        return np.load(at) # [250]

    def read_frames(self, frame_folder, clip_sr=1, label=None):

        tuple_clip = []

        tuple_start = random.randint(0, 36)  # 64
        clip_start = tuple_start

        frame_list = glob(os.path.join(frame_folder, '*.jpg'))
        frame_list.sort()

        num_frames = len(frame_list)

        try:
            ratio = int(np.ceil(self.clip_len / len(frame_list))) + 1
        except:
            print("frame_folder:", frame_folder)

        cache_frame_list = frame_list.copy()
        for i in range(ratio):

            frame_list.extend(cache_frame_list)

        self.transforms = torch.nn.Sequential(
                transforms.RandomVerticalFlip(p= (1.0 if np.random.random() > 0.5 else 0) ),
                transforms.RandomHorizontalFlip(p=(1.0 if np.random.random() > 0.5 else 0))
            )

        idx = 0
        tuble_id_list = []

        for _ in range(self.tuple_len):
            clip_id_list = []

            for i in range(self.clip_len):
                # clip_sr is the pace of video pace, usually randomly sampled from 1~4

                if (clip_start + (idx + 1) * clip_sr) >= num_frames:
                    clip_start = (clip_start + (idx + 1)* clip_sr) - num_frames
                    idx = 0

                try:
                    img = cv2.imread(frame_list[clip_start + idx * clip_sr])
                    clip_id_list.append(clip_start + idx * clip_sr)

                except:
                    print(f"frame {clip_start + idx * clip_sr} of {num_frames} and len {len(frame_list)}")

                img = self.resize(image=img)["image"]

                if i == 0:
                    info = self.get_img_info(frame_list[i], img, byshortmax=self.final_img_size, label=label)
                    images = torch.zeros((self.clip_len, 3, info['Hcrop'], info['Wcrop']))

                img = self.crop_by_ratio(img, info=info,
                                         center=(self.mode == 'test'))

                img = self.transform(image=img)["image"]

                images[i] = img

                idx += 1

            images = images.permute(1, 0, 2, 3)

            images = self.transforms(images)

            tuple_clip.append(images)
            tuble_id_list.append(clip_id_list)

            clip_start = clip_start + self.clip_len

        return tuple_clip

    # Frame Order Shuffle
    def order_shuffle(self, tuple_clip, tuple_audio):

        tuple_order = list(range(0, self.tuple_len))

        clip_and_order = list(zip(tuple_clip, tuple_audio, tuple_order))
        random.shuffle(clip_and_order)
        tuple_clip, tuple_audio, tuple_order = zip(*clip_and_order)

        return torch.stack(tuple_clip), torch.cat(tuple_audio), \
               torch.tensor(tuple_order)              # np.concatenate(tuple_audio), \

    def idx2data(self, idx, frame_list, audio_list):

        data = self.csvfile.iloc[idx]

        path = data['path']
        classnum = data['classnum']

        # has n cut videos
        frame_list = glob(os.path.join(path, frame_list))
        frame_list.sort()

        audio_list = glob(os.path.join(path, audio_list))
        audio_list.sort()

        return classnum, frame_list, audio_list

    def get_at_data(self, idx):
        data = self.csvfile.iloc[idx]
        path = data['path']

        at_list = glob(os.path.join(path, self._front_view_audio_tracking_list))
        at_list.sort()

        return at_list

    def read_data(self, frame, audio, select_cut_id, clip_sr=1, label=None):

        # clip_sr is the pace of video pace, usually randomly sampled from 1~4

        return self.read_frames(frame[select_cut_id],  label=label, clip_sr=clip_sr), \
               self.read_audio(audio[select_cut_id], clip_sr)

    def __len__(self):
        return len(self.csvfile)

class x360Dataset(x360_Basic_Dataset):
    """UCF101 dataset for video clip order prediction. Generate clips and permutes them on-the-fly.
        Need the corresponding configuration file exists.

        Args:
            root_dir (string): Directory with videos and splits.
            train (bool): train split or test split.
            clip_len (int): number of frames in clip, 16/32/64.
            interval (int): number of frames between clips, 16/32.
            tuple_len (int): number of clips in each tuple, 3/4/5.
            transforms_ (object): composed transforms which takes in PIL image and output tensors.
        """

    def __init__(self, args, csvfile, mode='train',
                 use_360=False, use_front=True, use_clip=False):

        self.args = args
        self.clip_len = args.cl   # length of video clip
        self.max_sr = args.max_sr          # max sampling rate

        self.interval = args.it   # args.cl, args.it, args.tl
        self.tuple_len = args.tl  # number of clips in each tuple, 3/4/5.

        self.mode = mode
        self.csvfile = pd.read_csv(csvfile)
        self.data_list = []
        self.cut_number = 6

        self.final_img_size = 128  #args.crop_sz
        self.resize_img_size = 512

        # $Root/ (Inside_Ouside)/ Location(Label)/ Video_ID
        root = '/bask/projects/j/jiaoj-3d-vision/360XProject/Data'

        self._360cut_frames_list = "360/360_panoramic_cut/all_frames/*"
        self._360cut_audios_list = "360/360_panoramic_cut/audios/*.wav"

        self._frontcut_frames_list = "360/front_view_cut/all_frames/*"
        self._frontcut_audios_list = "360/front_view_cut/audios/*.wav"

        self._Clip_frames_list = "Snapchat/clip1/binocular_cut/all_frames/*"
        self._Clip_audios_list = "Snapchat/clip1/binocular_cut/audios/*.wav"

        self._front_view_audio_tracking_list = "360/front_view_cut/at/*/front_audio_tracking.npy"

        self.use_360 = use_360
        self.use_front = use_front
        self.use_clip = use_clip

        self.resize = A.Compose([
                A.SmallestMaxSize(self.resize_img_size, interpolation=3)
                ])

        if self.mode == 'train':

            self.transform = A.Compose([
                # A.HorizontalFlip(p=0.5),
                # A.VerticalFlip(p=0.5),
                # A.ShiftScaleRotate(scale_limit=0.3, rotate_limit=15, p=0.5),

                A.RandomBrightnessContrast(brightness_limit=(-0.1, 0.1),
                                           contrast_limit=(-0.1, 0.1), p=0.5),
                A.ImageCompression(p=0.3),  #

                # A.Affine(p=0.3),
                ToTensorV2(),
            ])

            # , keypoint_params=A.KeypointParams(format='xy', remove_invisible=False)

            self.au_transform = Au.Compose([
                    Au.AddGaussianNoise(min_amplitude=0.001, max_amplitude=0.015, p=0.5),
                    Au.TimeStretch(min_rate=0.8, max_rate=1.25, p=0.5),
                    Au.PitchShift(min_semitones=-4, max_semitones=4, p=0.5),
                    Au.Shift(min_fraction=-0.5, max_fraction=0.5, p=0.5),
                ])

            self.spec_transform = Au.SpecCompose([
                        # Au.SpecChannelShuffle(p=0.5),
                        Au.SpecFrequencyMask(p=0.5),
                        # ToTensorV2()
                ])

        else:
            self.transform = A.Compose([
                ToTensorV2(),
            ])

            self.au_transform = Au.Compose([])
            self.spec_transform = Au.SpecCompose([])

    def __getitem__(self, idx):

        length_of_list = 100
        clip_sr = random.randint(1, self.max_sr)

        # Load Data list
        if self.use_360:
            cls, _360frames_list, _360audio_list = \
                self.idx2data(idx, self._360cut_frames_list, self._360cut_audios_list)
            length_of_list = np.min([length_of_list, len(_360frames_list), len(_360audio_list)])

        if self.use_front:
            cls, _frontframes_list, _frontaudio_list = \
                self.idx2data(idx, self._frontcut_frames_list, self._frontcut_audios_list)
            length_of_list = np.min([length_of_list, len(_frontframes_list), len(_frontaudio_list)])

        if self.use_clip:
            cls, _Clipframes_list, _Clipaudio_list = \
                self.idx2data(idx, self._Clip_frames_list, self._Clip_audios_list)
            length_of_list = np.min([length_of_list, len(_Clipframes_list), len(_Clipaudio_list)])

        Output_dict = {"cls": cls, "sr": clip_sr}

        if self.args.use_directional_audio:
            at_list = self.get_at_data(idx)
            length_of_list = np.min([length_of_list, len(at_list)])

        select_cut_id = np.random.randint(0, length_of_list)   # number of clips

        if self.args.use_directional_audio:
            at = self.read_audio_tracking(at_list[select_cut_id])[:250]  # clip at 250
            if len(at) < 255:
                at = np.pad(at, (0,255-len(at)), 'constant', constant_values=(0, 0))

            Output_dict['at'] = np.expand_dims(at, 0)

        # select Data
        if self.use_360:
            # tuple
            f, a = self.read_data(_360frames_list, _360audio_list, select_cut_id,
                                  clip_sr=clip_sr)
            f, a, o = self.order_shuffle(f, a[0])

            Output_dict["360"] = {"f": f, "order": o, "a": a}

        if self.use_front:
            f, a = self.read_data(_frontframes_list, _frontaudio_list, select_cut_id,
                                   clip_sr=clip_sr)

            f, a, o = self.order_shuffle(f, a[0])

            Output_dict["front"] = {"f": f, "order": o, "a": a}

        if self.use_clip:
            f, a = self.read_data(_Clipframes_list, _Clipaudio_list, select_cut_id,
                                  label='crop', clip_sr=clip_sr)
            f, a, o = self.order_shuffle(f, a[0])

            Output_dict["clip"] = {"f": f, "order": o, "a": a}

        return Output_dict

if __name__ == "__main__":
    Annotation = '../360data/AVE_Dataset/Annotations.txt'
    with open(Annotation) as f:
        lines = f.readlines()
    print("totally {} lines".format(len(lines)))

    label_dict = {}

    for i in lines[1:]:
        i = i.split('&')
        label_dict[i[1]] = i[0]

    label_list = set(i for i in label_dict.values())
    print(label_list)


from django.http import HttpResponse
from django.shortcuts import render
from .models import Producto, Categoria, Proveedor
def inicio(request):
    return render(request, 'home.html')

def productos(request):
    productos = Producto.objects.all()
    proovedor = Proveedor.objects.all()
    categorias = Categoria.objects.all()
    return render(request, 'productos.html', {'productos':productos, 'proveedor':proovedor, 'categorias':categorias})

def productoFormulario(request):
    if request.method == 'POST':
        categoria_id = request.POST.get('categoria')
        proveedor_id = request.POST.get('proveedor')
        if categoria_id == 'nueva':
            # Seleccionó una nueva categoría, con lo cual primero lo guardo.
            nueva_categoria = Categoria(nombre=request.POST['nueva_categoria'])
            nueva_categoria.save()
            categoria_id = nueva_categoria.id #Guardo el id de la categoria generada, para poder referenciarla posteriormente.
        if proveedor_id == 'nueva':
            nuevo_proveedor = Proveedor(nombre=request.POST['nuevo_proveedor'])
            nuevo_proveedor.save()
            proveedor_id = nuevo_proveedor.id
        producto = Producto.objects.create(nombre=request.POST['nombre'],precio=request.POST['precio'],stock=request.POST['stock'],categoria_id=categoria_id, proveedor_id=proveedor_id)
        producto.save()
        return render(request, 'home.html')
    proveedores = Proveedor.objects.all()
    categorias = Categoria.objects.all()
    return render(request, 'productoFormulario.html', {'categorias':categorias})


from modules.components.regex import *
from modules.components.regexTree import *
from modules.components.NFA import *
from modules.components.DFA import *
from modules.components.minimizedDFA import *
from modules.common.utils import *
from modules.components.automataTester import *
from modules.components.yalexParser import *
from modules.components.yaparParser import *
from modules.components.codeGen import *

def readYalex(file, delete = True):
    if delete:
        checkFolder("out/")
        deleteAllFiles("out/")

    print("\n ----- Reading Yalex file -----")

    yalex = YalexParser(file)
    print("Infix from Yalex file: " + yalex.regex)

    regex = Regex(yalex.regex, yalex.alphabet)
    postfix = regex.postfix

    print("\nParsed Infix: " + regex.validatedInfix)
    print("\nPostfix: " + postfix)

    tree = RegexTree(postfix, file.split("/")[-1])
    tree.printTree()

def readYapar(yalexFile, yaparFile, simulateFile, delete = True):
    if delete:
        checkFolder("out/")
        deleteAllFiles("out/")

    print("\n ----- Parsing SLR(1) -----")

    yalex = YalexParser(yalexFile)
    regex = Regex(yalex.regex, yalex.alphabet)
    tree = RegexTree(regex.postfix, yalexFile.split("/")[-1], True)
    tree.printTree()

    yapar = YaparParser(yaparFile, yalex)
    yapar.createImage()
    yapar.buildParsingTable()
    yapar.print()

    newDFA = DFA(tree, yalexFile.split("/")[-1])
    newDFA.createImage()
    yalexSimulation = yalex.simulate(simulateFile, newDFA, yalex.tokens)
    yapar.simulate(yalexSimulation)

def generateFromYalex(file):
    checkFolder("out/")
    deleteAllFiles("out/")

    print("\n ----- Reading Yalex file -----")

    yalex = YalexParser(file)
    print("Infix from Yalex file: " + yalex.regex)

    regex = Regex(yalex.regex, yalex.alphabet)
    postfix = regex.postfix

    print("\nParsed Infix: " + regex.validatedInfix)
    print("\nPostfix: " + postfix)

    tree = RegexTree(postfix, file.split("/")[-1], True)
    tree.printTree()

    newDFA = DFA(tree, file.split("/")[-1])
    newDFA.print()
    newDFA.createImage()

    CodeGen(newDFA, yalex.tokens)

def generateNFA(regex, simulate = None):
    checkFolder("out/")
    deleteAllFiles("out/")

    print("\n ----- Generating Automatas -----")
    print("Original Infix: " + regex)

    regex = Regex(regex)
    postfix = regex.postfix
    parsed = regex.validatedInfix

    print("Parsed Infix: " + parsed)
    print("Postfix: " + postfix)

    tree = RegexTree(postfix, parsed)
    tree.printTree()

    newNFA = NFA(tree.tree, parsed)
    newNFA.print()
    newNFA.createImage()

    subsetDFA = DFA(newNFA, parsed)
    subsetDFA.print()
    subsetDFA.createImage()

    tree = RegexTree(postfix, parsed, True)
    tree.printTree()

    directDFA = DFA(tree, parsed)
    directDFA.print()
    directDFA.createImage()

    minDirectDFA = minimizedDFA(directDFA)
    minDirectDFA.print()
    minDirectDFA.createImage()

    minSubsetDFA = minimizedDFA(subsetDFA)
    minSubsetDFA.print()
    minSubsetDFA.createImage()

    if simulate:
        newNFA.simulate(simulate)
        subsetDFA.simulate(simulate)
        directDFA.simulate(simulate)
        minDirectDFA.simulate(simulate)
        minSubsetDFA.simulate(simulate)

def runTests(regex, simulate):
    checkFolder("out/")
    deleteAllFiles("out/")

    if isinstance(regex, str):
        testAutomata(regex, simulate)
    elif isinstance(regex, list):
        testAutomatas(regex, simulate)


import os
import pandas as pd
import sys
import matplotlib.pyplot as plt
import lib.math as lib

def draw():
	ax = plt.subplot2grid(shape=(nb[0], nb[1]), loc=(y, i - y * size))
	ax.set_title(title[i], color='#2497C0')
	ax.set_ylabel('Frequency', color='#A9ADAF')
	ax.set_xlabel('Score', color='#DCD0A1')
	ax.hist(g[title[i]], bins='auto', density=True, alpha=0.75, histtype='stepfilled', color='#EC493C', label="Gryffindor")
	ax.hist(r[title[i]], bins='auto', density=True, alpha=0.75, histtype='stepfilled', color='#125E9F', label="Ravenclaw")
	ax.hist(s[title[i]], bins='auto', density=True, alpha=0.75, histtype='stepfilled', color='#24C070', label="Slytherin")
	ax.hist(h[title[i]], bins='auto', density=True, alpha=0.75, histtype='stepfilled', color='#F8FC05', label="Hufflepuff")
	ax.legend()

def get_size_to_draw():
	ncol = size
	if len(title) < size:
		ncol = len(title)
	nrow = int((len(title)) / size) + 1
	if ((len(title)) % size == 0):
		nrow -= 1
	if (ncol == 0 or nrow == 0):
		print("Error: no line data")
		exit(-1)
	return ([nrow, ncol])

if __name__ == "__main__":
	if (len(sys.argv) < 2):
		print("Error: no file data included")
		exit(-1)
	FILENAME = str(sys.argv[1])
	# read the data
	if not os.path.exists(FILENAME):
		print("Error: file not found")
		exit(-1)
	data = pd.read_csv(FILENAME, index_col=False)
	if (lib.describe_count(data['Hogwarts House'].dropna()) == 0):
		print("Error: empty value")
		exit(-1)
	# drop the data
	data.drop(['Index', 'First Name', 'Last Name', 'Birthday', 'Best Hand'], axis='columns', inplace=True)
	g = data.loc[data['Hogwarts House'] == "Gryffindor"]
	r = data.loc[data['Hogwarts House'] == "Ravenclaw"]
	s = data.loc[data['Hogwarts House'] == "Slytherin"]
	h = data.loc[data['Hogwarts House'] == "Hufflepuff"]
	data.drop(['Hogwarts House'], axis='columns', inplace=True)
	title = list(data.columns)
	size = 4
	nb = get_size_to_draw()
	fig = plt.figure(figsize=(nb[0] * 2, nb[1] * 2))
	plt.style.use('seaborn')
	fig.set_size_inches(18.5, 10.5)

	y = -1
	for i in range(len(title)):
		if (i % size == 0):
			y += 1
		draw()
	colors = ['red', 'tan', 'lime']
	fig.tight_layout()
	fig.savefig('img/histogram.png', dpi=100)
	plt.show()


import numpy as np

def normalize(A: np.matrix) -> np.matrix:
    if A.shape[0] != A.shape[1]:
        raise ValueError("A should be a square matrix")

    n = A.shape[0]
    A = np.asarray(A)

    # normalize sink nodes
    is_sink_node = np.sum(A, axis=1) == 0
    B = (np.ones_like(A) - np.identity(n)) / (n-1)
    A[:, is_sink_node] += B[:, is_sink_node]

    # normalize other nodes
    D_inv = np.diag(1/np.sum(A, axis=1))
    A = np.dot(D_inv, A)

    return np.matrix(A)


import cv2
import matplotlib
import matplotlib.pyplot as plt

#reading image
img1 = cv2.imread('broken1.png')
gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)

#keypoints
sift = cv2.SIFT_create()
kp = sift.detect(gray1,None)
img1 = cv2.drawKeypoints(gray1, kp, img1)
plt.imshow(img1)
plt.show()


# multiAgents.py
# --------------
# Licensing Information:  You are free to use or extend these projects for
# educational purposes provided that (1) you do not distribute or publish
# solutions, (2) you retain this notice, and (3) you provide clear
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
#
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
# The core projects and autograders were primarily created by John DeNero
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
# Student side autograding was added by Brad Miller, Nick Hay, and
# Pieter Abbeel (pabbeel@cs.berkeley.edu).

from util import manhattanDistance
from game import Directions
import random, util

from game import Agent
from pacman import GameState

class ReflexAgent(Agent):
    """
    A reflex agent chooses an action at each choice point by examining
    its alternatives via a state evaluation function.

    The code below is provided as a guide.  You are welcome to change
    it in any way you see fit, so long as you don't touch our method
    headers.
    """

    def getAction(self, gameState: GameState):
        """
        You do not need to change this method, but you're welcome to.

        getAction chooses among the best options according to the evaluation function.

        Just like in the previous project, getAction takes a GameState and returns
        some Directions.X for some X in the set {NORTH, SOUTH, WEST, EAST, STOP}
        """
        # Collect legal moves and successor states
        legalMoves = gameState.getLegalActions()

        # Choose one of the best actions
        scores = [self.evaluationFunction(gameState, action) for action in legalMoves]
        bestScore = max(scores)
        bestIndices = [index for index in range(len(scores)) if scores[index] == bestScore]
        chosenIndex = random.choice(bestIndices) # Pick randomly among the best

        "Add more of your code here if you want to"

        return legalMoves[chosenIndex]

    def evaluationFunction(self, currentGameState: GameState, action):
        """
        Design a better evaluation function here.

        The evaluation function takes in the current and proposed successor
        GameStates (pacman.py) and returns a number, where higher numbers are better.

        The code below extracts some useful information from the state, like the
        remaining food (newFood) and Pacman position after moving (newPos).
        newScaredTimes holds the number of moves that each ghost will remain
        scared because of Pacman having eaten a power pellet.

        Print out these variables to see what you're getting, then combine them
        to create a masterful evaluation function.
        """
        # Useful information you can extract from a GameState (pacman.py)
        successorGameState = currentGameState.generatePacmanSuccessor(action)
        newPos = successorGameState.getPacmanPosition()
        newFood = successorGameState.getFood()
        newGhostStates = successorGameState.getGhostStates()
        newScaredTimes = [ghostState.scaredTimer for ghostState in newGhostStates]

        "*** YOUR CODE HERE ***"
        return successorGameState.getScore()

def scoreEvaluationFunction(currentGameState: GameState):
    """
    This default evaluation function just returns the score of the state.
    The score is the same one displayed in the Pacman GUI.

    This evaluation function is meant for use with adversarial search agents
    (not reflex agents).
    """
    return currentGameState.getScore()

class MultiAgentSearchAgent(Agent):
    """
    This class provides some common elements to all of your
    multi-agent searchers.  Any methods defined here will be available
    to the MinimaxPacmanAgent, AlphaBetaPacmanAgent & ExpectimaxPacmanAgent.

    You *do not* need to make any changes here, but you can if you want to
    add functionality to all your adversarial search agents.  Please do not
    remove anything, however.

    Note: this is an abstract class: one that should not be instantiated.  It's
    only partially specified, and designed to be extended.  Agent (game.py)
    is another abstract class.
    """

    def __init__(self, evalFn = 'scoreEvaluationFunction', depth = '2'):
        self.index = 0 # Pacman is always agent index 0
        self.evaluationFunction = util.lookup(evalFn, globals())
        self.depth = int(depth)

class MinimaxAgent(MultiAgentSearchAgent):
    """
    Your minimax agent (question 2)
    """

    def getAction(self, gameState: GameState):
        """
        Returns the minimax action from the current gameState using self.depth
        and self.evaluationFunction.

        Here are some method calls that might be useful when implementing minimax.

        gameState.getLegalActions(agentIndex):
        Returns a list of legal actions for an agent
        agentIndex=0 means Pacman, ghosts are >= 1

        gameState.generateSuccessor(agentIndex, action):
        Returns the successor game state after an agent takes an action

        gameState.getNumAgents():
        Returns the total number of agents in the game

        gameState.isWin():
        Returns whether or not the game state is a winning state

        gameState.isLose():
        Returns whether or not the game state is a losing state
        """
        "*** YOUR CODE HERE ***"
        util.raiseNotDefined()

class AlphaBetaAgent(MultiAgentSearchAgent):
    """
    Your minimax agent with alpha-beta pruning (question 3)
    """

    def getAction(self, gameState: GameState):
        """
        Returns the minimax action using self.depth and self.evaluationFunction
        """
        "*** YOUR CODE HERE ***"
        util.raiseNotDefined()

class ExpectimaxAgent(MultiAgentSearchAgent):
    """
      Your expectimax agent (question 4)
    """

    def getAction(self, gameState: GameState):
        """
        Returns the expectimax action using self.depth and self.evaluationFunction

        All ghosts should be modeled as choosing uniformly at random from their
        legal moves.
        """
        "*** YOUR CODE HERE ***"

        # mby these could be useful for new custom eval?
        # successorGameState = currentGameState.generatePacmanSuccessor(action)
        # newPos = successorGameState.getPacmanPosition()
        # newFood = successorGameState.getFood()
        # newGhostStates = successorGameState.getGhostStates()
        # newScaredTimes = [ghostState.scaredTimer for ghostState in newGhostStates]

        def newUtil(gameState):
            position = gameState.getPacmanPosition()
            pellets = gameState.getFood().asList()

            nearestScaredGhost = float('inf')
            nearestPellet = float('inf')
            if pellets:
                for pellet in pellets:
                    distance = manhattanDistance(position, pellet)
                    if distance < nearestPellet:
                        nearestPellet = distance
            # for nst in newScaredTimes:
            #     if(nst>0):
            #         #ghost is scared here
            #         nearestScaredGhost=min(nearestScaredGhost,nst)

            score = gameState.getScore()

            # foodVal = 10
            # scaredval = 20 #test case vals
            #use new scared time here maybe to compute eval to chase ghost
            evaluation = 1.0 / nearestPellet + score
            #customeval with weights on remaining ghosts did not work, aim was to get pacman to chase ghosts when they are scared
            return evaluation

        # Determine the action that maximizes the expected utility for Pacman
        def max_utility(gameState, depth):
            if gameState.isWin():
                return newUtil(gameState)
            if gameState.isLose():
                return newUtil(gameState)
            if (depth+1) == self.depth: #testing needs to be done with different values. 3 is very slow, might be worse in the lab
                return newUtil(gameState)
            #These conditions are used to check if the game is in its final stage
            #The game can conlcude in 3 different ways, either winning, losing or when the depth
            #is higher than the specified depth designated in the multiAgent code.

            max_value = float('-inf')
            pacman_actions = gameState.getLegalActions(0)
            #List of pacman actions stored to loop through and check which would yield a better outcome
            for action in pacman_actions:
                successor_state = gameState.generateSuccessor(0, action)
                current_expectation = min_utility(successor_state, depth+1, 1)
                # Update max_value if the expected value is greater
                max_value = max(max_value, current_expectation)
            return max_value

        # Determine the minimum expected utility for the ghosts
        def min_utility(gameState, depth, agent_index):
            if gameState.isWin():
                return newUtil(gameState)
            if gameState.isLose():
                return newUtil(gameState)
            actions = gameState.getLegalActions(agent_index)
            max_expectation = 0
            #try to emulate chance node here but it won't really work without having real values to comfortably assume prob for each state
            num_actions = len(actions)
            for action in actions:
                successor_state = gameState.generateSuccessor(agent_index, action)
                if agent_index == (gameState.getNumAgents() - 1):
                    current_expectation = max_utility(successor_state, depth)
                else:
                    current_expectation = min_utility(successor_state, depth, agent_index + 1)
                max_expectation += current_expectation
            if num_actions == 0:
                return 0
            return (max_expectation+0.0) / (num_actions+0.0)

        # Get the legal actions for the root node
        actions = gameState.getLegalActions(0)

        # Initialize variables for the best action and its expected utility
        best_action = None
        best_score = float('-inf')

#
        # Evaluate each action and choose the one with the highest expected utility
        for action in actions:
            # Generate the successor state for the current action
            successor_state = gameState.generateSuccessor(0, action)
            # Compute the expected utility of the successor state using min_utility
            score = min_utility(successor_state, 0, 1)
            # Update the best action and its expected utility if necessary
            if score > best_score:
                best_action = action
                best_score = score

        # Return the best action based off nesw&stop vals. Should be a string so if best_action fails here it cud be bc of that
        #none val shouldn't cause an issue though
        return best_action

        util.raiseNotDefined()

def betterEvaluationFunction(currentGameState: GameState):
    """
    Your extreme ghost-hunting, pellet-nabbing, food-gobbling, unstoppable
    evaluation function (question 5).

    DESCRIPTION: Similar idea to phase 1 where manhattan distance is used as the heuristic for picking the closest food for the pacman to eat
    """
    "*** YOUR CODE HERE ***"
    util.raiseNotDefined()

# Abbreviation
better = betterEvaluationFunction


def argflag(key, argv=None):
    """
    Determines if a key is specified on the command line

    Args:
        key (str or tuple): string or tuple of strings. Each key should be
            prefixed with two hyphens (i.e. `--`)
        argv (Optional[list]): overrides `sys.argv` if specified

    Returns:
        bool: flag : True if the key (or any of the keys) was specified

    Example:
        >>> import ubelt as ub
        >>> argv = ['--spam', '--eggs', 'foo']
        >>> assert ub.argflag('--eggs', argv=argv) is True
        >>> assert ub.argflag('--ans', argv=argv) is False
        >>> assert ub.argflag('foo', argv=argv) is True
        >>> assert ub.argflag(('bar', '--spam'), argv=argv) is True
    """
    if argv is None:  # nocover
        argv = sys.argv
    keys = [key] if isinstance(key, six.string_types) else key
    flag = any(k in argv for k in keys)
    return flag


#!/usr/bin/env python

# Using the ROS api to control different joints in the hsr with the
# aim of extending the arm towrads an object to try to pick it up
# Then afterwards, it will move back towards its default position

import sys
import time
import tf
import controller_manager_msgs.srv
import rospy
import trajectory_msgs.msg
from myvis.msg import Object
from myvis.msg import Objects

DEG_90 = 1.5708
MAX_LIFT = 0.69
MIN_LIFT = 0
GRIPPER_OPEN_ANGLE = 1.239
GRIPPER_CLOSE_ANGLE = -0.105
OPEN_GRIPPER_OFFSET = 0.07
ARM_LOWERING_SPEED = float(sys.argv[1])
HAND_CAMERA_TOPIC = '/hsrb/hand_camera/image_raw'
HEAD_RGB_TOPIC = '/hsrb/head_rgbd_sensor/rgb/image_rect_color'
HEAD_DEPT_TOPIC = '/hsrb/head_rgbd_sensor/depth_registered/image_rect_raw'
OBJECT_POS_TOPIC = 'ObjectsMap'

class Arm_Controller:
    def __init__(self):
        rospy.init_node('arm_test')
        self.object_to_pick = Object()
        self.objects_list = Objects()
        # initialize ROS publisher
        self.arm_pub = rospy.Publisher('/hsrb/arm_trajectory_controller/command',
                              trajectory_msgs.msg.JointTrajectory, queue_size=10)
        self.gripper_pub = rospy.Publisher('/hsrb/gripper_controller/command',
                                trajectory_msgs.msg.JointTrajectory, queue_size=10)
        self.omni_pub = rospy.Publisher('/hsrb/omni_base_controller/command',
                                trajectory_msgs.msg.JointTrajectory, queue_size=10)
        self.object_sub = rospy.Subscriber(OBJECT_POS_TOPIC, Objects, self.object_pos_callback)

        self.head_pub = rospy.Publisher('/hsrb/head_trajectory_controller/command',
                                trajectory_msgs.msg.JointTrajectory, queue_size=10)

        # wait to establish connection between the controller
        while self.arm_pub.get_num_connections() == 0:
            rospy.sleep(0.1)

        # make sure the controller is running
        rospy.wait_for_service('/hsrb/controller_manager/list_controllers')
        list_controllers = (
            rospy.ServiceProxy('/hsrb/controller_manager/list_controllers',
                               controller_manager_msgs.srv.ListControllers))

        # Check that both the arm trajectory and gripper controllers are running
        running = False
        # Assume gripper always works, some reason it doesn't detect the controller but when
        # publishing, the gripper receives the message fine
        running_check = [False, True, False,False]
        while not running:
            rospy.sleep(0.1)
            for c in list_controllers().controller:
                if c.name == 'arm_trajectory_controller' and c.state == 'running':
                    running_check[0] = True
                if c.name == 'gripper_controller' and c.state == 'running':
                    running_check[1] = True
                if c.name == 'omni_base_controller' and c.state == 'running':
                    running_check[2] = True
                if c.name == 'head_trajectory_controller' and c.state == 'running':
                    running_check[3] = True
                # Check all controllers are running
                running = True
                for flag in running_check:
                    if flag == False:
                        running = False

        # Arm joints
        self.al_joint = 0
        self.af_joint = 0
        self.ar_joint = DEG_90
        self.wf_joint = DEG_90
        self.wr_joint = 0
        #self.reset_default_pos()
        # Gripper joints
        self.hm_joint = 0
        # Initial position
        trans = self.lookup_transform("/map", "/base_link")
        self.base_x = trans[0]
        self.base_y = trans[1]
        self.base_z = trans[2]
        print("Initial position: x: %d, y: %d" %(self.base_x, self.base_y))
        print("Arm controller initialised")

    # Reset the arm to the default position
    def reset_default_pos(self):
        self.move_arm(0, 0, -DEG_90, -DEG_90, 0)
        print("Resetting arm to default position")

    # Turns the arm roll and wrist flex joint to face the hand north of the robot
    def face_hand_forward(self):
        self.move_arm(self.al_joint, self.af_joint, 0, -DEG_90, self.wr_joint)
        print("Arm facing forward published")

    # Lower the arm and adjust the wrist flex joint so that the hand is always 90 degrees
    def lower_arm_sync_wrist(self):
        while self.af_joint > -DEG_90:
            temp_af_joint = max(self.af_joint-ARM_LOWERING_SPEED, -DEG_90)
            temp_wf_joint = -(DEG_90-abs(temp_af_joint))
            self.move_arm(self.al_joint, temp_af_joint, self.ar_joint, temp_wf_joint, self.wr_joint)
            rospy.sleep(0.5)

    # Move the arm by pusblishing a joint trajectory message with the relevant arm joints
    def move_arm(self, al_joint, af_joint, ar_joint, wf_joint, wr_joint):
        # fill ROS message
        traj = trajectory_msgs.msg.JointTrajectory()
        traj.joint_names = ["arm_lift_joint", "arm_flex_joint",
                            "arm_roll_joint", "wrist_flex_joint", "wrist_roll_joint"]
        p = trajectory_msgs.msg.JointTrajectoryPoint()
        p.positions = [al_joint, af_joint, ar_joint, wf_joint, wr_joint]
        p.velocities = [0, 0, 0, 0, 0]
        p.time_from_start = rospy.Time(3)
        traj.points = [p]
        # Update joint angles
        self.al_joint = al_joint
        self.af_joint = af_joint
        self.ar_joint = ar_joint
        self.wf_joint = wf_joint
        self.wr_joint = wr_joint
        # publish ROS message
        self.arm_pub.publish(traj)

    def move_arm_lift(self, target_z):
        currTrans = self.lookup_transform('/base_link', '/hand_motor_dummy_link')
        curr_z = currTrans[2]
        if curr_z < target_z:
            temp_al_joint = target_z - curr_z
        else:
            temp_al_joint = curr_z - target_z
        self.move_arm(temp_al_joint, self.af_joint, self.ar_joint, self.wf_joint, self.wr_joint)

    # Move the gripper by publishing a joint trajectory message with the hand motor joint
    def move_gripper(self, hm_joint):
        traj = trajectory_msgs.msg.JointTrajectory()
        traj.joint_names = ["hand_motor_joint"]
        p = trajectory_msgs.msg.JointTrajectoryPoint()
        p.positions = [hm_joint]
        p.velocities = [0]
        p.effort = [0.1]
        p.time_from_start = rospy.Time(3)
        # Update joint
        self.hm_joint = hm_joint
        traj.points = [p]
        # publish ROS message
        self.gripper_pub.publish(traj)

    def open_gripper(self):
        self.move_gripper(GRIPPER_OPEN_ANGLE)

    def close_gripper(self):
        self.move_gripper(GRIPPER_CLOSE_ANGLE)

    def move_base(self, x_pos, y_pos):
        traj = trajectory_msgs.msg.JointTrajectory()
        traj.joint_names = ["odom_x", "odom_y", "odom_t"]
        p = trajectory_msgs.msg.JointTrajectoryPoint()
        p.positions = [x_pos, y_pos, 0]
        p.velocities = [0, 0, 0]
        p.time_from_start = rospy.Time(6)
        traj.points = [p]
        # Update the base pos
        self.base_x = x_pos
        self.base_y = y_pos
        # publish ROS message
        # For some reason publishing the message once doesn't get the robot
        for i in range(10):
            self.omni_pub.publish(traj)

    # Gets transform transform of the hand to offset the position where the robot should be
    # so the gripper is right in front of the object
    def align_hand_with_object(self, target_x, target_y):
        trans = self.lookup_transform('/base_link', '/hand_motor_dummy_link')
        new_target_x = target_x-trans[0]-OPEN_GRIPPER_OFFSET
        new_target_y = target_y-trans[1]
        print("Original target x: %f y: %f Target x: %f y: %f" %(target_x, target_y, new_target_x, new_target_y))
        return new_target_x, new_target_y

    def lookup_transform(self, source_link, target_link):
        listener = tf.TransformListener()
        seconds_passed = 0
        timeout = 5
        delay_rate = 0.5
        print("Looking up transform")
        while not rospy.is_shutdown():
            try:
                (trans,rot) = listener.lookupTransform(source_link, target_link, rospy.Time(0))
                print("Transform received")
                return trans
            except (tf.LookupException, tf.ConnectivityException, tf.ExtrapolationException):
                continue
            if seconds_passed >= timeout:
                print("Transform lookup timed out")
                return (0, 0, 0)
            rospy.sleep(delay_rate)
            seconds_passed += delay_rate

    def objects_print(self):
        print("Received Object")
        print("Objects array has length: ",  self.objects_list.length)

    def get_object(self,name):
        for xa in range(0, self.objects_list.length):
            if self.objects_list.Objects[xa].name == name:
                self.object_to_pick = self.objects_list.Objects[xa]

    def object_print(self):
        print("Objects name: ", self.object_to_pick.name)

    def object_x(self):
        return self.object_to_pick.x

    def object_y(self):
        return self.object_to_pick.y

    def object_z(self):
        return self.object_to_pick.z

    def object_name(self):
        return self.object_to_pick.name

    def move_head(self):
        traj = trajectory_msgs.msg.JointTrajectory()
        traj.joint_names = ["head_pan_joint", "head_tilt_joint"]
        p = trajectory_msgs.msg.JointTrajectoryPoint()
        p.positions = [0.5, 0.5]
        p.velocities = [0, 0]
        p.time_from_start = rospy.Time(3)
        traj.points = [p]
        self.head_pub.publish(traj)

    # Increasing head pan turns head left, decreasing turns head right
    # Increasing head tilt tilts head up, decreasing tilts head down
    def move_headxy(self ,x,y):
        traj = trajectory_msgs.msg.JointTrajectory()
        traj.joint_names = ["head_pan_joint", "head_tilt_joint"]
        p = trajectory_msgs.msg.JointTrajectoryPoint()
        p.positions = [x, y]
        p.velocities = [0, 0]
        p.time_from_start = rospy.Time(3)
        traj.points = [p]
        self.head_pub.publish(traj)

    def grab_obj_mode1(self, target_x, target_y, target_z):
        self.open_gripper()
        self.face_hand_forward()
        self.lower_arm_sync_wrist()
        rospy.sleep(10)
        self.move_arm_lift(target_z)
        # Move to the target location and close the gripper when it is reached
        target_x, target_y = self.align_hand_with_object(target_x, target_y)
        error_bound = 0.05
        # Move Y axis
        print("Moving Y axis")
        trans = self.lookup_transform('/map', '/base_link')
        curr_x = trans[0]
        curr_y = trans[1]
        self.move_base(curr_x, target_y)
        while curr_y < target_y - error_bound or curr_y > target_y + error_bound:
            trans = self.lookup_transform('/map', '/base_link')
            curr_y = trans[0]
            print(trans)
        # Move X axis
        rospy.sleep(5)
        print("Moving X axis")
        trans = self.lookup_transform('/map', '/base_link')
        curr_x = trans[0]
        curr_y = trans[1]
        self.move_base(target_x, curr_y)
        while curr_x < target_x - error_bound or curr_x > target_x + error_bound:
            trans = self.lookup_transform('/map', '/base_link')
            curr_x = trans[0]
            print(trans)
        # Moving in both axis at once can cause hand to collide with the object
        # However if an offset is given in the axis the robot is facing the object, then this can be done
        '''
        while ((curr_x < target_x - error_bound or curr_x > curr_x > target_x + error_bound) or
            (curr_y < target_y - error_bound or curr_y > curr_y > target_y + error_bound)):
            trans = self.lookup_transform('/map', '/base_link')
            curr_x = trans[0]
            curr_y = trans[1]
            print(trans)
        '''
        print("Reached target point on map")
        self.close_gripper()
        rospy.sleep(10)
        # STRANGE BUG: It won't go back to its original position but somewhere halfway but if the self.move_base is called again
        # then it moves back to its original posiiton
        print("Going back to original position")
        self.reset_default_pos()
        self.move_base(ac.base_x, ac.base_y)

    def object_pos_callback(self, objects):
        self.objects_list = objects
        objectTarget = "bottle"
        self.get_object(objectTarget)
        if(self.object_name() == objectTarget):
            print("Objects x: ", self.object_x())
            print("Objects y: ", self.object_y())
            print("Objects z: ", self.object_z())
            self.grab_obj_mode1(self.object_x(), self.object_y(), self.object_z())

def main(args):
    ac = Arm_Controller()
    ac.reset_default_pos()
    ac.open_gripper()
    target_x = float(sys.argv[2])
    target_y = float(sys.argv[3])
    target_z = float(sys.argv[4])

    try:
        rospy.spin()
    except KeyboardInterrupt:
        print("Shutting down")

if __name__ == '__main__':
    main(sys.argv)


claims = []

class Claim:
    def __init__(self, claim_id, offsetH, offsetW, height, width):
        self.id = claim_id
        self.offsetH = offsetH
        self.offsetW = offsetW
        self.height = height
        self.width = width
        self.overlapping = False

rightmost_edge = 0
bottommost_edge = 0

with open("puzzleinput.txt", "r") as file:
    for line in file:
        claim = line.replace("\n", "") # Remove newlines
        claim = claim.split(" ")

        offsetH = int( claim[2].split(",")[0] ) # Example format: 910,975:
        offsetW = int( claim[2].split(",")[1][:-1] ) # This last [:-1] is just to chop off the :

        width = int( claim[3].split("x")[0] )# Example format 27x23
        height = int( claim[3].split("x")[1] )

        # While we are doing this we may as well get the max size of our grid
        right_edge = offsetW + width
        bottom_edge = offsetH + height
        if right_edge > rightmost_edge:
            rightmost_edge = right_edge
        if bottom_edge > bottommost_edge:
            bottommost_edge = bottom_edge

        new_claim = Claim(claim[0], offsetH, offsetW, width, height )
        claims += [new_claim]

print(claims[200].id) # Yep
print(rightmost_edge)
print(bottommost_edge)

grid = []
for n in range(0, rightmost_edge):
    grid += [[0] * bottommost_edge]

print(len(grid))
print(len(grid[2]))
# In theory we should now have a grid 1013 x 1005 large

print("--------------------")
# Onto the meat.
inches_overshot = 0
for claim in claims:
    for nw in range(claim.offsetH, claim.offsetH + claim.height):
        for nh in range(claim.offsetW, claim.offsetW + claim.width):
            grid[nw][nh] += 1
            if grid[nw][nh] > 1:
                inches_overshot += 1
                claim.overlapping = True

inches_overshot = 0

for claim in claims:
    for nw in range(claim.offsetH, claim.offsetH + claim.height):
        for nh in range(claim.offsetW, claim.offsetW + claim.width):
            grid[nw][nh] += 1
            if grid[nw][nh] > 2:
                inches_overshot += 1
                claim.overlapping = True

for claim in claims:
    if not claim.overlapping:
        print("Part 2: ", claim.id)

overlapping_claims = 0
for w in grid:
    for h in w:
        if h > 1:
            overlapping_claims += 1

print("Number of overlapping claims: ", overlapping_claims)
print("---------------")


from training_loop import teaching_training

import utils
import numpy as np
def run_train_loop(args):
    save_single_run = True
    if save_single_run:
        end = args.num_runs_start+1
    else:
        end = args.num_runs_end

    dir = f'{args.rootdir}/teacher-data'

    for seed in range(args.num_runs_start, end):

        print(f'\n\n\n\nrun {seed} with teacher SR = {args.SR} and batch size = {args.teacher_batchsize} and learning rate = {args.teacher_lr} buffer = {args.teacher_buffersize}, reward {args.reward_function}\n\n\n\n')
        print(f'start seed = {args.num_runs_start}, ending seed = {end}')
        if args.trained_teacher:
            file = utils.get_file_name(args, dir, seed)
            return_file = utils.get_return_file_name(args, dir, seed)
            return_list = utils.get_data(return_file)
            print(f'file being used = {file}, return_file = {return_file}')
        else:
            file = None
            return_list = None
        teacher_return_list, teacher_scores, teacher_actions, eval_student_score  = teaching_training(seed, args, file, return_list)
        print(f'Teacher returns on run {seed}, {teacher_return_list}')

        collected_data = [teacher_return_list, teacher_scores, teacher_actions, eval_student_score]
        data_names = ['teacher_return_list', 'teacher_scores', 'teacher_actions', 'eval_student_score']

        for idx, data in enumerate(collected_data):
            dir = f'{args.rootdir}/teacher-data'
            utils.make_dir(args, dir)
            file_details = [data_names[idx], seed]
            model_name = utils.get_model_name(args, dir, file_details)
            utils.save_data(model_name,data)

    print(f'run {seed} complete')