import copy
import os
from collections import OrderedDict

import numpy as np

from imagesplit.file.data_type import DataType
from imagesplit.file.file_image_descriptor import FileImageDescriptor
from imagesplit.file.file_wrapper import FileWrapper, FileStreamer
from imagesplit.file.image_file_reader import LinearImageFileReader
from imagesplit.image.combined_image import Axis
from imagesplit.utils.utilities import compute_bytes_per_voxel, \
    get_numpy_datatype

class MetaIoFile(LinearImageFileReader):
    """A class for reading or writing 3D imaging data to/from a MetaIO file
    pair (.mhd and .raw). """

    # pylint: disable=too-many-instance-attributes, super-with-arguments

    def __init__(self, local_file_size, header_filename,
                 file_handle_factory, header_template):
        super(MetaIoFile, self).__init__(local_file_size)
        self._file_handle_factory = file_handle_factory
        self._header_filename = header_filename
        self._input_path = os.path.dirname(os.path.abspath(header_filename))
        self._file_wrapper = None
        self._file_streamer = None
        if header_template:
            # File is for writing
            self._mode = 'wb'
            # Force the raw filename to match the header filename
            base_filename = os.path.splitext(
                os.path.basename(header_filename))[0]
            header = copy.deepcopy(header_template)
            header['ElementDataFile'] = base_filename + '.raw'

            save_mhd_header(header_filename, header)
            self._header = header

        else:
            # File is for reading
            self._mode = 'rb'
            self._header = load_mhd_header(header_filename)

        self._bytes_per_voxel = compute_bytes_per_voxel(
            self._header["ElementType"]) # ToDo: set this based on output format
        self._numpy_format = get_numpy_datatype(
            self._header["ElementType"],
            self._header["BinaryDataByteOrderMSB"])
        self._subimage_size = self._header["DimSize"]
        self._dimension_ordering = get_condensed_dim_order(self._header)

    @classmethod
    def load_and_parse_header(cls, filename):
        """Reads a MetaIO header file and parses"""

        header = load_mhd_header(filename)
        return parse_mhd(header)

    @classmethod
    def create_read_file(cls, subimage_descriptor, file_handle_factory):
        """Create a MetaIoFile class for writing"""

        filename = subimage_descriptor.filename
        local_file_size = subimage_descriptor.ranges.image_size
        return cls(local_file_size, filename, file_handle_factory, None)

    @classmethod
    def create_write_file(cls, subimage_descriptor, file_handle_factory):
        """Create a MetaIoFile class for this filename and template"""

        header_template = cls._create_meta_header(subimage_descriptor)
        # header_template = copy.deepcopy(subimage_descriptor.template)
        local_file_size = subimage_descriptor.get_local_size()
        local_origin = subimage_descriptor.get_local_origin()

        if subimage_descriptor.data_type:
            header_template["ElementType"] = \
                DataType.metaio_from_name(subimage_descriptor.data_type)
        header_template["DimSize"] = local_file_size
        header_template["Origin"] = local_origin
        filename = subimage_descriptor.filename
        return cls(local_file_size, filename, file_handle_factory,
                   header_template)

    def close_file(self):
        """Close file"""
        self.close()

    def write_line(self, start_coords, image_line, rescale_limits):
        """Write consecutive voxels to the raw binary file."""

        return self._get_file_streamer().write_line(
            start_coords, image_line, rescale_limits)

    def read_line(self, start_coords, num_voxels_to_read):
        """Read consecutive voxels of image data from the raw binary file
        starting at the specified coordinates. """

        return self._get_file_streamer().read_line(start_coords,
                                                   num_voxels_to_read)

    def get_bytes_per_voxel(self):
        """Return the number of bytes used to represent a single voxel in
        this image. """

        header = self._get_header()
        return compute_bytes_per_voxel(header["ElementType"])

    def get_dimension_ordering(self):
        """
        Return the preferred dimension ordering for writing data.

        Returns an array of 3 element, where each element represents a
        dimension in the global coordinate system numbered from 1 to 3 and
        is positive if data are to be written in ascending coordinates (in
        the global system) or negative if to be written in descending global
        coordinates along that dimension
        """

        return self._dimension_ordering

    @classmethod
    def _create_meta_header(cls, subimage_descriptor):
        local_file_size = subimage_descriptor.get_local_size()
        local_origin = subimage_descriptor.get_local_origin()
        local_voxel_size = subimage_descriptor.get_local_voxel_size()

        transform_matrix = condensed_to_cosine(
            subimage_descriptor.axis.to_condensed_format())

        header = get_default_metadata()
        header["ObjectType"] = 'Image'
        header["NDims"] = np.size(local_file_size)
        header["BinaryData"] = 'True'
        header["BinaryDataByteOrderMSB"] = subimage_descriptor.msb
        header["CompressedData"] = 'False'
        header["TransformMatrix"] = transform_matrix
        header["ElementSize"] = local_voxel_size
        header["DimSize"] = local_file_size
        header["ElementType"] = \
            DataType.metaio_from_name(subimage_descriptor.data_type)
        header["Origin"] = local_origin

        return header

    def _get_header(self):
        """Return an OrderedDict containing the MetaIO metaheader metadata
        for this image. """

        if not self._header:
            self._header = load_mhd_header(self._header_filename)
        return self._header

    def _get_file_wrapper(self):
        """Return the FileWrapper representing this image, creating it if
        it does not already exist. """

        if not self._file_wrapper:
            header = self._get_header()
            filename_raw = os.path.join(self._input_path,
                                        header["ElementDataFile"])
            self._file_wrapper = FileWrapper(filename_raw,
                                             self._file_handle_factory,
                                             self._mode)
        return self._file_wrapper

    def _get_file_streamer(self):
        """Return the FileStreamer representing this image, creating it
        if it does not already exist. """

        if not self._file_streamer:
            self._file_streamer = FileStreamer(self._get_file_wrapper(),
                                               self._subimage_size,
                                               self._bytes_per_voxel,
                                               self._numpy_format,
                                               self._dimension_ordering)
        return self._file_streamer

    def close(self):
        """Close the files associated with this image, if they are not
        already closed. """

        if self._file_streamer:
            self._file_streamer.close()
            self._file_streamer = None
        if self._file_wrapper:
            self._file_wrapper.close()
            self._file_wrapper = None

