import glob
import os
import re
import time
from datetime import datetime

import numpy as np
import scipy.io
import utm

from kite import util

__all__ = ["Gamma", "Matlab", "ISCE", "GMTSAR", "ROI_PAC", "SARscape", "SNAP_Gamma"]

try:
    from osgeo import gdal

    __all__.append("LiCSAR")
    __all__.append("ARIA")
except ImportError:
    pass

d2r = np.pi / 180.0
km = 1e3
op = os.path

LAMBDA_SENTINEL = 0.055465763

def check_required(required, params):
    for r in required:
        if r not in params:
            return False
    return True

def safe_cast(val, to_type, default=None):
    try:
        return to_type(val)
    except (ValueError, TypeError):
        return default

class HeaderError(Exception):
    pass

class AttribDict(dict):
    def __getattr__(self, item):
        return self[item]

    def __setattr__(self, item, value):
        self[item] = value

class SceneIO(object):
    """Prototype class for SARIO objects."""

    def __init__(self, scene=None):
        if scene is not None:
            self._log = scene._log.getChild("IO/%s" % self.__class__.__name__)
        else:
            import logging

            self._log = logging.getLogger("SceneIO/%s" % self.__class__.__name__)

        self.container = AttribDict(
            phi=0.0,  # Look orientation counter-clockwise angle from east
            theta=0.0,  # Look elevation angle (up from horizontal in degree)
            # 90 deg North
            displacement=None,  # Displacement towards LOS
            frame=AttribDict(
                llLon=None,  # Lower left corner latitude
                llLat=None,  # Lower left corner londgitude
                dN=None,  # Pixel delta in north, meter or degree
                dE=None,  # Pixel delta in east, meter or degree
                spacing="meter",  # Pixel spacing unit
            ),
            # Meta information
            meta=AttribDict(
                title=None,
                orbital_node=None,
                satellite_name=None,
                wavelength=None,
                time_master=None,
                time_slave=None,
            ),
            # All extra information
            extra={},
        )

    def read(self, filename, **kwargs):
        """Read function of the file format

        :param filename: file to read
        :type filename: string
        :param kwargs: Keyword arguments
        :type kwargs: {dict}
        """
        raise NotImplementedError("read not implemented")

    def write(self, filename, **kwargs):
        """Write method for IO

        :param filename: file to write to
        :type filename: string
        :param **kwargs: Keyword arguments
        :type **kwargs: {dict}
        """
        raise NotImplementedError("write not implemented")

    def validate(self, filename, **kwargs):
        """Validate file format

        :param filename: file to validate
        :type filename: string
        :returns: Validation
        :rtype: {bool}
        """
        raise NotImplementedError("validate not implemented")

class Matlab(SceneIO):
    """
    Variable naming conventions for Matlab :file:`.mat` container:

        ================== ==================== ===================== =====
        Property           Matlab ``.mat`` name type                  unit
        ================== ==================== ===================== =====
        Scene.displacement ``ig_``              n x m array           [m]
        Scene.phi          ``phi``              float or n x m array  [rad]
        Scene.theta        ``theta``            float or n x m array  [rad]
        Scene.frame.x      ``xx``               n x 1 vector          [m]
        Scene.frame.y      ``yy``               m x 1 vector          [m]
        Scene.utm_zone     ``utm_zone``         str ('33T')
        ================== ==================== ===================== =====

    Displacement is expected to be in meters. Note that the displacement maps
    could also be pixel offset maps rather than unwrapped SAR interferograms.
    For SAR azimuth pixel offset maps calculate ``phi`` from the heading
    direction and set ``theta=0.``. For SAR range pixel offsets use the same
    LOS angles as for InSAR.
    """

    def validate(self, filename, **kwargs):
        if filename[-4:] == ".mat":
            return True
        else:
            return False
        try:
            variables = self.io.whosmat(filename)
            if len(variables) > 50:
                return False
            return True
        except ValueError:
            return False

    def read(self, filename, **kwargs):
        container = self.container

        mat = scipy.io.loadmat(filename)
        utm_e = None
        utm_n = None
        utm_zone = None
        utm_zone_letter = None
        phi0 = None
        theta0 = None

        for mat_k, _ in mat.items():
            for io_k in container.keys():
                if io_k in mat_k:
                    container[io_k] = np.rot90(mat[mat_k])
                elif "ig_" in mat_k:
                    container.displacement = np.rot90(mat[mat_k])
                elif "xx" in mat_k:
                    utm_e = mat[mat_k].flatten()
                elif "yy" in mat_k:
                    utm_n = mat[mat_k].flatten()
                elif "utm_zone" in mat_k:
                    utm_zone = int(mat["utm_zone"][0][:-1])
                    utm_zone_letter = str(mat["utm_zone"][0][-1])
                elif "phi" in mat_k:
                    phi0 = mat[mat_k].flatten()
                elif "theta" in mat_k:
                    theta0 = mat[mat_k].flatten()

        if len(theta0) == 1:
            container.theta = np.ones(np.shape(container.displacement)) * theta0

        if len(theta0) == 1:
            container.phi = np.ones(np.shape(container.displacement)) * phi0

        if utm_zone is None:
            utm_zone = 33
            utm_zone_letter = "N"
            self._log.warning(
                "Variable utm_zone not defined. Defaulting to UTM Zone %d%s!"
                % (utm_zone, utm_zone_letter)
            )

        if not (np.all(utm_e) or np.all(utm_n)):
            self._log.warning("Could not find referencing UTM vectors in .mat file!")
            utm_e = np.linspace(100000, 110000, container.displacement.shape[0])
            utm_n = np.linspace(1100000, 1110000, container.displacement.shape[1])

        if utm_e.min() < 1e4 or utm_n.min() < 1e4:
            utm_e *= km
            utm_n *= km

        container.frame.dE = np.abs(utm_e[1] - utm_e[0])
        container.frame.dN = np.abs(utm_n[1] - utm_n[0])
        try:
            container.frame.llLat, container.frame.llLon = utm.to_latlon(
                utm_e.min(), utm_n.min(), utm_zone, utm_zone_letter
            )

        except utm.error.OutOfRangeError:
            self._log.warning(
                "Could not interpret spatial vectors," " referencing to 0, 0 (lat, lon)"
            )
            container.frame.llLat, container.frame.llLon = (0.0, 0.0)
        return container

class Gamma(SceneIO):
    """

    Reading geocoded displacement maps (unwrapped igs) originating
        from GAMMA software.

    .. note :: Expects:

        * [:file:`*`] Binary file from Gamma with displacement in radians
        * [:file:`*.slc.par`] If you want to translate radians to
          meters using the `radar_frequency`.
        * [:file:`*par`] Parameter file, describing ``corner_lat, corner_lon,
          nlines, width, post_lat, post_lon`` or ``post_north, post_east,
          corner_east, corner_north, nlines, width``.
        * [:file:`*theta*`, :file:`*phi*`] Two look vector files,
          generated by GAMMA command ``look_vector``.

    .. warning ::

        * Data has to be georeferenced to latitude/longitude or UTM!
        * Look vector files - expected to have a particular name
    """

    @staticmethod
    def _parseParameterFile(filename):
        params = {}
        rc = re.compile(r"^(\w*):\s*([a-zA-Z0-9+-.*]*\s[a-zA-Z0-9_]*).*")

        with open(filename, mode="r") as par:
            for line in par:
                parsed = rc.match(line)
                if parsed is None:
                    continue

                groups = parsed.groups()
                params[groups[0]] = safe_cast(
                    groups[1], float, default=groups[1].strip()
                )
        return params

    def _getParameters(self, path, log=False):
        required_utm = [
            "post_north",
            "post_east",
            "corner_east",
            "corner_north",
            "nlines",
            "width",
        ]
        required_lat_lon = [
            "corner_lat",
            "corner_lon",
            "nlines",
            "width",
            "post_lat",
            "post_lon",
        ]

        path = op.dirname(op.realpath(path))
        par_files = glob.glob("%s/*par" % path)

        for file in par_files:
            params = self._parseParameterFile(file)

            if check_required(required_utm, params) or check_required(
                required_lat_lon, params
            ):
                if not log:
                    self._log.info("Found parameter file %s", file)
                return params

        raise ImportError("Parameter file does not hold required parameters")

    def _getSLCParameters(self, path):
        required_params = ("nlines",)
        path = op.dirname(op.realpath(path))
        par_files = glob.glob(f"{path}/*par")

        for file in par_files:
            params = self._parseParameterFile(file)

            if check_required(required_params, params):
                self._log.info("Found SLC parameter file %s", file)
                return params

        raise ImportError(
            "Could not find SLC parameter file *.par"
            f" with parameters {required_params}"
        )

    def validate(self, filename, **kwargs):
        try:
            par_file = kwargs.pop("par_file", filename)
            self._getParameters(par_file)
            return True
        except ImportError:
            return False

    def _getLOSAngles(self, filename, pattern):
        path = op.dirname(op.realpath(filename))
        phi_files = glob.glob("%s/%s" % (path, pattern))
        if len(phi_files) == 0:
            self._log.warning(
                "Could not find LOS file %s, " "defaulting to angle to 0." % pattern
            )
            return 0.0
        elif len(phi_files) > 1:
            self._log.warning(
                "Found multiple LOS files %s, " "defaulting to angle 0." % pattern
            )
            return 0.0

        filename = phi_files[0]
        self._log.info("Loading LOS %s from %s" % (pattern, filename))
        return np.memmap(filename, mode="r", dtype=">f4")

    def read(self, filename, **kwargs):
        """
        :param filename: Gamma software parameter file
        :type filename: str
        :param par_file: Corresponding parameter (:file:`*par`) file.
                         (optional)
        :type par_file: str
        :returns: Import dictionary
        :rtype: dict
        :raises: ImportError
        """
        par_file = kwargs.pop("par_file", filename)

        params = self._getParameters(par_file, log=True)

        try:
            params_slc = self._getSLCParameters(par_file)
        except ImportError as exc:
            raise exc

        fill = None

        ncols = int(params["width"])
        nlines = int(params["nlines"])
        radar_frequency = float(params_slc.get("radar_frequency", 5.405e9))  # Sentinel1

        displ = np.fromfile(filename, dtype=">f4")
        # Resize array if last line is not scanned completely
        if (displ.size % ncols) != 0:
            fill = np.empty(ncols - displ.size % ncols)
            fill.fill(np.nan)
            displ = np.append(displ, fill)

        displ = displ.reshape(nlines, ncols)
        displ[displ == -0.0] = np.nan
        displ = np.flipud(displ)

        if radar_frequency is not None:
            radar_frequency = float(radar_frequency)
            self._log.info(
                "Scaling displacement by radar_frequency %f GHz"
                % (radar_frequency / 1e9)
            )
            wavelength = util.C / radar_frequency
            displ /= -4 * np.pi
            displ *= wavelength

        else:
            wavelength = "None"
            self._log.warning(
                "Could not determine radar_frequency from *.slc.par file!"
                " Leaving displacement to radians."
            )

        phi = self._getLOSAngles(filename, "*phi*")
        theta = self._getLOSAngles(filename, "*theta*")
        theta = theta

        if isinstance(phi, np.ndarray):
            phi = phi.reshape(nlines, ncols)
            phi = np.flipud(phi)
        if isinstance(theta, np.ndarray):
            theta = theta.reshape(nlines, ncols)
            theta = np.flipud(theta)

        if fill is not None:
            theta = np.append(theta, fill)
            phi = np.append(phi, fill)

        container = self.container

        container.displacement = displ
        container.theta = theta
        container.phi = phi

        container.meta.wavelength = wavelength
        container.meta.title = params.get("title", "None")

        container.bin_file = filename
        container.par_file = par_file

        if params["DEM_projection"] == "UTM":
            utm_zone = params["projection_zone"]
            try:
                utm_zone_letter = utm.latitude_to_zone_letter(params["center_latitude"])
            except ValueError:
                self._log.warning(
                    "Could not parse UTM Zone letter," " defaulting to N!"
                )
                utm_zone_letter = "N"

            self._log.info(
                "Using UTM reference: Zone %d%s" % (utm_zone, utm_zone_letter)
            )

            dN = params["post_north"]
            dE = params["post_east"]

            utm_corn_e = params["corner_east"]
            utm_corn_n = params["corner_north"]

            utm_corn_eo = utm_corn_e + dE * displ.shape[1]
            utm_corn_no = utm_corn_n + dN * displ.shape[0]

            utm_e = np.linspace(utm_corn_e, utm_corn_eo, displ.shape[1])
            utm_n = np.linspace(utm_corn_n, utm_corn_no, displ.shape[0])

            llLat, llLon = utm.to_latlon(
                utm_e.min(), utm_n.min(), utm_zone, utm_zone_letter
            )

            container.frame.llLat = llLat
            container.frame.llLon = llLon

            container.frame.dE = abs(dE)
            container.frame.dN = abs(dN)

        else:
            self._log.info("Using Lat/Lon reference")
            container.frame.spacing = "degree"
            container.frame.llLat = params["corner_lat"] + params["post_lat"] * nlines
            container.frame.llLon = params["corner_lon"]
            container.frame.dE = abs(params["post_lon"])
            container.frame.dN = abs(params["post_lat"])

        return container