def load_mhd_header(filename):
    """Return an OrderedDict containing metadata loaded from an mhd file."""

    metadata = OrderedDict()

    with open(filename) as header_file:
        for line in header_file:
            (key, val) = [x.strip() for x in line.split("=")]
            if key in ['ElementSpacing', 'Offset', 'CenterOfRotation',
                       'TransformMatrix', 'ElementSize']:
                new_val = [float(s) for s in val.split()]
            elif key in ['NDims', 'ElementNumberOfChannels']:
                new_val = int(val)
            elif key in ['DimSize']:
                new_val = [int(s) for s in val.split()]
            elif key in ['BinaryData', 'BinaryDataByteOrderMSB',
                         'CompressedData']:
                # pylint: disable=simplifiable-if-statement
                if val.lower() == "true":
                    new_val = True
                else:
                    new_val = False
            else:
                new_val = val

            metadata[key] = new_val

    return metadata

def save_mhd_header(filename, metadata):
    """Saves a mhd header file to disk using the given metadata"""

    # Add default metadata, replacing with custom specified values
    header = ''
    default_metadata = get_default_metadata()
    for key, val in default_metadata.items():
        if key in metadata.keys():
            value = metadata[key]
        else:
            value = val
        if value or value == 'False' or value == 0:
            value = str(value)
            value = value.replace("[", "").replace("]", "").replace(",", "")
            header += '%s = %s\n' % (key, value)

    # Add any custom metadata tags
    for key, val in metadata.items():
        if key not in default_metadata.keys():
            value = str(metadata[key])
            value = value.replace("[", "").replace("]", "").replace(",", "")
            header += '%s = %s\n' % (key, value)

    file_handle = open(filename, 'w')
    file_handle.write(header)
    file_handle.close()

def get_default_metadata():
    """Return an OrderedDict containing default mhd file metadata"""

    return OrderedDict(
        [('ObjectType', 'Image'), ('NDims', '3'), ('BinaryData', 'True'),
         ('BinaryDataByteOrderMSB', 'True'),
         ('CompressedData', []), ('CompressedDataSize', []),
         ('TransformMatrix', []), ('Offset', []),
         ('CenterOfRotation', []), ('AnatomicalOrientation', []),
         ('ElementSpacing', []), ('DimSize', []),
         ('ElementNumberOfChannels', []), ('ElementSize', []),
         ('ElementType', 'MET_FLOAT'), ('ElementDataFile', []),
         ('Comment', []), ('SeriesDescription', []), ('AcquisitionDate', []),
         ('AcquisitionTime', []),
         ('StudyDate', []), ('StudyTime', [])])

def get_condensed_dim_order(header):
    """Return the condensed dimension order and flip string for this header"""
    if "TransformMatrix" in header and header["TransformMatrix"]:
        transform = header["TransformMatrix"]
        new_dimension_order, flip_orientation = \
            mhd_cosines_to_permutation(
                transform[0:3], transform[3:6], transform[6:9])
    elif "AnatomicalOrientation" in header and header["AnatomicalOrientation"]:
        new_dimension_order, flip_orientation = \
            anatomical_to_permutation(
                header["AnatomicalOrientation"])
    else:
        new_dimension_order = [0, 1, 2]
        flip_orientation = [False, False, False]

    return Axis(new_dimension_order, flip_orientation).to_condensed_format()