class ROI_PAC(SceneIO):
    """
    .. note:: Expects:

        * Binary file from ROI_PAC (:file:`*`)
        * Parameter file (:file:`<binary_file>.rsc`),
          describing ``WIDTH, FILE_LENGTH, X_FIRST, Y_FIRST, X_STEP,
          Y_STEP, WAVELENGTH``
        * If the georeferencing is in UTM coordinates, further needed
          entries in parameter file are 'X_UNIT' and 'Y_UNIT' that give
          'meters' and 'LAT_REF3' as well as 'LON_REF3'.

        The unwrapped displacement is expected in radians and will be scaled
        to meters by ``WAVELENGTH`` parsed from the :file:`*.rsc` file.

    """

    def validate(self, filename, **kwargs):
        try:
            par_file = kwargs.pop("par_file", self._getParameterFile(filename))
            self._parseParameterFile(par_file)
            return True
        except ImportError:
            return False

    def _getParameterFile(self, bin_file):
        par_file = op.realpath(bin_file) + ".rsc"
        try:
            self._parseParameterFile(par_file)
            self._log.info("Found parameter file %s" % par_file)
            return par_file
        except (ImportError, IOError):
            raise ImportError("Could not find ROI_PAC parameter file (%s)" % par_file)

    @staticmethod
    def _parseParameterFile(par_file):
        params = {}
        required_L0 = [
            "WIDTH",
            "FILE_LENGTH",
            "X_FIRST",
            "Y_FIRST",
            "X_STEP",
            "Y_STEP",
            "WAVELENGTH",
        ]
        required_utm = ["X_UNIT", "LAT_REF1", "LON_REF1"]

        rc = re.compile(r"([\w]*)\s*([\w.+-]*)")
        with open(par_file, "r") as par:
            for line in par:
                parsed = rc.match(line)
                if parsed is None:
                    continue
                groups = parsed.groups()
                params[groups[0]] = safe_cast(
                    groups[1], float, default=groups[1].strip()
                )

        if check_required(required_L0, params):
            if check_required(required_utm, params):
                geo_ref = "all"
                return params, geo_ref
            else:
                geo_ref = "latlon"
                return params, geo_ref

        raise ImportError(
            "Parameter file %s does not hold the basic \
             required parameters"
            % par_file
        )

    def read(self, filename, **kwargs):
        """
        :param filename: ROI_PAC binary file
        :type filename: str
        :param par_file: Corresponding parameter (:file:`*rsc`) file.
                         (optional)
        :type par_file: str
        :returns: Import dictionary
        :rtype: dict
        :raises: ImportError
        """
        par_file = kwargs.pop("par_file", self._getParameterFile(filename))

        par, geo_ref = self._parseParameterFile(par_file)
        nlines = int(par["FILE_LENGTH"])
        ncols = int(par["WIDTH"])
        wavelength = par["WAVELENGTH"]
        heading = par["HEADING_DEG"]
        if geo_ref == "latlon":
            lat_ref = par["Y_FIRST"]
            lon_ref = par["X_FIRST"]
        elif geo_ref == "all":
            lat_ref = par["LAT_REF3"]
            lon_ref = par["LON_REF3"]

        look_ref1 = par["LOOK_REF1"]
        look_ref2 = par["LOOK_REF2"]
        look_ref3 = par["LOOK_REF3"]
        look_ref4 = par["LOOK_REF4"]

        utm_zone_letter = utm.latitude_to_zone_letter(lat_ref)
        utm_zone = utm.latlon_to_zone_number(lat_ref, lon_ref)

        look = np.mean(np.array([look_ref1, look_ref2, look_ref3, look_ref4]))

        data = np.memmap(filename, dtype="<f4")
        data = data.reshape(nlines, ncols * 2)

        displ = data[:, ncols:]
        displ = np.flipud(displ)
        displ[displ == -0.0] = np.nan
        displ = displ / (4.0 * np.pi) * wavelength

        z_scale = par.get("Z_SCALE", 1.0)
        z_offset = par.get("Z_OFFSET", 0.0)
        displ += z_offset
        displ *= z_scale

        container = self.container

        container.displacement = displ
        container.theta = np.deg2rad(90.0 - look)
        container.phi = np.deg2rad(-heading + 180.0)

        container.meta.title = par.get("TITLE", "None")
        container.meta.wavelength = par["WAVELENGTH"]
        container.bin_file = filename
        container.par_file = par_file

        if geo_ref == "all":
            if par["X_UNIT"] == "meters":
                container.frame.spacing = "meter"
                container.frame.dE = par["X_STEP"]
                container.frame.dN = -par["Y_STEP"]
                geo_ref = "utm"

            elif par["X_UNIT"] == "degree":
                container.frame.spacing = "degree"
                geo_ref = "latlon"

        elif geo_ref == "latlon":
            self._log.info("Georeferencing is in Lat-Lon [degrees].")
            container.frame.spacing = "degree"
            container.frame.llLat = par["Y_FIRST"] + par["Y_STEP"] * nlines
            container.frame.llLon = par["X_FIRST"]

            # c_utm_0 = utm.from_latlon(lat_ref, lon_ref)
            # c_utm_1 = utm.from_latlon(lat_ref + par['Y_STEP'],
            #                           lon_ref + par['X_STEP'])

            # c.frame.dE = c_utm_1[0] - c_utm_0[0]
            # c.frame.dN = abs(c_utm_1[1] - c_utm_0[1])
            container.frame.dE = par["X_STEP"]
            container.frame.dN = -par["Y_STEP"]

        elif geo_ref == "utm":
            self._log.info(
                "Georeferencing is in UTM (zone %d%s)", utm_zone, utm_zone_letter
            )
            y_ll = par["Y_FIRST"] + par["Y_STEP"] * nlines
            container.frame.llLat, container.frame.llLon = utm.to_latlon(
                par["X_FIRST"], y_ll, utm_zone, zone_letter=utm_zone_letter
            )

        return self.container

class ISCEXMLParser(object):
    def __init__(self, filename):
        import xml.etree.ElementTree as ET

        self.root = ET.parse(filename).getroot()

    @staticmethod
    def type_convert(value):
        for t in (float, int, str):
            try:
                return t(value)
            except ValueError:
                continue
        raise ValueError("Could not convert value")

    def getProperty(self, name):
        name = name.lower()

        for child in self.root.iter():
            child_name = child.get("name")
            if isinstance(child_name, str):
                child_name = child_name.lower()
            if child_name == name.lower():
                if child.tag == "property":
                    return self.type_convert(child.find("value").text)
                elif child.tag == "component":
                    values = {}
                    for prop in child.iter("property"):
                        values[prop.get("name")] = self.type_convert(
                            prop.find("value").text
                        )
                    return values
        return None