def anatomical_to_permutation(anatomical_orientation_string):
    """Dimension permutation vector corresponding to this orientation string"""

    direction_cosine_1 = \
        anatomical_to_cosine(anatomical_orientation_string[0])
    direction_cosine_2 = \
        anatomical_to_cosine(anatomical_orientation_string[1])
    direction_cosine_3 = \
        anatomical_to_cosine(anatomical_orientation_string[2])

    permutation_vector, flip_orientation = \
        mhd_cosines_to_permutation(
            direction_cosine_1, direction_cosine_2, direction_cosine_3)

    return permutation_vector, flip_orientation

def anatomical_to_cosine(anatomical_orientation_char):
    """Get Dicom direction cosine for this orientation string"""
    if anatomical_orientation_char == 'R':
        return [1, 0, 0]
    elif anatomical_orientation_char == 'L':
        return [-1, 0, 0]
    elif anatomical_orientation_char == 'A':
        return [0, 1, 0]
    elif anatomical_orientation_char == 'P':
        return [0, -1, 0]
    elif anatomical_orientation_char == 'I':
        return [0, 0, 1]
    elif anatomical_orientation_char == 'S':
        return [0, 0, -1]

    raise ValueError('No implementation yet for anatomical orientation ' +
                     anatomical_orientation_char + '.')

def permutation_to_cosine(permutation, flip):
    """Get mhd direction cosine for this dimension permutation"""

    dir_cosine = [0, 0, 0, 0, 0, 0, 0, 0, 0]
    dir_cosine[permutation[0]*3] = -1 if flip[0] else 1
    dir_cosine[permutation[1]*3 + 1] = -1 if flip[1] else 1
    dir_cosine[permutation[2]*3 + 2] = -1 if flip[2] else 1

    return dir_cosine

def condensed_to_cosine(condensed_format):
    """Get mhd direction cosine for this condensed format axis"""

    axis = Axis.from_condensed_format(condensed_format)
    return permutation_to_cosine(axis.dim_order, axis.dim_flip)

def mhd_cosines_to_permutation(direction_cosine_1,
                               direction_cosine_2,
                               direction_cosine_3):
    """Get dimension permutation vectors for these mhd direction cosines"""
    orientation_1 = direction_cosine_1
    orientation_2 = direction_cosine_2
    orientation_3 = direction_cosine_3

    permutation_vector, dimension_1, dimension_2, dimension_3 = \
        permutation_from_orientations(orientation_1, orientation_2)

    flip_orientation = get_flip_from_orientations(
        orientation_1, orientation_2, orientation_3, dimension_1, dimension_2,
        dimension_3)
    if np.sum(np.equal(permutation_vector, 0)) != 1 or \
            np.sum(np.equal(permutation_vector, 1)) != 1 or \
            np.sum(np.equal(permutation_vector, 2)) != 1 or \
            np.size(np.setdiff1d(permutation_vector, [0, 1, 2])) != 0:
        raise ValueError('Invalid permutation vector')

    return permutation_vector, flip_orientation

def permutation_from_orientations(orientation_1, orientation_2):
    """Get dimension permutation vectors for these orientation vectors"""
    dimension_1, dimension_2, dimension_3 = \
        dimensions_from_orientations(orientation_1, orientation_2)

    permutation_vector = [2, 2, 2]
    permutation_vector[dimension_1] = 0
    permutation_vector[dimension_2] = 1
    return permutation_vector, dimension_1, dimension_2, dimension_3

def dimensions_from_orientations(orientation_vector_1,
                                 orientation_vector_2):
    """Get dimension permutation vectors for these orientation vectors"""

    dimension_number_1 = np.argmax(np.abs(orientation_vector_1))
    remaining_dimensions = np.setdiff1d([0, 1, 2], dimension_number_1)
    reduced_orientation_vector_2 = np.take(orientation_vector_2,
                                           remaining_dimensions)
    dim2_from_reduced_set = np.argmax(np.abs(reduced_orientation_vector_2))
    dimension_number_2 = remaining_dimensions[dim2_from_reduced_set]
    dimension_number_3 = np.setdiff1d([0, 1, 2],
                                      [dimension_number_1, dimension_number_2])
    dimension_number_3 = dimension_number_3[0]
    return dimension_number_1, dimension_number_2, dimension_number_3

def get_flip_from_orientations(orientation_1, orientation_2, orientation_3,
                               dimension_1, dimension_2, dimension_3):
    """Get dimension flip vectors for these orientation vectors"""

    flip = [False, False, False]
    flip[dimension_1] = orientation_1[dimension_1] < 0
    flip[dimension_2] = orientation_2[dimension_2] < 0
    flip[dimension_3] = orientation_3[dimension_3] < 0
    return flip