class ISCE(SceneIO):
    """
    Reading geocoded, unwrapped displacement maps
    processed with ISCE software (https://winsar.unavco.org/isce.html).

    .. note :: Expects:

        * Unwrapped displacement binary (:file:`*.unw.geo`)
        * Metadata XML (:file:`*.unw.geo.xml`)
        * LOS binary data (:file:`*.rdr.geo`)

    .. note ::

        When using ``gdal_translate`` to crop the scene, use the argument
        ``-co SCHEME=BIL`` to make the output

    .. note ::

        Data are in radians but no transformation to
        meters yet, as ``wavelength`` or at least sensor name is not
        provided in the XML file.
    """

    def validate(self, filename, **kwargs):
        try:
            self._getDisplacementFile(filename)
            self._getLOSFile(filename)
            return True
        except ImportError:
            return False

    def _getLOSFile(self, path):
        if not op.isdir(path):
            path = op.dirname(path)
        rdr_files = glob.glob(op.join(path, "*.rdr.geo"))

        if len(rdr_files) == 0:
            raise ImportError("Could not find LOS file (*.rdr.geo)")

        rdr_file = rdr_files[0]
        self._log.info("Found LOS file: %s", rdr_file)
        return rdr_file

    def _getDisplacementFile(self, path):
        if op.isfile(path):
            disp_file = path
        else:
            files = glob.glob(op.join(path, "*.unw.geo"))
            if len(files) == 0:
                raise ImportError(
                    "Could not find displacement file " "(.unw.geo) at %s", path
                )
            disp_file = files[0]

        if not op.isfile("%s.xml" % disp_file):
            raise ImportError(
                "Could not find displacement XML file "
                "(%s.unw.geo.xml)" % op.basename(disp_file)
            )
        self._log.info("Found Displacement file: %s", disp_file)
        return disp_file

    def read(self, path, **kwargs):
        path = op.abspath(path)
        container = self.container

        xml_file = self._getDisplacementFile(path) + ".xml"
        self._log.info("Parsing ISCE XML file %s" % xml_file)
        isce_xml = ISCEXMLParser(xml_file)

        coord_lon = isce_xml.getProperty("coordinate1")
        coord_lat = isce_xml.getProperty("coordinate2")
        container.frame.dN = np.abs(coord_lat["delta"])
        container.frame.dE = np.abs(coord_lon["delta"])

        nlon = int(coord_lon["size"])
        nlat = int(coord_lat["size"])

        container.frame.spacing = "degree"
        container.frame.llLat = coord_lat["startingvalue"] + (nlat * coord_lat["delta"])
        container.frame.llLon = coord_lon["startingvalue"]

        displ = np.memmap(self._getDisplacementFile(path), dtype="<f4").reshape(
            nlat, nlon * 2
        )[:, nlon:]

        displ = np.flipud(displ)
        displ[displ == 0.0] = np.nan
        container.displacement = displ

        los_file = self._getLOSFile(path)
        los_data = np.fromfile(los_file, dtype="<f4").reshape(nlat * 2, nlon)

        theta = np.flipud(los_data[0::2, :])
        phi = np.flipud(los_data[1::2, :])

        def los_is_degree():
            return np.abs(theta).max() > np.pi or np.abs(phi).max() > np.pi

        if not los_is_degree():
            raise ImportError(
                "The LOS file (%s) seems to be in radians! "
                "Change it to degree!" % op.basename(los_file)
            )

        phi[phi == 0.0] = np.nan
        theta[theta == 0.0] = np.nan

        phi *= d2r
        theta *= d2r

        phi = np.pi / 2 + phi
        theta = np.pi / 2 - theta

        container.phi = phi
        container.theta = theta

        return container

class GMTSAR(SceneIO):
    """
    Reading GMTSAR grid files.

    .. note ::

        Expects:

        * Displacement grid (NetCDF, :file:`*los_ll.grd`) in meter
          (in case use "gmt grdmath los_cm_ll.grd 0.01 MUL = los_m_ll.grd')
        * LOS binary data (see instruction, :file:`*.los.enu`)

    Calculate the corresponding unit look vectors with GMT5SAR ``SAT_look``:

    .. code-block:: sh

        gmt grd2xyz los_ll.grd | gmt grdtrack -Gdem.grd | \\
        awk {'print $1, $2, $4'} | \\
        SAT_look 20050731.PRM -bos > 20050731.los.enu
    """

    def validate(self, filename, **kwargs):
        try:
            if self._getDisplacementFile(filename)[-4:] == ".grd":
                return True
        except ImportError:
            return False
        return False

    def _getLOSFile(self, path):
        if not op.isdir(path):
            path = op.dirname(path)
        los_files = glob.glob(op.join(path, "*.los.*"))
        if len(los_files) == 0:
            self._log.warning(GMTSAR.__doc__)
            raise ImportError("Could not find LOS file (*.los.*)")
        los_file = los_files[0]
        self._log.debug("Found LOS file: %s", los_file)
        return los_file

    def _getDisplacementFile(self, path):
        if op.isfile(path):
            return path
        else:
            files = glob.glob(op.join(path, "*.grd"))
            if len(files) == 0:
                raise ImportError(
                    "Could not find displacement file " "(*.grd) at %s", path
                )
            disp_file = files[0]
        self._log.debug("Found Displacement file: %s", disp_file)
        return disp_file

    def read(self, path, **kwargs):
        from scipy.io import netcdf_file

        path = op.abspath(path)
        container = self.container

        grd = netcdf_file(self._getDisplacementFile(path), mode="r", version=2)
        displ = grd.variables["z"][:].copy()
        container.displacement = displ
        shape = container.displacement.shape
        # LatLon
        container.frame.spacing = "degree"
        container.frame.llLat = grd.variables["lat"][:].min()
        container.frame.llLon = grd.variables["lon"][:].min()

        container.frame.dN = (
            grd.variables["lat"][:].max() - container.frame.llLat
        ) / shape[0]
        container.frame.dE = (
            grd.variables["lon"][:].max() - container.frame.llLon
        ) / shape[1]

        # Theta and Phi
        try:
            los = np.memmap(self._getLOSFile(path), dtype="<f4")
            e = los[3::6].copy().reshape(shape)
            n = los[4::6].copy().reshape(shape)
            u = los[5::6].copy().reshape(shape)

            phi = np.arctan(n / e)
            theta = np.arcsin(u)
            # phi[n < 0] += np.pi

            container.phi = phi
            container.theta = theta
        except ImportError:
            self._log.warning(self.__doc__)
            self._log.warning("Defaulting theta to pi/2 and phi to 0.")
            container.theta = np.pi / 2
            container.phi = 0.0
        return container

class SARscape(SceneIO):
    """
    Reading SARscape :file:`*_disp` files.

    .. note ::

        Expects:

        * Header file in :file:`*_disp.hdr`
        * Displacement data in cm in :file:`*_disp`
        * LOS data in :file:`*disp_ILOS` and :file:`*disp_ALOS` files.
    """

    def read(self, filename, **kwargs):
        header = self.parseHeaderFile(filename)

        def load_data(filename):
            self._log.debug("Loading %s" % filename)
            return np.flipud(
                np.fromfile(filename, dtype=np.float32).reshape(
                    (header.lines, header.samples)
                )
            )

        displacement = load_data(filename)
        theta_file, phi_file = self.getLOSFiles(filename)

        if not theta_file:
            theta = np.full_like(displacement, 0.0)
        else:
            theta = load_data(theta_file)
            theta = np.deg2rad(theta)

        if not phi_file:
            phi = np.full_like(displacement, np.pi / 2)
        else:
            phi = load_data(phi_file)
            phi = np.pi / 2 - np.rad2deg(phi)

        container = self.container
        container.displacement = displacement
        container.phi = phi
        container.theta = theta

        map_info = header.map_info
        container.frame.dE = float(map_info[5])
        container.frame.dN = dN = float(map_info[6])
        container.frame.spacing = "meter"

        container.frame.llLat, container.frame.llLon = utm.to_latlon(
            float(map_info[3]),
            float(map_info[4]) - header.lines * dN,
            zone_number=int(map_info[7]),
            northern=True if map_info[8] == "Northern" else False,
        )

        return container

    def parseHeaderFile(self, filename):
        hdr_file = self._getHDRFile(filename)
        conf = re.compile(r"^(.+)\s+=\s+(.+)\n", re.MULTILINE)

        header = AttribDict()
        with open(hdr_file) as f:
            s = f.read()

            linebreaks = re.compile(r"{(.+)\n?(.+)}")
            s = linebreaks.sub(r"{ \g<1> \g<2> }", s)

            for match in conf.finditer(s):
                groups = match.groups()
                key = groups[0].strip().replace(" ", "_")
                value = groups[1].strip()
                try:
                    value = int(value)
                except ValueError:
                    pass

                header[key] = value

            header.map_info = header.map_info.strip("{} ").split(", ")
            if not len(header.map_info) == 11:
                raise HeaderError("`map info` header is not consistent!")
            if header.map_info[0] != "UTM":
                raise HeaderError("`map info` is not UTM!")

        return header

    def getLOSFiles(self, filename):
        ilos_file = op.abspath(filename + "_ILOS")
        if not op.exists(ilos_file):
            self._log.warning("Could not find ILOS file! (%s)" % ilos_file)
            ilos_file = False

        alos_file = op.abspath(filename + "_ALOS")
        if not op.exists(alos_file):
            self._log.warning("Could not find ALSO file! (%s)" % alos_file)
            alos_file = False
        return ilos_file, alos_file

    def _getHDRFile(self, filename):
        hdr_file = op.abspath(op.splitext(filename)[0] + ".hdr")
        if not op.exists(hdr_file):
            raise OSError("SARscape .hdr file not found (%s)" % hdr_file)
        return hdr_file

    def validate(self, filename, **kwargs):
        val = re.compile(r"SARscape|ENVI Standard", re.MULTILINE)
        try:
            hdr_file = self._getHDRFile(filename)
        except OSError:
            return False

        with open(hdr_file) as f:
            res = val.search(f.read())
            if res is not None:
                return True
            return False

class LiCSAR(SceneIO):
    """
    Import unwrapped Geotiffs from the
    `COMET LiCSAR Portal <https://comet.nerc.ac.uk/COMET-LiCS-portal/>`_.

    .. note ::

        Requires the Python package
        `gdal/osgeo <https://pypi.org/project/GDAL/>`_! Or through

        Expects:

        * Unwrapped geotiff in :file:`*.unw.tif`
        * LOS data in :file:`*.geo.[NEU].tif` files

    See also the download script in :mod:`kite.clients`.
    """

    def _getLOS(self, filename, component):
        path = op.dirname(filename)
        fn = glob.glob(op.join(path, component))
        if len(fn) != 1:
            raise ImportError("Cannot find LOS vector file %s!" % component)

        dataset = gdal.Open(fn[0], gdal.GA_ReadOnly)
        return self._readBandData(dataset)

    @staticmethod
    def _readBandData(dataset, band=1):
        band = dataset.GetRasterBand(band)
        array = band.ReadAsArray()
        array[array == band.GetNoDataValue()] = np.nan

        return np.flipud(array)

    def read(self, filename, **kwargs):
        dataset = gdal.Open(filename, gdal.GA_ReadOnly)
        georef = dataset.GetGeoTransform()

        llLon = georef[0]
        llLat = georef[3] + dataset.RasterYSize * georef[5]

        c = self.container

        c.frame.spacing = "degree"
        c.frame.llLat = llLat
        c.frame.llLon = llLon
        c.frame.dE = georef[1]
        c.frame.dN = abs(georef[5])

        displacement = self._readBandData(dataset)
        c.displacement = -displacement / (4 * np.pi) * LAMBDA_SENTINEL

        try:
            los_n = self._getLOS(filename, "*.geo.N.tif")
            los_e = self._getLOS(filename, "*.geo.E.tif")
            los_u = self._getLOS(filename, "*.geo.U.tif")
        except ImportError:
            self._log.warning(
                "Cannot find LOS angle files *.geo.[NEU].tif,"
                " using static Sentinel-1 descending angles."
            )

            heading = 83.0
            incident = 50.0

            un = np.sin(d2r * incident) * np.cos(d2r * heading)
            ue = np.sin(d2r * incident) * np.sin(d2r * heading)
            uz = np.cos(d2r * incident)

            los_n = np.full_like(c.displacement, un)
            los_e = np.full_like(c.displacement, ue)
            los_u = np.full_like(c.displacement, uz)

        c.phi = np.arctan2(los_n, los_e)
        c.theta = np.arcsin(los_u)

        c.meta.title = dataset.GetDescription()

        return c

    def validate(self, filename, **kwargs):
        if gdal.IdentifyDriver(filename) is None:
            return False
        return True

class ARIA(SceneIO):
    """
    Import unwrapped InSAR scenes from the
    `NASA/JPL ARIA <https://aria.jpl.nasa.gov/>`_ GUNW data products.

    .. note ::

        Requires the Python package
        `gdal/osgeo <https://pypi.org/project/GDAL/>`_! Or through

        Expects:

        * Extracted layers: unwrappedPhase, lookAngle, incidenceAngle,
          connectedComponents

    Use ``ariaExtract.py`` to extract the layers:

    .. code-block:: sh

        ariaExtract.py -w ascending -f aria-data.nc -d download \\
        -l unwrappedPhase,incidenceAngle,lookAngle

    """

    @staticmethod
    def _readBandData(dataset, band=1):
        band = dataset.GetRasterBand(band)
        array = band.ReadAsArray()
        if array.dtype != np.int16 and array.dtype != np.int:
            array[array == band.GetNoDataValue()] = np.nan

        return np.flipud(array)

    @staticmethod
    def _dataset_from_dir(folder):
        files = set(f for f in os.scandir(folder) if f.is_file())
        for f in files:
            if op.splitext(f.name)[-1] == "":
                break
        else:
            raise ImportError("could not load dataset from %s" % folder)

        return gdal.Open(f.path, gdal.GA_ReadOnly)

    def read(self, folder, **kwargs):
        unw_phase = self._dataset_from_dir(op.join(folder, "unwrappedPhase"))
        georef = unw_phase.GetGeoTransform()

        llLon = georef[0]
        llLat = georef[3] + unw_phase.RasterYSize * georef[5]

        c = self.container

        c.frame.spacing = "degree"
        c.frame.llLat = llLat
        c.frame.llLon = llLon
        c.frame.dE = georef[1]
        c.frame.dN = abs(georef[5])

        conn_comp = self._dataset_from_dir(op.join(folder, "connectedComponents"))

        displacement = self._readBandData(unw_phase)
        conn_mask = self._readBandData(conn_comp)  # Mask from snaphu
        displacement *= np.where(conn_mask, 1.0, np.nan)

        c.displacement = displacement / (4 * np.pi) * LAMBDA_SENTINEL

        inc_angle = self._dataset_from_dir(op.join(folder, "incidenceAngle"))
        azi_angle = self._dataset_from_dir(op.join(folder, "azimuthAngle"))

        c.theta = np.pi / 2 - self._readBandData(inc_angle) * d2r
        c.phi = self._readBandData(azi_angle) * d2r

        c.meta.scene_id = op.basename(unw_phase.GetDescription())
        c.meta.scene_title = c.meta.scene_id

        t_slave, t_master = c.meta.scene_id.split("_")
        c.meta.time_master = datetime(
            *time.strptime(t_master, "%Y%m%d")[:6]
        ).timestamp()
        c.meta.time_slave = datetime(*time.strptime(t_slave, "%Y%m%d")[:6]).timestamp()

        c.meta.satellite_name = "undefined (ARIA)"

        return c

    def validate(self, folder, **kwargs):
        expected_dirs = set(
            ["unwrappedPhase", "incidenceAngle", "lookAngle", "connectedComponents"]
        )
        if not op.isdir(folder):
            return False

        dirs = set(d.name for d in os.scandir(folder) if d.is_dir())
        if not expected_dirs - dirs:
            return True

        return False

class SNAP_Gamma(SceneIO):
    """SNAP import

    Reading geocoded displacement maps (unwrapped igs) originating
    from SNAP software using the export option.

    When georeferencing the scene, export the incidence angle from ellpsoid in
    the tab **Processing Parameters**.
    Export the unwrapped **displacement** band and
    **incidenceAngleFromEllipsoid** to Gamma format:

    1. File -> Export -> SAR Formats -> Gamma

    Select **Metadata -> Abstracted_Metadata** and
    2. File -> Export -> Other -> Product Metadata.

    .. note :: Expects:

        * [:file:`*`] Binary file from SNAP Gamma Export with displacement in
            radians
        * [:file:`*.Abstracted_Metadata.txt`] Metadata (parameter) file
            If you want to translate radians to
            meters using the `radar_frequency`.
        * [:file:`*par`] Parameter file, describing ``first_near_lat,
        last_near_long, num_output_lines, num_samples_per_line,
                lat_pixel_res, lon_pixel_res, radar_frequency and heading.``
        * [:file:`incidenceAngleFromEllipsoid.rslc`] Incidence angle file.

    .. warning ::

        * Data has to be georeferenced to latitude/longitude or UTM!
        * Look vector files - expected to have a particular name
    """

    @staticmethod
    def _parseParameterFile(filename):
        params = {}
        rc = re.compile(r"([\w]*)\s*([\w.+-]*)")
        with open(filename, mode="r") as par:
            for line in par:
                if line[:29] == "metadata.Abstracted_Metadata.":
                    parsed = rc.match(line[29:])
                    if parsed is None:
                        continue

                    groups = parsed.groups()
                    params[groups[0]] = safe_cast(
                        groups[1], float, default=groups[1].strip()
                    )
        return params

    def _getParameters(self, path, log=False):
        required_utm = (
            "post_north",
            "post_east",
            "corner_east",
            "corner_north",
            "num_output_lines",
            "num_samples_per_line",
        )
        required_lat_lon = (
            "first_near_lat",
            "last_near_long",
            "num_output_lines",
            "num_samples_per_line",
            "lat_pixel_res",
            "lon_pixel_res",
        )

        par_file = glob.glob(op.join(path, "*Abstracted_Metadata.txt"))
        for file in par_file:
            params = self._parseParameterFile(file)
            if check_required(required_utm, params) or check_required(
                required_lat_lon, params
            ):
                if not log:
                    self._log.info("Found parameter file %s" % file)
                return op.basename(file), params

        raise ImportError(
            "Parameter file %s does not hold required parameters" % par_file
        )

    def validate(self, filename, **kwargs):
        try:
            folder = op.dirname(op.abspath(filename))
            self._getParameters(folder)
            return True
        except ImportError:
            return False

    def _getLOSAngles(self, filename, pattern):
        path = op.dirname(op.realpath(filename))
        phi_files = glob.glob(op.join(path, pattern))
        if len(phi_files) == 0:
            self._log.warning(
                "Could not find LOS file %s, " "defaulting to angle to 0.", pattern
            )
            return 0.0
        elif len(phi_files) > 1:
            self._log.warning(
                "Found multiple LOS files %s, " "defaulting to angle 0.", pattern
            )
            return 0.0

        filename = phi_files[0]
        self._log.info("Loading LOS %s from %s" % (pattern, filename))
        return np.memmap(filename, mode="r", dtype=">f4")

    def read(self, filename, **kwargs):
        """
        :param filename: Gamma software parameter file
        :type filename: str
        :param par_file: Corresponding parameter (:file:`*par`) file.
                         (optional)
        :type par_file: str
        :returns: Import dictionary
        :rtype: dict
        :raises: ImportError
        """
        par_file, params = self._getParameters(
            op.dirname(op.abspath(filename)), log=True
        )

        ncols = int(params["num_samples_per_line"])
        nlines = int(params["num_output_lines"])
        radar_frequency = params.get("radar_frequency", None)
        heading_par = float(params.get("centre_heading", None))
        displ = np.fromfile(filename, dtype=">f4")

        # Resize array if last line is not scanned completely
        fill = 0
        if (displ.size % ncols) != 0:
            fill = np.empty(ncols - displ.size % ncols)
            fill.fill(np.nan)
            displ = np.append(displ, fill)

        displ = displ.reshape(nlines, ncols)
        displ[displ == -0.0] = np.nan
        displ = np.flipud(displ)

        if radar_frequency and "_dsp_" not in par_file:
            radar_frequency = float(radar_frequency)
            radar_frequency *= 1e6  # SNAP gives MHz
            self._log.info(
                "Scaling displacement by radar_frequency %f GHz", radar_frequency / 1e9
            )
            wavelength = util.C / radar_frequency
            displ /= -4 * np.pi
            displ *= wavelength

        elif not radar_frequency and "_dsp_" not in par_file:
            self._log.warning("Could not determine radar_frequency!")
            wavelength = None

        else:
            wavelength = None

        inc_angle = self._getLOSAngles(
            filename, "incidenceAngleFromEllipsoid.rslc"
        ).copy()
        if fill:
            inc_angle = np.append(inc_angle, fill)
        inc_angle[inc_angle == 0.0] = np.nan

        phi = np.full_like(displ, (180.0 - heading_par))
        theta = 90.0 - inc_angle.reshape(displ.shape)
        theta = np.flipud(theta)

        c = self.container

        c.displacement = displ
        c.theta = theta * d2r
        c.phi = phi * d2r

        c.meta.wavelength = wavelength
        c.meta.title = params.get("PRODUCT", "SNAP Import")
        c.meta.satellite_name = params.get("SPH_DESCRIPTOR", "None")

        orb = params.get("PASS", None)
        c.meta.orbital_node = orb.title() if orb else None

        c.bin_file = filename
        c.par_file = par_file

        if params["map_projection"] == "UTM":
            utm_zone = params["projection_zone"]
            try:
                utm_zone_letter = utm.latitude_to_zone_letter(params["center_latitude"])
            except ValueError:
                self._log.warning(
                    "Could not parse UTM Zone letter," " defaulting to N!"
                )
                utm_zone_letter = "N"

            self._log.info("Using UTM reference: Zone %d%s", utm_zone, utm_zone_letter)
            c.frame.spacing = "meter"

            dN = abs(params["post_north"])
            dE = abs(params["post_east"])

            utm_corn_e = params["corner_east"]
            utm_corn_n = params["corner_north"]

            utm_corn_eo = utm_corn_e + dE * displ.shape[1]
            utm_corn_no = utm_corn_n + dN * displ.shape[0]

            utm_e = np.linspace(utm_corn_e, utm_corn_eo, displ.shape[1])
            utm_n = np.linspace(utm_corn_n, utm_corn_no, displ.shape[0])

            llLat, llLon = utm.to_latlon(
                utm_e.min(), utm_n.min(), utm_zone, utm_zone_letter
            )

        else:
            self._log.info("Using Lat/Lon reference")
            c.frame.spacing = "degree"

            if orb.lower() == "ascending":
                llLat = params["last_near_lat"]
                llLon = params["last_near_long"]

            elif orb.lower() == "descending":
                llLat = params["last_far_lat"]
                llLon = params["first_near_long"]

            else:
                raise AttributeError("cannot determine orbit")

            dE = abs(params["lon_pixel_res"])
            dN = abs(params["lat_pixel_res"])

        c.frame.llLat = llLat
        c.frame.llLon = llLon

        c.frame.dE = dE
        c.frame.dN = dN

        return c