def parse_mhd(header):
    """Read a metaheader and returns a FileImageDescriptor"""

    file_format = "mhd"
    dim_order = get_condensed_dim_order(header)
    data_type = DataType.name_from_metaio(header["ElementType"])
    image_size = header["DimSize"]
    msb = header["BinaryDataByteOrderMSB"]
    compression = None
    voxel_size = header['ElementSize']
    return (FileImageDescriptor(file_format=file_format,
                                dim_order=dim_order,
                                data_type=data_type,
                                image_size=image_size,
                                msb=msb,
                                compression=compression,
                                voxel_size=voxel_size), header)


#! /usr/bin/env python3
# -*- coding: UTF-8 -*-
import json
import sys

#---------------------------------
#read static information JSON file
#---------------------------------
class StaticInfo:
    '''This class reads static information json file.
     The static information is produced by goil (tpl_static_info.json).
     This files helps to make the link between the ids (integers) and the name  of
     Trampoline objects (alarms, tasks, events, resources, …)
    '''
    def __init__(self,filename):
        self.staticInfo    = [] #JSON raw data

        self.procNames     = [] #procs names (including idle)
        self.resourceNames = [] #resource names
        self.timeObjNames  = [] #alarms names

        self.procStates    = [] #tasks  states (trampoline specific)
        self.timeObjStates = [] #alarms states (trampoline specific)

        self.msgSendNames  = [] #message sender Names
        self.msgRcvNames   = [] #message receiver Names

        self.iocNames      = [] #ioc Names

        self.readJSON(filename)

    #convert event mask to index.
    def getEventName(self,task,mask):
        try:
            for taskEvt in self.staticInfo['task'][task]['EVENT']:
                for evt in self.staticInfo['event']:
                    if evt['MASK'] == mask and taskEvt['VALUE'] == evt['NAME']:
                        return evt['NAME']
        except KeyError:
            print('error in event name: no event for task {0} and mask {1}'.format(str(task),str(mask)))

    def readJSON(self,filename):
        try:
            with open(filename) as staticInfoFile:
                self.staticInfo = json.load(staticInfoFile)
        except OSError as e:
            print('static information file not found ('+filename+'). '
                  'You should enable the trace in the .oil file and '
                  'recompile your application.')
            sys.exit(1)
        # proc (task+isr) names
        for task in self.staticInfo['task']:
            self.procNames.append(task['NAME'])
        for isr in self.staticInfo['isr']:
            if isr['CATEGORY'] == 2: #isr1 nor handled by the OS.
                self.procNames.append(isr['NAME'])
        self.procNames.append('idle') #idle task is the last one.
        #resource names
        for to in self.staticInfo['resource']:
            self.resourceNames.append(to['NAME'])
        #time objs names
        for to in self.staticInfo['alarm']:
            self.timeObjNames.append(to['NAME'])
        #msg names
        for msg in self.staticInfo['message']:
            if msg['MESSAGEPROPERTY'][:7]=='RECEIVE':
                self.msgRcvNames.append(msg['NAME'])
            elif  msg['MESSAGEPROPERTY'][:4]=='SEND':
                self.msgSendNames.append(msg['NAME'])
        #ioc names
        for ioc in self.staticInfo['ioc']:
            self.iocNames.append(ioc['NAME'])

        # OS constant names (task states, alarm states)
        self.procStates     = ['SUSPENDED','READY','RUNNING','WAITING','AUTOSTART','READY_AND_NEW']
        self.timeObjStates  = ['SUSPENDED','READY','RUNNING','WAITING','AUTOSTART','READY_AND_NEW']
        self.resourceStates = ['FREE','TAKEN']

    def getIdleTaskId(self):
        #idle task is the last one.
        return len(self.procNames)-1

    def debug(self):
        ''' print basic debugging info '''
        #procs
        print('Tasks  ('+str(len(self.procNames))+'):')
        for proc in self.procNames:
            print('\t'+proc)
        #alarms
        print('Alarms ('+str(len(self.timeObjNames))+'):')
        for al in self.timeObjNames:
            print('\t'+al)

if __name__ == "__main__":
    filename = 'tpl_static_info.json'
    if len(sys.argv) == 2:
        filename = sys.argv[1]
    si = StaticInfo(filename)
    si.debug()


from itertools import product
from random import choice, randint
import numpy as np
from pylearn2.datasets.preprocessing import CentralWindow
from pylearn2.train_extensions import TrainExtension
from skimage.transform import rotate, rescale
import data_prep
from skimage.filter import rank
from skimage.morphology import disk
import multiprocessing

from PIL import Image