"""A basic pong game to apply OOP concepts like classes, inheritance, and modular programming."""
import time
from turtle import Screen

from ball import Ball
from paddle import Paddle
from scoreboard import Scoreboard

GAME = True

screen = Screen()
screen.setup(width=800, height=600)
screen.bgcolor("#000000")
screen.title("Pong")
screen.tracer(0)

right = Paddle((350, 0))
left = Paddle((-350, 0))
ball = Ball()
scoreboard = Scoreboard()

screen.listen()
screen.onkeypress(key="Up", fun=right.up)
screen.onkeypress(key="Down", fun=right.down)
screen.onkeypress(key="w", fun=left.up)
screen.onkeypress(key="s", fun=left.down)

while GAME:
    time.sleep(0.1)
    screen.update()
    ball.move()

    if ball.ycor() < -280 or ball.ycor() > 280:
        ball.bounce_y()

    if ball.distance(right) < 50 and ball.xcor() > 320 or ball.distance(left) < 50 and ball.xcor() < -320:
        ball.bounce_x()

    if ball.xcor() > 380:
        scoreboard.left += 1
        scoreboard.display()
        ball.reset()

    if ball.xcor() < -380:
        scoreboard.right += 1
        scoreboard.display()
        ball.reset()

screen.exitonclick()


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu Sep 17 03:00:22 2020

@author: gimjiwon
"""

class node():

    def __init__(self, data):
        self.data = data
        self.next = None

    def __str__(self):
        return self.data.__str__()

class LinkedList():

    def __init__(self):
        self.head = None
        self.tail = None
        self.count = 0

    def __str__(self):  # [1,2,3]
        q = self.head
        ret_str = '['
        while q != None:
            ret_str += q.__str__()
            # ret_str += q.data.__str__()
            if q.next != None: ret_str += ', '
            q = q.next

        return ret_str + ']'

    # 노드 추가 함수
    def append_node(self, data):
        # 추가될 노드 생성
        new_node = node(data)

        if self.head == None:  # 첫번째 node
            self.head = new_node
            self.tail = new_node
        else:  # 기존의 노드들이 있을 경우 제일 끝에 추가
            self.tail.next = new_node
            self.tail = new_node
        self.count += 1
        return

    def extend_list(self, aList):
        # 추가하려는 링크드 리스트가 비었을 때 그냥 리턴
        if aList.head == None: return

        # 추가하려는 링크드 리스트 복사
        new_l = LinkedList()
        q = aList.head
        while q != None:
            new_l.append_node(q.data)
            q = q.next

        # 기존의 링크드리스트가 비었을 경우 헤드로...
        if self.head == None:
            self.head = new_l.head

        # 기존의 링크드 리스트의 테일노드가 있을경우
        if self.tail != None:
            # tail 뒤에 추가해준다
            self.tail.next = new_l.head

        # 기존 tail값 변경
        self.tail = new_l.tail

        # 새로 추가된 만큼 더해준다.
        self.count += new_l.count
        return

    # 노드 개수 리턴해주는 함수
    def get_count(self):
        return self.count

    # 첫번째 타겟 엘리먼트 삭제
    def remove_first(self, data):
        # 타깃을 찾기위함 item변수할당
        item = self.head
        # 기존 노드가 비었을 경우
        if item == None:
            print("no item")
            return
        else:
            # prev값 생성
            prev = None
            # item에 값이 있을동안 찾는다(처음부터 끝까지)
            while item:
                # 타깃을 찾은 경우
                if item.data == data:
                    # case 1 타깃이 테일
                    if item == self.tail:
                        temp3 = self.tail
                        self.tail = prev
                        self.tail.next = None
                        self.count -= 1
                        del temp3
                        return

                    # case 2 타깃이 헤드
                    if item == self.head:
                        temp2 = self.head
                        self.head = self.head.next
                        self.count -= 1
                        del temp2
                        return

                    # case 3 타깃이 중간에 있음
                    else:
                        temp = item
                        prev.next = item.next
                        self.count -= 1
                        del temp
                        return
                # 다음 타깃을 찾아 떠나서..
                else:
                    prev = item
                    item = item.next
            # 맞는 타깃이 없을 경우
            else:
                print("no data")
                return

    # 모 타겟 엘리먼트 삭제
    def remove(self, item):

        if self.head == None:
            return

        # head가 내가 지워야 하는 item인 경우...
        while self.head and self.head.data == item:
            self.head = self.head.next
            self.count -= 1

        if self.head == None:
            self.tail = None
            return

        p = self.head.next  # 앞서서 지울 노드를 검사
        q = self.head  # next연결을 위해서 p의 바로 앞노드를 pointing
        while p:
            if p.data == item:
                # 여기는 여러분들이 채워주세요
                # p는 앞쪽으로 한칸 이동
                # q의 next는 p의 next로
                self.count -= 1
                if p == self.tail:  # 대상이 마지막 노드일 경우 tail을 바꾸어주고 break..
                    del p
                    q.next = None
                    self.tail = q
                    break

                # 중간 노드일 경우
                else:
                    # q.next = p.next
                    # p = p.next.next
                    tmp = p
                    p = p.next  # 삭제될 다음노드로 이동
                    q.next = p  # 건너뛰고 이어줌(삭제)
                    del tmp

            else:
                p = p.next
                q = q.next

if __name__ == '__main__':
    a = LinkedList()
    a.append_node(1)
    a.append_node(1)
    a.append_node(1)
    a.append_node(2)
    a.append_node(3)
    print("a :", a)
    print("the number of a is ", a.get_count())
    print()
    b = LinkedList()
    b.append_node('a')
    b.append_node('b')
    print("b : ", b)
    a.extend_list(b)
    print("a+b : ", a)
    print("the number of b is ", b.get_count())
    print("the number of a = a+b is ", a.get_count())
    print()
    b.append_node('c')

    print("c appended b : ", b)
    print("the number of b is ", b.get_count())
    print("check extended a :", a)
    print("the number of a is ", a.get_count())
    print()
    a.remove_first(1)
    print("first 1 removed a : ", a)
    print("the number of a is ", a.get_count())
    print()
    a.append_node(1)
    a.append_node(2)
    a.append_node(3)
    a.append_node(1)

    print("1,2,3,1 appended a : ", a)
    print("the number of a is ", a.get_count())
    a.remove(1)
    print()

    print("all 1 removed a : ", a)
    print("the number of a is ", a.get_count())
    print()

    a.append_node(1)
    print("1 appended a : ", a)
    print("the number of a is ", a.get_count())


import vapoursynth as vs
import math

#resize to 1080 in order to compare quality through vmaf
#adding borders if needed
def resize_to_1080(video):
	height=video.height
	width=video.width
	if width/height >= 1920.0/1080.0:
		newheight=int(1920.0*height/width)
		borderup=int((1080.0-newheight)/2)
		if borderup % 2 == 1:
			borderup+=1
		borderdown=1080-borderup-newheight
		video=core.resize.Bicubic(clip=video,width=1920,height=newheight)
		video=core.std.AddBorders(clip=video,top=borderup,bottom=borderdown)
	else:
		newwidth=int(1080.0*width/height)
		borderleft=int((1920.0-newwidth)/2)
		if borderleft % 2==1:
			borderleft+=1
		borderright=1920-borderleft-newwidth
		video=core.resize.Bicubic(clip=video,width=newwidth,height=1080)
		video=core.std.AddBorders(clip=video,left=borderleft,right=borderright)
	return video

#compressibility check to keep 5% of the video in 30 seconds clips,
#spread equally to all video.
def compressibility_check(video):
	percentage = 5.0
	clipsduration=30.0

	clipframes=math.ceil(clipsduration*video.fps_num/video.fps_den)
	compressibilitiframes = math.ceil(percentage * video.num_frames / 100)
	numberofclips=math.floor(compressibilitiframes/clipframes)

	if numberofclips*clipframes/video.num_frames < (percentage/100):
		numberofclips+=1

	big_part_length=math.ceil(video.num_frames/numberofclips)
	center=math.floor(big_part_length/2)

	clips=[]
	for i in range(numberofclips):
	  start_point=big_part_length*i+center-math.ceil(clipframes/2)
	  endpoint=big_part_length*i+center+math.ceil(clipframes/2)
	  clip=video[start_point:endpoint]
	  clips.append(clip)

	compressclip=clips[0]
	for i in range(1,numberofclips):
	  compressclip=compressclip+clips[i]
	return compressclip

core = vs.get_core()
source = core.ffms2.Source(r'movie_original.mkv')
source=compressibility_check(source)
#source=resize_to_1080(source)

compressed=core.ffms2.Source(r'30_slow_half.mkv')
#compressed=core.std.AddBorders(clip=compressed,top=130,bottom=124)
compressed=resize_to_1080(compressed)

video3=core.vmaf.VMAF(source, compressed,  log_path="30_slow_half.xml", log_fmt=0, pool=1,ci=True)
video3.set_output()


# -*- coding: utf-8 -*-
"""
Created on Fri Oct 25 11:55:24 2019

@author: samer
"""

import os
directory_path = 'C:\\Users\\samer\\Desktop'
list_of_files = os.listdir(directory_path)
print(list_of_files)


# -*- coding: utf-8 -*-

from odoo import models, fields, api

class Ticket_type(models.Model):
    _name = 'ticket.type'

    name = fields.Char(string='Name')

    _sql_constraints = [
                     ('name',
                      'unique(name)',
                      'Item Name Already Exist!')
    ]


# Python Libraries
import json

# Django / Third-party Libraries

# Stx Libraries
from src.libs.aws_postman import AWSPostman

def lambda_handler(event, context):
	postman = AWSPostman()
	postman.add_Sender('stxtest2021@gmail.com')
	postman.add_Recipient('francisco.germain@gmail.com')
	data = eval(event['body'])
	subject = f"Contacto MISEVER"
	text = f"Se ha recibido la siguiente informacion en tu\
		formulario de contacto:\n\
		Nombre:{data['name']} Correo:{data['correo']}\
		Teléfono:{data['telefono']} mensaje:{data['mensaje']}"
	postman.send_Email(subject, text)
	return {
		"statusCode": 200,
		"headers": {
		"Content-Type": "application/json"
		},
		"body": json.dumps({
		"detail ": "Envío Exitoso!"
		})
	}


# encoding:utf-8

# @Author: Rilzob
# @Time: 2018/12/9 上午9:46

from Experiment.Lexer import Lexer
from utils import load_file

import sys

class Parser(Lexer):  # 递归下降分析法
    def __init__(self):
        super().__init__()
        sourcecode = load_file('/Users/rilzob/PycharmProjects/CompileFrontEnd/Experiment/Test3.txt')
        # sourcecode = load_file('/Users/rilzob/PycharmProjects/CompileFrontEnd/Exercise/SourceCode.txt')
        self.lexer_scanner(sourcecode)
        self.worditer = iter(self.wordlist)
        self.word = next(self.worditer)
        self.SEM = []  # 语义栈
        self.SYN = []  # 语法栈
        self.result = []  # 暂存表达式结果
        self.QT = []
        # self.keyword = '_'  # 暂存keyword的值
        self.id = '_'  # 暂存id的值
        self.constant = '_'  # 暂存constant的值
        self.currentwordlist = []
        self.currentword = ''
        self.i = 1
        self.operator = ''
        self.vartype = ''  # 暂存声明语句中标识符的type
        self.varname = ''  # 暂存声明语句中标识符的name
        self.varvalue = ''  # 暂存声明语句中标识符的值
        self.record = []  # 记录符号的信息
        self.SYNBL = []  # 符号表总表

    def is_id(self):
        if self.word in self.iT:
            return True
        else:
            return False

    def varinit(self):
        self.varname = ''
        self.vartype = ''
        self.varvalue = ''
        self.record = []

    def id_in_SYNBL(self, name):
        for record in self.SYNBL:
            if name == record[0]:
                return True
        return False

    def _statement(self):  # 声明语句
        if not self._type():
            return False
        if self.is_id():
            self.id = self.word
            self.varname = self.word
            self.word = next(self.worditer)
            if self.word == '=':
                self.word = next(self.worditer)
                if not self._constant():
                    return False
                # self.varvalue = self.word
            self.record.append(self.varname)
            self.record.append(self.vartype)
            self.record.append(self.varvalue)
            self.SYNBL.append(self.record)
            # 将该变量添加到符号表中
            self.varinit()
            return True
        else:
            print("Error1")
            return False

    def _constant(self):
        if self.word in self.CT:  # 判断是否是num
            self.constant = self.word
            self.varvalue = self.word
            self.word = next(self.worditer)
            return True
        elif self.word in self.cT or self.word in self.sT:  # 判断是否是string
            self.constant = self.word
            self.varvalue = self.word
            self.word = next(self.worditer)
            return True
        else:
            print("Error4")
            return False

    def _type(self):
        if self.word == 'int' or self.word == 'float' or self.word == 'char':
            self.vartype = self.word
            self.word = next(self.worditer)
            return True
        else:
            print("Error5")
            return False

    def _assignment(self):  # 赋值语句
        if self.is_id():
            self.id = self.word
            if self.id_in_SYNBL(self.id):
                self.word = next(self.worditer)
                if self.word == '=':
                    self.word = next(self.worditer)
                    if not self._expression():
                        return False
                    # self.QT.append('(' + '=' + ',' + str(self.SEM.pop()) + ',' + '_' + ',' + str(self.id) + ')')
                    quaternary = []
                    quaternary.append('=')
                    quaternary.append(str(self.SEM.pop()))
                    quaternary.append('_')
                    quaternary.append(str(self.id))
                    self.QT.append(quaternary)
                    self.id = '_'  # 重新初始化
                    return True
                else:
                    print("Error7")
                    return False
            else:
                print("符号表没有该变量，该变量没有声明")
                return False
        else:
            print("Error6")
            return False

    # def is_i(self, word):  # 标识符
    #     if (word in self.iT) or (word in self.CT):
    #         return True
    #     else:
    #         return False

    def judge_F(self):
        if self.is_id() or (self.word in self.CT):
            if self.is_id():
                if not self.id_in_SYNBL(self.word):
                    print("不在符号表中，变量%s未声明" % str(self.word))
                    return False
            self.SYN.append('PUSH(' + str(self.word) + ')')
            self.word = next(self.worditer)
            return True
        elif self.word == '(':
            self.word = next(self.worditer)
            self.judge_E()
            if self.word == ')':
                self.word = next(self.worditer)
                return True
            else:
                print("Error10")
                return False
        else:
            print("Error9")
            return False

    def judge_T(self):
        if self.judge_F():
            while True:
                try:
                    if self.word in ['*', '/']:
                        self.currentwordlist.append(self.word)
                        self.word = next(self.worditer)
                        if self.judge_F():
                            self.currentword = self.currentwordlist.pop()
                            if self.currentword == '*':
                                self.SYN.append('GEQ(*)')
                            else:
                                self.SYN.append('GEQ(/)')
                            continue
                        else:
                            return False
                    return True
                except StopIteration:
                    print("结束1")
                    sys.exit()
        else:
            return False

    def judge_E(self):
        if self.judge_T():
            while True:
                try:
                    if self.word in ['+', '-']:
                        self.currentwordlist.append(self.word)
                        self.word = next(self.worditer)
                        if self.judge_T():
                            self.currentword = self.currentwordlist.pop()
                            if self.currentword == '+':
                                self.SYN.append('GEQ(+)')
                            else:
                                self.SYN.append('GEQ(-)')
                            continue
                        else:
                            return False
                    return True
                except StopIteration:
                    print("结束2")
                    sys.exit(0)
        else:
            return False

    def _expression(self):
        if not self.judge_E():
            return False
        else:
            for currentstr in self.SYN:
                if currentstr.startswith('PUSH'):
                    self.SEM.append(currentstr.lstrip('PUSH(').rstrip(')'))
                elif currentstr.startswith('GEQ'):
                    char1 = self.SEM.pop()
                    char2 = self.SEM.pop()
                    self.SEM.append('t' + str(self.i))
                    # self.result.append('t' + str(self.i))
                    # self.QT.append('(' + currentstr.lstrip('GEQ(').rstrip(')') + ',' + char2 + ','
                    #                + char1 + ',' + 't' + str(self.i) + ')')
                    string = 't' + str(self.i)
                    quaternary = []
                    quaternary.append(currentstr.lstrip('GEQ(').rstrip(')'))
                    quaternary.append(char2)
                    quaternary.append(char1)
                    quaternary.append(string)
                    self.QT.append(quaternary)
                    self.i += 1
            self.SYN = []  # 重新初始化
            self.currentwordlist = []
            self.currentword = ''
            return True

    def _condition(self):
        if self.is_id():
            self.SEM.append(str(self.word))
            self.word = next(self.worditer)
        elif not self._expression():
            return False
        if self.word in ['<', '>', '|', '&', '<=', '>=']:
            self.operator = self.word
            self.word = next(self.worditer)
            if self.is_id():
                self.SEM.append(str(self.word))
                self.word = next(self.worditer)
            elif not self._expression():
                return False
            # self.QT.append('(' + str(self.operator) + ',' + str(self.SEM.pop()) + ',' + str(self.SEM.pop()) + ',' + 't'
            #                + str(self.i) + ')')
            string = 't' + str(self.i)
            quaternary = []
            quaternary.append(str(self.operator))
            quaternary.append(str(self.SEM.pop()))
            quaternary.append(str(self.SEM.pop()))
            quaternary.append(string)
            self.QT.append(quaternary)
            self.i += 1
            self.operator = ''
            return True
        else:
            return True

    def is_type(self):
        if self.word == 'int' or self.word == 'float' or self.word == 'char':
            return True
        else:
            return False

    def _sentence(self):
        if self.is_type():
            if not self._statement():
                return False
            if self.word == ';':
                # self.QT.append('(' + 'init' + ',' + str(self.constant) + ',' + '_' + ',' + str(self.id) + ')')
                quaternary = []
                quaternary.append('init')
                quaternary.append(str(self.constant))
                quaternary.append('_')
                quaternary.append(str(self.id))
                self.QT.append(quaternary)
                self.constant = '_'  # 重新初始化
                self.id = '_'
                self.word = next(self.worditer)
                return True
            else:
                print("Error15")
                return False
        elif self.is_id():
            if not self._assignment():
                return False
            if self.word == ';':
                self.word = next(self.worditer)
                return True
            else:
                print("Error16")
                return False
        elif self._expression():
            if self.word == ';':
                self.word = next(self.worditer)
                return True
            else:
                print("Error17")
                return False
        else:
            print("Error11")
            return False

    def _ifelsecontrol(self):
        if self.word == 'if':
            self.word = next(self.worditer)
            if not self._condition():
                return False
            # self.QT.append('(' + 'if' + ',' + 't' + str(self.i - 1) + ',' + '_' + ',' + '_' + ')')
            string = 't' + str(self.i - 1)
            quaternary = []
            quaternary.append('if')
            quaternary.append(string)
            quaternary.append('_')
            quaternary.append('_')
            self.QT.append(quaternary)
            if self.word == '{':
                self.word = next(self.worditer)
                while True:
                    if self.word == '}':
                        break
                    elif not self._sentence():
                        return False
                if self.word == '}':
                    self.word = next(self.worditer)
                    if self.word == 'else':
                        # self.QT.append('(' + 'el' + ',' + '_' + ',' + '_' + ',' + '_' + ')')
                        quaternary = []
                        quaternary.append('el')
                        quaternary.append('_')
                        quaternary.append('_')
                        quaternary.append('_')
                        self.QT.append(quaternary)
                        self.word = next(self.worditer)
                        if self.word == '{':
                            self.word = next(self.worditer)
                            while True:
                                if self.word == '}':
                                    break
                                elif not self._sentence():
                                    return False
                            if self.word == '}':
                                # self.QT.append('(' + 'ie' + ',' + '_' + ',' + '_' + ',' + '_' + ')')
                                quaternary = []
                                quaternary.append('ie')
                                quaternary.append('_')
                                quaternary.append('_')
                                quaternary.append('_')
                                self.QT.append(quaternary)
                                self.word = next(self.worditer)
                                return True
                            else:
                                print("Error18")
                                return False
                        else:
                            print("Error19")
                            return False
                    else:
                        # self.QT.append('(' + 'ie' + ',' + '_' + ',' + '_' + ',' + '_' + ')')
                        quaternary = []
                        quaternary.append('ie')
                        quaternary.append('_')
                        quaternary.append('_')
                        quaternary.append('_')
                        self.QT.append(quaternary)
                        return True
                else:
                    print("Error12")
                    return False
            else:
                print("Error13")
                return False
        else:
            print("Error14")
            return False

    def _whilecontrol(self):
        if self.word == 'while':
            # self.QT.append('(' + 'wh' + ',' + '_' + ',' + '_' + ',' + '_' + ')')
            quaternary = []
            quaternary.append('wh')
            quaternary.append('_')
            quaternary.append('_')
            quaternary.append('_')
            self.QT.append(quaternary)
            self.word = next(self.worditer)
            if not self._expression():
                return False
            if self.word == '{':
                self.word = next(self.worditer)
                if not self._sentence():
                    return False
                if self.word == '}':
                    self.word = next(self.worditer)
                    # self.QT.append('(' + 'we' + ',' + '_' + ',' + '_' + ',' + '_' + ')')
                    quaternary = []
                    quaternary.append('we')
                    quaternary.append('_')
                    quaternary.append('_')
                    quaternary.append('_')
                    self.QT.append(quaternary)
                    return True
                else:
                    print("Error20")
                    return False
            else:
                print("Error21")
                return False
        else:
            print("Error22")
            return False

    def _forcontrol(self):
        if self.word == 'for':
            self.word = next(self.worditer)
            if self.word == '(':
                self.word = next(self.worditer)
                if not self._assignment():
                    return False
                if self.word == ';':
                    self.word = next(self.worditer)
                    if not self._expression():
                        return False
                    if self.word == ';':
                        self.word = next(self.worditer)
                        if not self._expression():
                            return False
                        if self.word == ')':
                            self.word = next(self.worditer)
                            if self.word == '{':
                                self.word = next(self.worditer)
                                if not self._sentence():
                                    return False
                                if self.word == '}':
                                    self.word = next(self.worditer)
                                    return True
                                else:
                                    print("Error23")
                                    return False
                            else:
                                print("Error24")
                                return False
                        else:
                            print("Error25")
                            return False
                    else:
                        print("Error26")
                        return False
                else:
                    print("Error27")
                    return False
            else:
                print("Error28")
                return False
        else:
            print("Error29")
            return False

    def _control(self):
        if self.word == 'if':
            if not self._ifelsecontrol():
                return False
            else:
                return True
        elif self.word == 'while':
            if not self._whilecontrol():
                return False
            else:
                return True
        elif self.word == 'for':
            if not self._forcontrol():
                return False
            else:
                return True
        else:
            print("Error30")
            return False

    def _main(self):
        while True:
            try:
                if self.is_id() or self.is_type():
                    if self._sentence():
                        continue
                    else:
                        return False
                elif self.word in ['if', 'while', 'for']:
                    if self._control():
                        continue
                    else:
                        return False
                else:
                    return False
            except StopIteration:
                print("符合语法")
                # sys.exit(0)


from sqlalchemy import *
from migrate import *

from migrate.changeset import schema
pre_meta = MetaData()
post_meta = MetaData()
flavor__types = Table('flavor__types', post_meta,
    Column('id', Integer),
    Column('name', String(length=64), primary_key=True, nullable=False),
    Column('vcpus', Integer),
    Column('memory_mb', Integer),
    Column('disk_gb', Integer),
)

def upgrade(migrate_engine):
    # Upgrade operations go here. Don't create your own engine; bind
    # migrate_engine to your metadata
    pre_meta.bind = migrate_engine
    post_meta.bind = migrate_engine
    post_meta.tables['flavor__types'].create()

def downgrade(migrate_engine):
    # Operations to reverse the above upgrade go here.
    pre_meta.bind = migrate_engine
    post_meta.bind = migrate_engine
    post_meta.tables['flavor__types'].drop()


from django.urls import path, include
from . import views
app_name='account'
urlpatterns = [

    path('<int:seller_id>',views.profile,name='profile'),
    path('<int:prod_id>/product/',views.product_detail,name='product_detail'),
    path('add_product',views.add_product,name='add_product'),
    path('add_address',views.add_address,name='add_address'),
    path('add_address2',views.add_address2,name='add_address2'),
    path('account', views.account, name='account'),
    path('basket',views.view_basket,name='basket'),
    path('<int:item_id>/addToBasket/',views.add_to_basket,name='add_to_basket'),
    path('<int:item_id>/removeBasket/',views.remove_from_basket,name='remove_from_basket'),
    path('checkout',views.checkout,name='checkout'),
    path('<int:order_id>/orderSummary/',views.orderSummary,name='orderSummary'),
    path('orderHistory',views.orderHistory,name='orderHistory'),
    path('<int:item_id>/updateProduct/',views.updateProduct,name='updateProduct'),
    path('<int:address_id>/updateAddress/',views.updateAddress,name='updateAddress'),
    path('<int:item_id>/deleteItem/',views.deleteItem,name='deleteItem'),
    path('<int:address_id>/deleteAddress/',views.deleteAddress,name='deleteAddress'),
    path('availability',views.change_availability,name='available'),
]


from fastapi import FastAPI, HTTPException
from movie import get_movie_recommendation
from mangum import Mangum
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()
handler = Mangum(app)
MAX_INPUT_LENGTH = 50

# adding headers to responses for Cross-Origin Response Blocked error
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.get("/movies")
async def generate_snippet_api(prompt: str):
    validate_input_length(prompt)
    snippet = get_movie_recommendation(prompt)
    return {"snippet": snippet, "keywords": []}

def validate_input_length(prompt: str):
    if len(prompt) >= MAX_INPUT_LENGTH:
        raise HTTPException(
            status_code=400,
            detail=f"Input length is too long. Must be under {MAX_INPUT_LENGTH} characters.",
        )


import numpy as np

# = = = NOTE: Below is a sample PRINT STRIDE FILE data
#! FIELDS time q.w q.x q.y q.z rest0.bias
# 0.000000 0.312824 0.361795 -0.802215 -0.357347 9.982789

# There are N columns, each of which is spaced.
# So, we should read the first field line as a header-designator.
# This needes to be before any data, or else the function should abort.

# It should be noted that PLUMED defaults to single precision.
# Therefore, we are using np.float32 as the conversion.

# This function reads in a PLUMED output done with the PRINT command.
# It expects files of the following form:
# #! FIELDS field1 field2 ...
# entry1 entry2
# entry3 entry4
# ...
#
# The function will check for internal consistency, and return a list of two arrays :
# [ field_names(nfields), parsed_data(nfields, nentries) ]
# The parsed data array is in F-ordering.
def read_from_plumedprint(fname):
    bHeaderRead=False
    #get number of lines and read headers
    ncomm=0
    nempty=0
    ndata=0
    nfields=0
    field_names=[]
    parsed_data=[]
    with open(fname) as fp:
        for ntot, line in enumerate(fp):
            if line == '\n':
                nempty=nempty+1
                continue
            if line.startswith("#"):
                ncomm=ncomm+1
                l = line.split()
                # Check if this comment line is a header line with FIELD.
                if l[1]=="FIELDS":
                    if bHeaderRead:
                        field_names_comp=[ l[i] for i in range(2,len(l)) ]
                        for a,b in zip(field_names, field_names_comp):
                            if a != b:
                                print( '= = ERROR: Multiple FIELD headers are present to indicate parallel trajectoreies, but their entries do not agree!' )
                                print( field_names )
                                print( field_names_comp )
                                return -1
                    else:
                        field_names=[ l[i] for i in range(2,len(l)) ]
                        nfields=len(field_names)
                        bHeaderRead=True
                continue

            # The default behaviour is now a data line.
            if not bHeaderRead:
                print( '= = ERROR: Data-like line encountered before a FIELDS definition! Line as follows:' )
                print( line )
                return -1
            l = line.split()
            if len(l) != nfields:
                print( '= = ERROR: Data-like line does not have the same number of fields as defined in FIELDS! ( %i )' % (nfields) )
                print( l )
                return -1
            for i in range(len(l)):
                parsed_data.append(np.float32(l[i]))

    #Add one to enumerate output, then remove counts for comments and empty lines
    ndata=ntot+1-ncomm-nempty
    print( '= = Input file %s has been read: Found %i data-like lines in input plumed FES file, with %i comment lines. ' % (fname, ndata, ncomm) )
    if nempty > 0:
        print( '= = = NOTE: There are %i empty lines' % nempty )
    print( '= = = %i field entries discovered. Field entries are as follows:' % nfields )
    print( str(field_names).strip('[]') )

    # Now reshape parsed_data to match the lines read and the total number of fields.
    parsed_data=np.reshape(parsed_data, (nfields,ndata), order='F')

    return field_names, parsed_data

def read_from_plumedprint_multi(fname):
    bHeaderRead=False
    #get number of lines and read headers
    ncomm=0
    nempty=0
    ndata=0
    nfields=0
    nchunks=0
    field_names=[]
    output_data=[]
    parsed_data=[]
    with open(fname) as fp:
        for ntot, line in enumerate(fp):
            if line == '\n':
                nempty=nempty+1
                continue
            if line.startswith("#"):
                ncomm=ncomm+1
                l = line.split()
                # Check if this comment line is a header line with FIELD.
                if l[1]=="FIELDS":
                    nchunks += 1
                    nf=len(l)
                    temp=[]
                    for i in range(2,nf):
                        temp.append(l[i])
                    nfields=len(temp)
                    field_names.append(temp)
                    bHeaderRead=True
                    #Copy previous data-chunk to a new item
                    if len(parsed_data) != 0:
                        output_data.append(parsed_data)
                        parsed_data=[]
                continue

            # The default behaviour is now a data line.
            if not bHeaderRead:
                print( '= = ERROR: Data-like line encountered before a FIELDS definition! Line as follows:' )
                print( line )
                return -1
            l = line.split()
            if len(l) != nfields:
                print( '= = ERROR: Data-like line does not have the same number of fields as defined in FIELDS!' )
                return -1
            floats=[float(x) for x in l]
            parsed_data.append(floats)

    #Add last bit to overall array.
    output_data.append(parsed_data)

    #Add one to enumerate output, then remove counts for comments and empty lines
    ndata=ntot+1-ncomm-nempty
    print( '= = Input file %s has been read: Found %i data-like lines in input plumed FES file, with %i comment lines. ' % (fname, ndata, ncomm) )
    if nempty > 0:
        print( '= = = NOTE: There are %i empty lines' % nempty )
    print( '= = = %i * %i field entries discovered. Field entries are as follows:' % (nchunks, nfields) )
    print( field_names )

    # Now reshape parsed_data to match the lines read and the total number of fields.
    #parsed_data=np.reshape(parsed_data, (nfields,ndata), order='F')

    return field_names, np.array(output_data)

# This function reverses the read function and outputs a basic PLUMED PRINT file.
#
# It is intended for bug-checking and hacking imitation PLUMED outputs from python.
def write_to_plumedprint(fname, field_names, data):
    # Surrogate print( function, useful for things like mimicking HILLS files and others. )

    #Consistency checks first.
    shape=data.shape
    nfields=len(field_names)
    if shape[0] != nfields:
        print( '= = ERROR: in function write_to_plumedprint, the number of fields do not match between the input data (%i) and field lists (%i)!' % (shape[0], nfields) )
        return -1

    fp=open(fname,'w')
    # Input HEADER data.
    print( "#! FIELDS "+" ".join(field_names), file=fp )

    for i in range(shape[1]):
        print( " ".join("%8f" % data[j][i] for j in range(shape[0])), file=fp )
    fp.close()
    print( '= = File %s has been written.' % fname )
    return


import os
import re
import glob
import h5py
import random
import math
import logging
import numpy as np
import os.path as osp
from scipy.io import loadmat
from tools.utils import mkdir_if_missing, write_json, read_json

class LTCC(object):
    """ LTCC

    Reference:
        Qian et al. Long-Term Cloth-Changing Person Re-identification. arXiv:2005.12633, 2020.

    URL: https://naiq.github.io/LTCC_Perosn_ReID.html#
    """
    dataset_dir = 'LTCC_ReID'
    def __init__(self, root='data', **kwargs):
        self.dataset_dir = osp.join(root, self.dataset_dir)
        self.train_dir = osp.join(self.dataset_dir, 'train')
        self.query_dir = osp.join(self.dataset_dir, 'query')
        self.gallery_dir = osp.join(self.dataset_dir, 'test')
        self._check_before_run()

        train, num_train_pids, num_train_imgs, num_train_clothes, pid2clothes = \
            self._process_dir_train(self.train_dir)
        query, gallery, num_test_pids, num_query_imgs, num_gallery_imgs, num_test_clothes = \
            self._process_dir_test(self.query_dir, self.gallery_dir)
        num_total_pids = num_train_pids + num_test_pids
        num_total_imgs = num_train_imgs + num_query_imgs + num_gallery_imgs
        num_test_imgs = num_query_imgs + num_gallery_imgs
        num_total_clothes = num_train_clothes + num_test_clothes

        logger = logging.getLogger('reid.dataset')
        logger.info("=> LTCC loaded")
        logger.info("Dataset statistics:")
        logger.info("  ----------------------------------------")
        logger.info("  subset   | # ids | # images | # clothes")
        logger.info("  ----------------------------------------")
        logger.info("  train    | {:5d} | {:8d} | {:9d}".format(num_train_pids, num_train_imgs, num_train_clothes))
        logger.info("  test     | {:5d} | {:8d} | {:9d}".format(num_test_pids, num_test_imgs, num_test_clothes))
        logger.info("  query    | {:5d} | {:8d} |".format(num_test_pids, num_query_imgs))
        logger.info("  gallery  | {:5d} | {:8d} |".format(num_test_pids, num_gallery_imgs))
        logger.info("  ----------------------------------------")
        logger.info("  total    | {:5d} | {:8d} | {:9d}".format(num_total_pids, num_total_imgs, num_total_clothes))
        logger.info("  ----------------------------------------")

        self.train = train
        self.query = query
        self.gallery = gallery

        self.num_train_pids = num_train_pids
        self.num_train_clothes = num_train_clothes
        self.pid2clothes = pid2clothes

    def _check_before_run(self):
        """Check if all files are available before going deeper"""
        if not osp.exists(self.dataset_dir):
            raise RuntimeError("'{}' is not available".format(self.dataset_dir))
        if not osp.exists(self.train_dir):
            raise RuntimeError("'{}' is not available".format(self.train_dir))
        if not osp.exists(self.query_dir):
            raise RuntimeError("'{}' is not available".format(self.query_dir))
        if not osp.exists(self.gallery_dir):
            raise RuntimeError("'{}' is not available".format(self.gallery_dir))

    def _process_dir_train(self, dir_path):
        img_paths = glob.glob(osp.join(dir_path, '*.png'))
        img_paths.sort()
        pattern1 = re.compile(r'(\d+)_(\d+)_c(\d+)')
        pattern2 = re.compile(r'(\w+)_c')

        pid_container = set()
        clothes_container = set()
        for img_path in img_paths:
            pid, _, _ = map(int, pattern1.search(img_path).groups())
            clothes_id = pattern2.search(img_path).group(1)
            pid_container.add(pid)
            clothes_container.add(clothes_id)
        pid_container = sorted(pid_container)
        clothes_container = sorted(clothes_container)
        pid2label = {pid:label for label, pid in enumerate(pid_container)}
        clothes2label = {clothes_id:label for label, clothes_id in enumerate(clothes_container)}

        num_pids = len(pid_container)
        num_clothes = len(clothes_container)

        dataset = []
        pid2clothes = np.zeros((num_pids, num_clothes))
        for img_path in img_paths:
            pid, _, camid = map(int, pattern1.search(img_path).groups())
            clothes = pattern2.search(img_path).group(1)
            camid -= 1 # index starts from 0
            pid = pid2label[pid]
            clothes_id = clothes2label[clothes]
            dataset.append((img_path, pid, camid, clothes_id))
            pid2clothes[pid, clothes_id] = 1

        num_imgs = len(dataset)

        return dataset, num_pids, num_imgs, num_clothes, pid2clothes

    def _process_dir_test(self, query_path, gallery_path):
        query_img_paths = glob.glob(osp.join(query_path, '*.png'))
        gallery_img_paths = glob.glob(osp.join(gallery_path, '*.png'))
        query_img_paths.sort()
        gallery_img_paths.sort()
        pattern1 = re.compile(r'(\d+)_(\d+)_c(\d+)')
        pattern2 = re.compile(r'(\w+)_c')

        pid_container = set()
        clothes_container = set()
        for img_path in query_img_paths:
            pid, _, _ = map(int, pattern1.search(img_path).groups())
            clothes_id = pattern2.search(img_path).group(1)
            pid_container.add(pid)
            clothes_container.add(clothes_id)
        for img_path in gallery_img_paths:
            pid, _, _ = map(int, pattern1.search(img_path).groups())
            clothes_id = pattern2.search(img_path).group(1)
            pid_container.add(pid)
            clothes_container.add(clothes_id)
        pid_container = sorted(pid_container)
        clothes_container = sorted(clothes_container)
        pid2label = {pid:label for label, pid in enumerate(pid_container)}
        clothes2label = {clothes_id:label for label, clothes_id in enumerate(clothes_container)}

        num_pids = len(pid_container)
        num_clothes = len(clothes_container)

        query_dataset = []
        gallery_dataset = []
        for img_path in query_img_paths:
            pid, _, camid = map(int, pattern1.search(img_path).groups())
            clothes_id = pattern2.search(img_path).group(1)
            camid -= 1 # index starts from 0
            clothes_id = clothes2label[clothes_id]
            query_dataset.append((img_path, pid, camid, clothes_id))

        for img_path in gallery_img_paths:
            pid, _, camid = map(int, pattern1.search(img_path).groups())
            clothes_id = pattern2.search(img_path).group(1)
            camid -= 1 # index starts from 0
            clothes_id = clothes2label[clothes_id]
            gallery_dataset.append((img_path, pid, camid, clothes_id))

        num_imgs_query = len(query_dataset)
        num_imgs_gallery = len(gallery_dataset)

        return query_dataset, gallery_dataset, num_pids, num_imgs_query, num_imgs_gallery, num_clothes


# file read/write stuff
def writeFile(pathToFile, contents):
    file = open(pathToFile, 'w+', encoding='utf-8')
    file.write(str(contents))
    file.close()

def readFile(pathToFile):
    file = open(pathToFile, 'r', encoding='utf-8')
    contents = file.read()
    file.close()
    return contents

input('Did you remember to update both version numbers in /src/core.js (enter to compile)')

INPUT_FILES = readFile('inputFiles.txt').split('\n') # A list of files to join together
OUTPUT_FILE = 'wrk.js' # Where to output the compiled product

# Go through the input files and join them together
output = ''
for fileIdx in range(len(INPUT_FILES)):
    filename = INPUT_FILES[fileIdx]
    if len(filename) > 0:
        content = readFile(filename)
        output += content + '\n\n'

writeFile(OUTPUT_FILE, output)