class RotatorExtension(TrainExtension):

    def __init__(self, window, randomize, center=(),
                 angles=range(360),
                 x_offsets=(0, 1), y_offsets=(0, 1),
                 median_radii=(0,),
                 mean_radii=(0,),
                 flip=True,
                 scales=(1,)):
        self.window = window
        self.randomize = randomize
        self.center = center
        self.angles = angles
        self.median_radii = median_radii
        self.mean_radii = mean_radii
        self.flip = flip
        self.scales = scales
        self.originals = [ds.get_topological_view() for ds in randomize]
        self.offsets = list(product(x_offsets, y_offsets))
        if len(randomize) > 0:
            self.originals = [ds.get_topological_view() for ds in randomize]
            self.init_image_shape = randomize[0].get_topological_view()[0].shape[:2]
        else:
            self.originals = []
            self.init_image_shape = center[0].get_topological_view()[0].shape[:2]
        self.max_offset = [d-w for d, w in zip(self.init_image_shape, self.window)]
        self.center_offset = [d//2 for d in self.max_offset]

    def change_images(self, initial_images, fn):
        return np.array([fn(img) for img in initial_images], dtype='float32')

    def setup(self, model, dataset, algorithm):
        for data in self.center:
            data.set_topological_view(self.change_images(data.get_topological_view(), lambda img: self.crop(img, self.center_offset, self.window)))

        self.randomize_datasets()

    def randomize_datasets(self):
        for original, dataset in zip(self.originals, self.randomize):
            imgs = original
            new_imgs = np.array([self.randomize_image(img) for img in imgs], dtype='float32')

            dataset.set_topological_view(new_imgs, axes=dataset.view_converter.axes)

    def on_monitor(self, model, dataset, algorithm):
        model = None
        dataset = None
        algorithm = None

        self.randomize_datasets()

    def randomize_image(self, img):
        h, w = img.shape[:2]
        img = rescale(img, choice(self.scales))
        img = rotate(img, choice(self.angles))
        if self.flip and randint(0, 1) == 0:
            img = np.fliplr(img)
        img = (255*(1-img[:, :, 0])).astype('uint8')

        med = choice(self.median_radii)
        if med > 0:
            img = rank.median(img, disk(med))
        mea = choice(self.mean_radii)
        if mea > 0:
            img = rank.mean(img, disk(mea))
        # workaround
        img = data_prep.crop(img, [h, w])
        img = 1-(img[:, :, np.newaxis]/255.)
        if self.init_image_shape != self.window:
            offset = [c1//2 + r for c1, r in zip(self.max_offset, choice(self.offsets))]
            img = self.crop(img, offset, self.window)
        return img

    def crop(self, img, offset, new_size):
        new_x, new_y = new_size
        off_x, off_y = offset
        return img[off_x:(off_x + new_x), off_y:(off_y + new_y), :]

def crop_01(img, offset, new_size):
    new_x, new_y = new_size
    off_x, off_y = offset
    return img[off_x:(off_x + new_x), off_y:(off_y + new_y)]

def basic_crop(img, new_sizes):
    h, w = img.shape
    cropped = np.zeros(new_sizes, dtype=img.dtype)
    start_y = (new_sizes[0] - h) // 2
    start_x = (new_sizes[1] - w) // 2
    new_h, new_w = min(h, new_sizes[0]), min(w, new_sizes[1])
    cropped[max(start_y, 0):(max(start_y, 0) + new_h),
            max(start_x, 0):(max(start_x, 0) + new_w)] = img[max(-start_y, 0):(max(-start_y, 0) + new_h),
                                                             max(-start_x, 0):(max(-start_x, 0) + new_w)]
    return cropped

def randomize_image_c01(img, window, angles=(0,), x_offsets=(0,), y_offsets=(0,),
                        median_radii=(0,), mean_radii=(0,), flip=False, scales=(1,)):
    offsets = list(product(y_offsets, x_offsets))
    img = img[0]
    h, w = img.shape
    # im = Image.fromarray(np.cast['uint8'](img*255))
    # scale = choice(scales)
    # im = im.resize((int(w*scale), int(h*scale)))
    # im = im.rotate(choice(angles))
    # img = np.asarray(im, dtype='float32')/255.
    img = rescale(img, choice(scales))
    img = rotate(img, choice(angles))
    if flip and randint(0, 1) == 0:
        img = np.fliplr(img)

    img = basic_crop(img, [h, w])

    max_offset = [d-w for d, w in zip(img.shape, window)]
    if img.shape != window:
        offset = [c1//2 + r*choice([1, -1]) for c1, r in zip(max_offset, choice(offsets))]
        img = crop_01(img, offset, window)

    med = choice(median_radii)
    if med > 0:
        img = rank.median(img, disk(med))
    mea = choice(mean_radii)
    if mea > 0:
        img = rank.mean(img, disk(mea))
    return img[np.newaxis, :, :]

# t0 = time()
# batch_x = randomize_dataset(train_x, (96, 96), scales=(2, 0.5))
# print(time() - t0)

def randomize_dataset_bc01(imgs, window, angles=range(360), x_offsets=(0, 1), y_offsets=(0, 1),
                           median_radii=(0,), mean_radii=(0,), flip=True, scales=(1,)):
    return np.array([randomize_image_c01(img, window, angles,
                                         x_offsets, y_offsets,
                                         median_radii, mean_radii,
                                         flip, scales) for img in imgs], dtype='float32')


import networkx as nx
from utils import read_inputs
from tqdm import tqdm
from math import gcd
import os

def lcm(a, b):
    return int(abs(a * b) / gcd(a, b))

INPUT_FILE = "2022/inputs/day24.txt"
CACHED_GRAPH = "2022/code/day24_maze_graph.pkl"

ARROW_TO_DELTA = {
    "^": (-1, 0),  # one row up
    "v": (1, 0),  # one row down
    ">": (0, 1),  # one col right
    "<": (0, -1),  # one col left
}

Arrows = set[tuple[tuple[int], tuple[int]]]

def parse_arrow_inputs(inputs: list[list[str]]) -> Arrows:
    arrows = set()
    for row in range(len(inputs)):
        for col in range(len(inputs[row])):
            arrow = inputs[row][col]
            if arrow in ARROW_TO_DELTA:
                delta = ARROW_TO_DELTA[arrow]
                arrows.add(((row, col), delta))
            else:
                continue

    return arrows

def move_arrows(arrows: Arrows, num_rows: int, num_cols: int) -> Arrows:
    new_arrows = set()
    for arrow in arrows:
        position, delta = arrow
        new_position = (
            (position[0] + delta[0]) % num_rows,
            (position[1] + delta[1]) % num_cols,
        )
        new_arrows.add((new_position, delta))

    return new_arrows

def build_maze(
    num_rows: int, num_cols: int, arrows: Arrows, cache: str = None
) -> nx.DiGraph:
    base_grid_graph = nx.grid_2d_graph(num_rows, num_cols)

    maze_graph = nx.DiGraph()
    least_common_mult = lcm(num_cols, num_rows)
    print(f"Grid size: {num_rows} x {num_cols} -- Time range: {least_common_mult}")

    time_range = range(least_common_mult)
    # add nodes
    for t in tqdm(time_range, desc="Nodes: "):
        maze_graph.add_nodes_from([(t, node) for node in base_grid_graph])
    # add edges across time
    for t in tqdm(time_range, desc="Edges: "):
        for node in base_grid_graph:
            # wait in-place
            maze_graph.add_edge((t, node), ((t + 1) % least_common_mult, node))
            # step to neighbour
            for other_node in nx.neighbors(base_grid_graph, node):
                maze_graph.add_edge(
                    (t, node), ((t + 1) % least_common_mult, other_node)
                )

    # add start and end node
    maze_graph.add_nodes_from([(t, "Start") for t in time_range])
    maze_graph.add_node("End")
    # add edges
    maze_graph.add_edges_from(
        [((t, "Start"), ((t + 1) % least_common_mult, (0, 0))) for t in time_range]
    )
    maze_graph.add_edges_from(
        [((t, (num_rows - 1, num_cols - 1)), "End") for t in time_range]
    )

    print("Before filtering: ", maze_graph)

    # filter for available positions
    arrows = parse_arrow_inputs(inputs)
    for t in tqdm(time_range, desc="Arrow subgraph: "):
        maze_graph.remove_nodes_from([(t, node) for node, _ in arrows])
        arrows = move_arrows(arrows, num_rows, num_cols)

    print("After filtering: ", maze_graph)

    if cache:
        nx.write_gpickle(maze_graph, cache)

    return maze_graph

if __name__ == "__main__":
    inputs = read_inputs(INPUT_FILE, split=True)
    # drop walls
    inputs = inputs[1:-1]
    inputs = [row[1:-1] for row in inputs]

    # read board size
    num_rows = len(inputs)
    num_cols = len(inputs[0])

    # setup graph
    arrows = parse_arrow_inputs(inputs)

    if os.path.exists(CACHED_GRAPH):
        print(f"-- Reading maze from {CACHED_GRAPH} --")
        maze_graph = nx.read_gpickle(CACHED_GRAPH)
    else:
        print("-- Building maze --")
        maze_graph = build_maze(num_rows, num_cols, arrows, cache=CACHED_GRAPH)

    print(maze_graph)

    print(nx.shortest_path_length(maze_graph, source=(0, "Start"), target="End"))


from django.shortcuts import render
from django.views.generic import TemplateView
from textblob import TextBlob
import re
from rest_framework import viewsets, status
from rest_framework.decorators import api_view
from rest_framework.response import Response

def clean_tweet(tweet):
        return ' '.join(re.sub("(@[A-Za-z0-9]+|([^0-9A-Za-z \t])|(\w+:\/\/\S+))", " ", tweet).split())

@api_view(['GET', 'POST'])
def hello_world(request):
    if request.method == 'GET':
        tweet = request.GET['tweet']
        analysis = TextBlob(clean_tweet(tweet))
        return Response({"message": 'GET',
                         "tweet": tweet,
                         "backwards": tweet[::-1],
                         "rating": round(analysis.sentiment.polarity,2),
                         "subjectivity": round(analysis.sentiment.subjectivity,2)})

    return Response({"message": "NOT A POST", "tweet": request.GET['tweet']})

class IndexView(TemplateView):
    template_name = 'index.html'

    def get_context_data(self):
        context = super().get_context_data()
        context['rating'] = 'None'
        if self.request.GET:
            data = self.request.GET
            tweet = data['tweetreact']
            print(tweet)
            analysis = TextBlob(clean_tweet(tweet))
            context['rating'] = analysis.sentiment.polarity
        return context

# class TestAPIView(viewsets.ViewSet):
#     serializer_class = serializers.TaskSerializer
#
#     def list(self, request):
#         serialzer = serializers.TaskSerializer(
#         instance=tasks.values(), many=True)
#         return Response(serializer.data)


from __future__ import absolute_import, division, print_function

import io

def is_binary_file(file):
    return isinstance(file, (io.BufferedReader, io.BufferedWriter,
                             io.BufferedRandom))

# Everything beyond this point will be some kind of hack needed to make
# everything work. It's not pretty and it doesn't make great sense much
# of the time. I am very sorry to the poor soul who has to read beyond.

class StringIO(io.StringIO):
    """Treat Bytes the same as Unicode by decoding ascii, for testing only."""
    def __init__(self, string=None, **kwargs):
        if isinstance(string, bytes):
            string = string.decode()
        super(StringIO, self).__init__(string, **kwargs)

class SaneTextIOWrapper(io.TextIOWrapper):
    def __init__(self, *args, **kwargs):
        super(SaneTextIOWrapper, self).__init__(*args, **kwargs)
        self._should_close_buffer = True

    def __del__(self):
        # Accept the inevitability of the buffer being closed by the destructor
        # because of this line in Python 2.7:
        # https://github.com/python/cpython/blob/2.7/Modules/_io/iobase.c#L221
        self._should_close_buffer = False
        # Actually close for Python 3 because it is an override.
        # We can't call super because Python 2 doesn't actually
        # have a `__del__` method for IOBase (hence this
        # workaround). Close is idempotent so it won't matter
        # that Python 2 will end up calling this twice
        self.close()

    def close(self):
        # We can't stop Python 2.7 from calling close in the deconstructor
        # so instead we can prevent the buffer from being closed with a flag.

        # Based on:
        # https://github.com/python/cpython/blob/2.7/Lib/_pyio.py#L1586
        if self.buffer is not None and not self.closed:
            try:
                self.flush()
            finally:
                if self._should_close_buffer:
                    self.buffer.close()

class WrappedBufferedRandom(io.BufferedRandom):
    def __init__(self, *args, **kwargs):
        super(WrappedBufferedRandom, self).__init__(*args, **kwargs)
        self._should_close_raw = True

    def __del__(self):
        self._should_close_raw = False
        self.close()

    # Based on:
    # https://github.com/python/cpython/blob/2.7/Lib/_pyio.py#L732
    def close(self):
        if self.raw is not None and not self.closed:
            try:
                # may raise BlockingIOError or BrokenPipeError etc
                self.flush()
            finally:
                if self._should_close_raw:
                    self.raw.close()

class CompressedMixin(object):
    """Act as a bridge between worlds"""
    def __init__(self, before_file, *args, **kwargs):
        self.streamable = kwargs.pop('streamable', True)
        self._should_close_raw = True
        self._before_file = before_file
        super(CompressedMixin, self).__init__(*args, **kwargs)

    def __del__(self):
        self._should_close_raw = False
        self.close()

    @property
    def closed(self):
        return self.raw.closed or self._before_file.closed

    # Based on:
    # https://github.com/python/cpython/blob/2.7/Lib/_pyio.py#L732
    def close(self):
        if self.raw is not None and not self.closed:
            try:
                # may raise BlockingIOError or BrokenPipeError etc
                self.flush()
            finally:
                if self._should_close_raw:
                    self.raw.close()
                    # The above will not usually close the before_file
                    # We want the decompression to be transparent, so we don't
                    # want users to deal with this edge case. Instead we can
                    # just close the original now that we are being closed.
                    self._before_file.close()

class CompressedBufferedReader(CompressedMixin, io.BufferedReader):
    pass

class CompressedBufferedWriter(CompressedMixin, io.BufferedWriter):
    pass

class IterableStringReaderIO(io.StringIO):
    def __init__(self, iterable, newline):
        self._iterable = iterable
        super(IterableStringReaderIO, self).__init__(u''.join(iterable),
                                                     newline=newline)

class IterableStringWriterIO(IterableStringReaderIO):
    def close(self):
        if not self.closed:
            backup = self.tell()
            self.seek(0)
            for line in self:
                self._iterable.append(line)
            self.seek(backup)
        super(IterableStringWriterIO, self).close()


'''
Object category identification

This file is a part of GenericDecoding_demo.
'''

from __future__ import print_function

import os
import pickle

import numpy as np
import pandas as pd

import bdpy
from bdpy.stats import corrmat

from god_config import *

from sklearn.decomposition import IncrementalPCA

# Main #################################################################
dir_path = os.path.dirname(os.path.realpath(__file__)) #current directory
results_dir = dir_path+'/results/feature-decoding/'
output_file = dir_path+'/results/feature-decoding-merged/results.pkl'

# Load results -----------------------------------------------------
result_list = []
image_features=bdpy.BData(image_feature_file)

for subject in {'Subject1'}:
    for feat in features:
        y = image_features.select(feat)
        print('Shape of y before PCA:', y.shape)
        ipca = IncrementalPCA(n_components=20, batch_size=20)
        ipca.fit(y)
        y=ipca.transform(y)
        print('Shape of y after PCA:', y.shape)
        print(y.shape)

        ind_cat_other = (image_features.select('FeatureType') == 4).flatten()
        other_category_features=y[ind_cat_other, :]
        for roi in ['VC']:
            rf_full = dir_path+'/results/feature-decoding/'+subject+'_'+roi+'_'+feat+'_'+'decode_results.pkl'
            print('Loading %s' % rf_full)
            with open(rf_full, 'rb') as f:
                res = pickle.load(f)
            test_label_pt=res['test_label'][0]
            test_label_im=res['test_label'][1]
            true_feature_pt=res['true_feature'][0]
            true_feature_im=res['true_feature'][1]
            predicted_feature_pt=res['predicted_feature'][0]
            predicted_feature_im=res['predicted_feature'][1]
            av_feature_pt=dict(zip(res['category_label_set'][0],res['category_feature_averaged'][0]))
            av_feature_im=dict(zip(res['category_label_set'][1],res['category_feature_averaged'][1]))

            #identification accuracy test - check correlation between predicted feature and category averaged feature vs random feature and category averaged feature
            num_correct_identify_pt=0
            num_correct_identify_im=0
            for i in range(true_feature_pt.shape[0]):
                pred_im=predicted_feature_pt[i]
                cat_av=av_feature_pt[int(test_label_pt[i])]
                pred_corr=np.corrcoef(pred_im,cat_av)
                # print(pred_corr)

    result_list.append(res)


from flask import Flask, jsonify, request, render_template
import numpy as np
import json
import pickle
from data_input import data_in

def load_models():
    file_name = "models/model.pkl"
    with open(file_name, 'rb') as pickled:
        data = pickle.load(pickled)
        model = data['model']
    return model

app = Flask(__name__)
@app.route('/predict', methods = ['GET'])
def predict():
    # stub input features
    x = np.array(data_in).reshape(1,-1)
    #x_in = np.array(x).reshape(1,-1)
    # load model
    model = load_models()
    prediction = model.predict(x)[0]
    response = json.dumps({'response': prediction})
    return response, 200

if __name__ == '__main__':
    app.run(debug=True)


import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import style
style.use("ggplot")
#Import Library Pandas
df = pd.read_csv("C:\AK.TXT")  #I am working in Windows environment
#Reading the dataset in a dataframe using Pandas
print(df.head(1))  #Print first three observations

web_state = {"Day": [1,2,3,4,5,6],
             "Visitors": [12,13,14,15,16,17],
             "Bounce_Rate": [25,26,27,28,29,30]}

df = pd.DataFrame(web_state)
print(df)

df=df.set_index("Day")
print(df.head())

print(df["Visitors"])
print(df.Visitors)

print(df[["Visitors","Bounce_Rate"]])
print(df.Visitors.tolist())

print(np.array(df[["Visitors","Bounce_Rate"]]))

df2 = pd.DataFrame(np.array(df[["Visitors","Bounce_Rate"]]))
print(df2)

###Pandas I/O csv
df3=pd.read_csv(r"C:\Users\Alok Satpathy\Desktop\state\namebystate\namebystate\learnpython\testdata.csv")
print(df3.head())

df3.set_index("Date", inplace=True)
df3.to_csv("newcsv2.csv")

df4=pd.read_csv("newcsv2.csv", index_col=0)
print(df4.head())

df4.columns=["Austin HPI"]
print(df4.head())
df4.to_csv("newcsv3.csv")
df4.to_csv("newcsv4.csv", header=False)

df5=pd.read_csv("newcsv4.csv", names=["Date","Austin_HPI"], index_col=0)
print(df5.head())

df5.to_html("example.html")

df6=pd.read_csv("newcsv4.csv", names=["Date","Austin_HPI"])
print(df6.head())

df6.rename(columns={"Austin_HPI":"77006_HPI"}, inplace=True)
print(df6.head())