import cv2
import glob
import imghdr
import os
import tempfile
from pathlib import Path
import numpy as np

from PySide2.QtWidgets import QVBoxLayout, QHBoxLayout, QToolButton, QSpinBox, QLineEdit, QGraphicsView, QSlider, QWidget, QApplication, QFileDialog, QMainWindow, QAction, QMessageBox, QPushButton, QLabel, QGroupBox, QComboBox
from PySide2.QtGui import QPixmap, QImage
from PySide2.QtCore import Slot, Qt
from .SegmenterView import ImageSegmenterView
from .CustomClasses import LabeledComboBox, LabeledSlider, LabeledSpinBox, ClickableLineEdit

def ensure_dir(*paths):
    full_path = ''
    for path in paths:
        full_path = os.path.join(full_path, path)
        if os.path.exists(full_path) and os.path.isdir(full_path):
            continue
        elif os.path.exists(full_path):
            raise ValueError("A file without an extension is blocking directory creation at {}".format(full_path))
        else:
            os.mkdir(full_path)
    return full_path

def QImage_to_CVMat(image):
    image = image.convertToFormat(QImage.Format.Format_ARGB32_Premultiplied)
    width = image.width()
    height = image.height()

    ptr = image.constBits()
    arr = np.array(ptr).reshape(height, width, 4)
    return arr

class SegmenterWidget(QWidget):
    def __init__(self):
        super(SegmenterWidget, self).__init__()
        self.active_image_dir = None
        self.active_label_dir = None
        self.src_paths = []
        self.current_idx = None
        self.prev_idx = 0
        label_types = [
            'Foot',
            'Inner Wound',
            'Outer Wound'
        ]

        # Load last used image directory
        self.meta_file = os.path.join(os.path.dirname(__file__), 'meta.txt')
        if os.path.exists(self.meta_file):
            with open(self.meta_file, 'r') as f:
                last_active_image_dir = f.readline().strip()
                # print(last_active_image_dir)
                if os.path.exists(last_active_image_dir):
                    self.active_image_dir = last_active_image_dir

        # Set up Segmenter Widget
        self.image_title = QLineEdit(self)
        self.image_title.setText('--None--')
        self.image_title.setReadOnly(True)
        self.viewer = ImageSegmenterView(self)

        # Bottom Toolbar Widgets

        ## 'Set Image Folder' button
        self.select_dir_btn = QToolButton(self)
        self.select_dir_btn.setText('Set Image Folder')
        self.select_dir_btn.clicked.connect(self.set_image_dir)
        self.current_dir = ClickableLineEdit(self, text='--None--', readOnly=True)
        self.current_dir.clicked.connect(self.set_image_dir)

        self.select_label_dir_btn = QToolButton(self)
        self.select_label_dir_btn.setText('Set Label Folder')
        self.select_label_dir_btn.clicked.connect(self.set_label_dir)
        self.current_label_dir = ClickableLineEdit(self, text='--None--', readOnly=True)
        self.current_label_dir.clicked.connect(self.set_label_dir)

        self.prev_btn = QToolButton(self)
        self.prev_btn.setText('Previous Image')
        self.prev_btn.clicked.connect(self.goto_previous)
        self.image_idx = LabeledSpinBox('Current Image:',
        minimum=0,
        maximum=0,
        starting_value=0)
        self.image_idx.valueChanged.connect(self.goto_image)
        self.next_btn = QToolButton(self)
        self.next_btn.setText('Next Image')
        self.next_btn.clicked.connect(self.goto_next)
        self.skip_btn = QToolButton(self)
        self.skip_btn.setText('Next Unlabeled Image')
        self.skip_btn.clicked.connect(self.skipto_next)
        self.skip_label_btn = QToolButton(self)
        self.skip_label_btn.setText('Next Labeled Image  ')
        self.skip_label_btn.clicked.connect(self.skipto_next_label)

        ## Sliders
        self.opacity_slider = LabeledSlider('Mask Opacity: {}%',
                                            single_step=5,
                                            page_step=20,
                                            minimum=0,
                                            maximum=100,
                                            starting_value=50)
        self.opacity_slider.valueChanged.connect(self.viewer.set_opacity)
        self.pen_size_slider = LabeledSlider('Pen Size: {}px',
                                             single_step=5,
                                             page_step=10,
                                             minimum=1,
                                             maximum=500,
                                             starting_value=30)
        self.pen_size_slider.valueChanged.connect(self.viewer.set_pen_size)

        # Right Toolbar
        self.label_options = LabeledComboBox("Current Segmentation Mask", items=label_types)
        self.label_options.currentTextChanged.connect(self.label_changed)

        ## Paint Buttons
        self.brush_box = QGroupBox("Brush Options")
        self.foreground_btn = QPushButton('Foreground')
        self.foreground_btn.setCheckable(True)
        self.foreground_btn.setChecked(True)
        self.foreground_btn.setAutoExclusive(True)
        self.foreground_btn.clicked.connect(self.viewer.set_foreground)
        self.poss_foreground_btn = QPushButton("Possible Foreground")
        self.poss_foreground_btn.setCheckable(True)
        self.poss_foreground_btn.setChecked(False)
        self.poss_foreground_btn.setAutoExclusive(True)
        self.poss_foreground_btn.clicked.connect(self.viewer.set_possible_foreground)
        self.background_btn = QPushButton('Background')
        self.background_btn.setCheckable(True)
        self.background_btn.setChecked(False)
        self.background_btn.setAutoExclusive(True)
        self.background_btn.clicked.connect(self.viewer.set_background)
        self.eraser_button = QPushButton('Erase')
        self.eraser_button.setCheckable(True)
        self.eraser_button.setChecked(False)
        self.eraser_button.setAutoExclusive(True)
        self.eraser_button.clicked.connect(self.viewer.set_possible_background)
        brush_layout = QVBoxLayout()
        brush_layout.addWidget(self.foreground_btn)
        brush_layout.addWidget(self.poss_foreground_btn)
        brush_layout.addWidget(self.background_btn)
        brush_layout.addWidget(self.eraser_button)
        self.brush_box.setLayout(brush_layout)

        ## Image Action Buttons
        self.undo_btn = QToolButton(self)
        self.undo_btn.setText('Undo')
        self.undo_btn.clicked.connect(self.viewer.undo)
        self.redo_btn = QToolButton(self)
        self.redo_btn.setText('Redo')
        self.redo_btn.clicked.connect(self.viewer.redo)

        self.save_button = QToolButton(self)
        self.save_button.setText('&Save Mask')
        self.save_button.clicked.connect(self.saveSegmentation)
        self.clear_button = QToolButton(self)
        self.clear_button.setText('Clear Mask')
        self.clear_button.clicked.connect(self.viewer.resetSegLayer)

        self.grabcut_button = QToolButton(self)
        self.grabcut_button.setText('Run &GrabCut Segmenter')
        self.grabcut_button.clicked.connect(self.run_grabcut)

        self.hide_image_button = QToolButton(self)
        self.hide_image_button.setText('Show/&Hide Image')
        self.hide_image_button.pressed.connect(self.viewer.hide_image)

        # Arrange layout
        MainLayout = QVBoxLayout(self)

        BottomStack = QVBoxLayout()

        BottomToolbar = QHBoxLayout()
        BottomToolbar.setAlignment(Qt.AlignLeft)
        Selectors = QVBoxLayout()
        ImageDirSelector = QHBoxLayout()
        ImageDirSelector.addWidget(self.select_dir_btn)
        ImageDirSelector.addWidget(self.current_dir)
        Selectors.addLayout(ImageDirSelector)
        LabelDirSelector = QHBoxLayout()
        LabelDirSelector.addWidget(self.select_label_dir_btn)
        LabelDirSelector.addWidget(self.current_label_dir)
        Selectors.addLayout(LabelDirSelector)

        BottomToolbar.addLayout(Selectors)
        BottomToolbar.addWidget(self.image_idx)
        BottomToolbar.addWidget(self.prev_btn)
        BottomToolbar.addWidget(self.next_btn)
        Skippers = QVBoxLayout()
        Skippers.addWidget(self.skip_btn)
        Skippers.addWidget(self.skip_label_btn)

        # BottomToolbar.addWidget(self.skip_btn)
        BottomToolbar.addLayout(Skippers)
        BottomStack.addLayout(BottomToolbar)

        RightToolbar = QVBoxLayout()
        RightToolbar.setAlignment(Qt.AlignCenter)
        RightToolbar.addWidget(self.label_options)
        RightToolbar.addStretch()
        RightToolbar.addWidget(self.brush_box)
        RightToolbar.addWidget(self.undo_btn)
        RightToolbar.addWidget(self.redo_btn)
        RightToolbar.addStretch()
        # self.gc_spinner.setFixedHeight(0)
        # RightToolbar.addWidget(self.gc_spinner)
        RightToolbar.addWidget(self.hide_image_button)
        RightToolbar.addWidget(self.grabcut_button)
        RightToolbar.addWidget(self.save_button)
        RightToolbar.addWidget(self.clear_button)

        TopLayout = QHBoxLayout()
        ImageLayout = QVBoxLayout()
        ImageLayout.addWidget(self.image_title)
        ImageLayout.addWidget(self.viewer)
        TopLayout.addLayout(ImageLayout)
        TopLayout.addLayout(RightToolbar)

        MainLayout.addLayout(TopLayout)
        FlatSliders = QHBoxLayout()
        FlatSliders.addWidget(self.pen_size_slider)
        FlatSliders.addWidget(self.opacity_slider)
        MainLayout.addLayout(FlatSliders)
        MainLayout.addLayout(BottomStack)

    @Slot()
    def exit_app(self, checked):
        QApplication.quit()

    def set_image_dir(self):
        dialog = QFileDialog(self, 'Select Image Directory')
        dialog.setFileMode(QFileDialog.Directory)
        dialog.setNameFilters(["Any Files (*)"])
        dialog.setViewMode(QFileDialog.List)
        if dialog.exec():
            selected_dir = dialog.selectedFiles()[0]
            if not os.path.isdir(selected_dir):
                selected_dir = os.path.dirname(selected_dir)
            self.active_image_dir = selected_dir
            with open(self.meta_file, 'a+') as f:
                f.write('Set Image Dir: ' + self.active_image_dir + '\n')
            if self.active_label_dir is None:
                self.active_label_dir = self.active_image_dir
                self.current_label_dir.setText(self.active_label_dir)
                with open(self.meta_file, 'a+') as f:
                    f.write('Set Label Dir: ' + self.active_label_dir + '\n')
            self.label_dir = ensure_dir(os.path.join(self.active_label_dir, self.label_options.currentText().replace(' ', '_')))
            # image_paths = glob.glob(os.path.join(self.active_image_dir, '*.png')) + glob.glob(os.path.join(self.active_image_dir, '*.jpg'))
            paths = glob.glob(os.path.join(self.active_image_dir, '*'))
            image_paths = []
            for path in paths:
                if os.path.isdir(path):
                    continue
                if imghdr.what(path) in ['jpeg', 'png', 'jpg']:
                    image_paths.append(path)
            self.src_paths = sorted(image_paths)
            self.current_dir.setText(self.active_image_dir)
            self.image_idx.setRange(1, len(self.src_paths))
            self.goto_image(1)

    def set_label_dir(self):
        dialog = QFileDialog(self, 'Select Label Directory')
        dialog.setFileMode(QFileDialog.Directory)
        dialog.setNameFilters(["Any Files (*)"])
        dialog.setViewMode(QFileDialog.List)
        if dialog.exec():
            selected_dir = dialog.selectedFiles()[0]
            if not os.path.isdir(selected_dir):
                selected_dir = os.path.dirname(selected_dir)
            self.active_label_dir = selected_dir
            self.current_label_dir.setText(self.active_label_dir)
            with open(self.meta_file, 'a+') as f:
                f.write('Set Label Dir: ' + self.active_label_dir + '\n')
            self.label_dir = ensure_dir(os.path.join(self.active_label_dir, self.label_options.currentText().replace(' ', '_')))
            if self.active_image_dir is not None:
                image_name = os.path.basename(self.src_paths[self.current_idx])
                image_name, suff = os.path.splitext(image_name)
                self.current_label_path = os.path.join(self.label_dir, image_name + '_label.png')
                if os.path.exists(self.current_label_path):
                    self.viewer.setSegLayer(QPixmap(self.current_label_path))
                else:
                    self.viewer.resetSegLayer()
                self.viewer.changed = False

    def label_changed(self, value):
        if len(self.src_paths) > 0:
            if self.viewer.changed:
                result = self.show_save_warning()
                if result == QMessageBox.Save:
                    self.saveSegmentation()
                elif result == QMessageBox.Cancel:
                    self.label_options.revert()
                    return
                elif result == QMessageBox.Discard:
                    pass
            self.label_dir = ensure_dir(os.path.join(self.active_label_dir, value.replace(' ', '_')))
            image_name = os.path.basename(self.src_paths[self.current_idx])
            image_name, suff = os.path.splitext(image_name)
            self.current_label_path = os.path.join(self.label_dir, image_name + '_label.png')
            if os.path.exists(self.current_label_path):
                self.viewer.setSegLayer(QPixmap(self.current_label_path))
            else:
                self.viewer.resetSegLayer()
            self.viewer.changed = False

    def goto_image(self, idx):
        if len(self.src_paths) > 0:
            if self.viewer.changed:
                result = self.show_save_warning()
                if result == QMessageBox.Save:
                    self.saveSegmentation()
                elif result == QMessageBox.Cancel:
                    self.image_idx.setValue(self.prev_idx, quiet=True)
                    return
                elif result == QMessageBox.Discard:
                    pass
            self.viewer.clear_history()
            self.current_idx = idx - 1
            self.viewer.setPhoto(QPixmap(self.src_paths[self.current_idx]))
            image_name = os.path.basename(self.src_paths[self.current_idx])
            image_name, suff = os.path.splitext(image_name)
            self.image_title.setText(image_name)
            self.current_label_path = os.path.join(self.label_dir, image_name + '_label.png')
            if os.path.exists(self.current_label_path):
                self.viewer.setSegLayer(QPixmap(self.current_label_path))
            self.prev_idx = idx

    def skipto_next(self):
        # print(self.current_idx)
        if self.current_idx < len(self.src_paths):
            check_idx = self.current_idx
            found = False
            while check_idx < len(self.src_paths):
                # print(check_idx, len(self.src_paths))
                image_name = os.path.basename(self.src_paths[check_idx])
                image_name, suff = os.path.splitext(image_name)
                label_path = os.path.join(self.label_dir, image_name + '_label.png')
                if os.path.exists(label_path):
                    check_idx += 1
                    continue
                else:
                    found = True
                    break
            if found:
                self.image_idx.setValue(check_idx + 1)
            else:
                return QMessageBox.warning(self, "Segmenter Tool", "No unlabeled images found for label type '{}'".format(self.label_options.currentText()),
                                   QMessageBox.Ok, QMessageBox.Ok)

    def skipto_next_label(self):
        if self.current_idx < len(self.src_paths):
            check_idx = self.current_idx + 1
            found = False
            while check_idx < len(self.src_paths):
                image_name = os.path.basename(self.src_paths[check_idx])
                image_name, suff = os.path.splitext(image_name)
                label_path = os.path.join(self.label_dir, image_name + '_label.png')
                if os.path.exists(label_path):
                    found = True
                    break
                else:
                    check_idx += 1
                    continue
            if found:
                self.image_idx.setValue(check_idx + 1)
            else:
                return QMessageBox.warning(self, "Segmenter Tool", "No labeled images found for label type '{}'".format(self.label_options.currentText()),
                                   QMessageBox.Ok, QMessageBox.Ok)

    def goto_next(self):
        if self.prev_idx < len(self.src_paths):
            self.image_idx.setValue(self.prev_idx + 1)

    def goto_previous(self):
        if self.prev_idx > 1:
            self.image_idx.setValue(self.prev_idx - 1)

    def saveSegmentation(self):
        if self.viewer.hasPhoto():
            segmentation = QImage_to_CVMat(self.viewer.seg_image)
            cv2.imwrite(self.current_label_path, segmentation)
            self.viewer.changed = False

    def run_grabcut(self):
        if self.viewer.hasPhoto():
            # self.gc_spinner.start()
            image = QImage_to_CVMat(QPixmap(self.src_paths[self.current_idx]).toImage())[:, :, :3]
            segmentation = QImage_to_CVMat(self.viewer.seg_image)
            rescale_factor = 0.125
            image_height, image_width = image.shape[:2]
            image = cv2.resize(image, (int(image_width * rescale_factor), int(image_height * rescale_factor)), interpolation=cv2.INTER_NEAREST)
            new_segmentation = cv2.resize(segmentation, (int(image_width * rescale_factor), int(image_height * rescale_factor)), interpolation=cv2.INTER_NEAREST)

            pfg, fg, bg, pbg = np.split(new_segmentation / 255, segmentation.shape[-1], axis=-1)
            pbg = 1 - pbg
            has_bg = (pbg.sum() + bg.sum()) > 0
            has_fg = (pfg.sum() + fg.sum()) > 0
            if not (has_bg and has_fg):
                if not has_bg:
                    message = 'You must select some background or empty area.'
                elif not has_fg:
                    message = 'You must select some foreground or possible foreground.'
                x = QMessageBox.critical(self, "Segmenter Tool", message, QMessageBox.Ok)
                # self.gc_spinner.stop()
                return

            mask = (cv2.GC_FGD * fg + cv2.GC_BGD * bg + cv2.GC_PR_FGD * pfg + cv2.GC_PR_BGD * pbg).astype(np.uint8)
            bgd_model = np.zeros((1, 65), np.float64)
            fgd_model = np.zeros((1, 65), np.float64)
            mask, _, _ = cv2.grabCut(image, mask, None, bgd_model, fgd_model, 2, cv2.GC_INIT_WITH_MASK)
            pfg = np.where(mask == cv2.GC_PR_FGD, 255, 0) + np.where(mask == cv2.GC_FGD, 255, 0)
            # fg = np.where(mask==cv2.GC_FGD, 255, 0)
            # bg = np.where(mask==cv2.GC_BGD, 255, 0)
            fg = np.zeros_like(pfg)
            bg = np.zeros_like(pfg)
            pbg = np.zeros_like(pfg)
            # pbg = np.where(mask == cv2.GC_PR_BGD, 0, 255) + np.where(mask == cv2.GC_BGD)
            # new_segmentation = np.concatenate([pfg, fg, bg, pbg], axis=2).astype(np.uint8)
            new_segmentation = cv2.resize(pfg, (image_width, image_height), interpolation=cv2.INTER_NEAREST)
            new_segmentation = new_segmentation - segmentation[:, :, 1]
            new_segmentation = np.concatenate([new_segmentation[:, :, np.newaxis], segmentation[:, :, 1:3]], axis=2)
            new_segmentation = np.concatenate([new_segmentation, new_segmentation.max(axis=2, keepdims=True)], axis=2)
            # new_segmentation = cv2.resize(new_segmentation, (image_width, image_height), interpolation=cv2.INTER_NEAREST)
            # new_segmentation[:, :, 0] = new_segmentation[:, :, 0] - segmentation[:, :, 1]
            # new_segmentation[:, :, 1:3] = segmentation[:, :, 1:3]
            # new_segmentation[:, :, 3] = new_segmentation.max(axis=2)
            # new_segmentation = np.where((segmentation[:, :, 1:2] == 255) + (segmentation[:, :, 2:3] == 255), segmentation, new_segmentation)
            tempdir = tempfile.mkdtemp()
            temp_png = os.path.join(tempdir, 'grabcut.png')
            cv2.imwrite(temp_png, new_segmentation)
            self.viewer.setSegLayer(QPixmap(temp_png))
            self.viewer.changed = True
            os.remove(temp_png)
            os.removedirs(tempdir)
            # self.gc_spinner.stop()

    def show_save_warning(self):
        return QMessageBox.warning(self, "Segmenter Tool", 'This segmentation has been modified.\nDo you want to save your changes?',
                                   QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel, QMessageBox.Save)

class SegmenterWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.setWindowTitle("Segmenter Tool")
        self.widget = SegmenterWidget()
        self.menu = self.menuBar()
        self.file_menu = self.menu.addMenu("File")
        self.edit_menu = self.menu.addMenu("Edit")

        save_action = QAction("Save", self)
        save_action.setShortcut("Ctrl+S")
        save_action.triggered.connect(self.widget.saveSegmentation)

        exit_action = QAction("Exit", self)
        exit_action.setShortcut("Ctrl+Q")
        exit_action.triggered.connect(self.exit_app)

        undo_action = QAction("Undo", self)
        undo_action.setShortcut('Ctrl+Z')
        undo_action.triggered.connect(self.widget.viewer.undo)

        redo_action = QAction("Redo", self)
        redo_action.setShortcut('Shift+Ctrl+Z')
        redo_action.triggered.connect(self.widget.viewer.redo)

        grabcut_action = QAction("Run GrabCut", self)
        grabcut_action.setShortcut("Ctrl+G")
        grabcut_action.triggered.connect(self.widget.run_grabcut)

        hide_action = QAction('Show/Hide Image', self)
        hide_action.setShortcut("Shift+H")
        hide_action.triggered.connect(self.widget.viewer.hide_image)

        self.file_menu.addAction(save_action)
        self.file_menu.addAction(exit_action)
        self.edit_menu.addAction(undo_action)
        self.edit_menu.addAction(redo_action)
        self.edit_menu.addAction(grabcut_action)
        self.edit_menu.addAction(hide_action)

        self.setCentralWidget(self.widget)

    @Slot()
    def exit_app(self, checked):
        if self.widget.viewer.changed:
            result = self.widget.show_save_warning()
            if result == QMessageBox.Save:
                self.widget.saveSegmentation()
            elif result == QMessageBox.Cancel:
                return
            elif result == QMessageBox.Discard:
                pass
        QApplication.quit()


import sys
import pdb
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import matplotlib.cm as cmx
import numpy as np

kernels = []
data = []
if len(sys.argv) != 2:
    print("Execute as :\npython {} {}".format(sys.argv[0],"filename.txt"))
    exit(1)
with open(sys.argv[1]) as f:
    for lineNum,line in enumerate(f):
        if 0 == lineNum:
            kernels = eval(line.strip())
        else:
            kernelNum, vals = line.strip().split(":")
            data.append(eval(vals))

data = np.asarray(data).T
jet = cm = plt.get_cmap('Dark2')
cNorm  = colors.Normalize(vmin=0, vmax=len(kernels))
scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=jet)

x_axis = range(1,len(data[0])+1)
# handles = []
for idx, kernel in enumerate(kernels):
    colorVal = scalarMap.to_rgba(idx)
    handle = plt.scatter(x_axis,data[idx,:]*100,color=colorVal,label=kernel)
    plt.plot(x_axis,data[idx,:]*100,color=colorVal)
    # print(handle.get_label())
    # print(handle)
    # handles.append(handle)

# plt.legend(handles,loc='lower right')
plt.xticks(range(14))
plt.legend(loc='lower right')
plt.xlabel("13 functional categories")
plt.ylabel("Accuracy (in percent)")
plt.grid()
plt.show()

# func_props = data.shape[1]
# kernel_count = data.shape[0]
#
# cNorm  = colors.Normalize(vmin=0, vmax=len(data.T))
# scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=jet)
# f, axarr = plt.subplots(1, func_props)
# for idx, data_func_prop in enumerate(data.T):
#     arr = axarr[idx]
#     # colorVal = scalarMap.to_rgba(idx)
#     # handle = arr.scatter([0]*kernel_count,data_func_prop*100,color=colorVal,label=kernel)
#     colorVal = scalarMap.to_rgba(idx)
#     handle = arr.scatter([0]*kernel_count,data_func_prop*100)
#
#     arr.set_title("Prop = {}".format(idx))
#     arr.grid()
#     arr.tick_params(axis='x',which='both',bottom='off',labelbottom='off')
#
# arr.legend(loc='center right')
# # plt.xlabel("13 functional categories")
# # plt.ylabel("Accuracy (in percent)")
# # plt.grid()
# plt.show()


# Author: Andrew.M.G.Reynen
from __future__ import print_function
from copy import deepcopy
from future.utils import iteritems

from PyQt5 import QtWidgets,QtGui,QtCore
from PyQt5.QtCore import Qt
import pyproj

from CustomFunctions import dict2Text, text2Dict
from lazylyst.UI.CustomPen import Ui_customPenDialog
from lazylyst.UI.BasePen import Ui_basePenDialog
from lazylyst.UI.ComboBox import Ui_comboBoxDialog
from lazylyst.UI.MapProj import Ui_mapProjDialog
from lazylyst.UI.ListEntry import Ui_listEntryDialog

# Default Preferences
def defaultPreferences(main):
    pref={
    'staPerPage':Pref(tag='staPerPage',val=6,dataType=int,
                      func=main.updateStaPerPage,condition={'bound':[1,30]},
                      tip='Number of stations (trace widgets) to display on each page'),
    'evePreTime':Pref(tag='evePreTime',val=-30,dataType=float,
                      tip='Time in seconds prior to the selected pick file names time to grab data'),
    'evePostTime':Pref(tag='evePostTime',val=60,dataType=float,
                      tip='Time in seconds after to the selected pick file names time to grab data'),
    'eveIdGenStyle':Pref(tag='eveIdGenStyle',val='next',dataType=str,
                         dialog='ComboBoxDialog',condition={'isOneOf':['fill','next']},
                         tip='Style used to generate a new empty pick files ID (when double clicking the archive event widget)'),
    'eveSortStyle':Pref(tag='eveSortStyle',val='time',dataType=str,
                        dialog='ComboBoxDialog',func=main.updateEveSort,condition={'isOneOf':['id','time']},
                        tip='How the archive list widget is sorted, also sorts hot variable pickFiles and pickTimes'),
    'cursorStyle':Pref(tag='cursorStyle',val='arrow',dataType=str,
                        dialog='ComboBoxDialog',func=main.updateCursor,condition={'isOneOf':['arrow','cross']},
                        tip='Cursor icon to use while hovering over plots'),
    'remExcessPicksStyle':Pref(tag='remExcessPicksStyle',val='oldest',dataType=str,
                        dialog='ComboBoxDialog',condition={'isOneOf':['oldest','closest','furthest']},
                        tip='Which excess pick(s) will be deleted when manually adding picks'),
    'mapProj':Pref(tag='mapProj',
                   val={'type':'Simple',
                        'epsg':'4326',
                        'simpleType':'AEA Conic',
                        'zDir':'end',
                        'units':'km',
                        'func':None,
                        'funcInv':None},
                    dataType=dict,
                    dialog='MapProjDialog',func=main.updateMapProj,
                    tip='Projection to be applied when converting Lat,Lon,Ele to X,Y,Z',
                    loadOrder=0),
    'pythonPathAdditions':Pref(tag='pythonPathAdditions',val=[],dataType=list,
                               dialog='ListEntryDialog',tip='Directories to add to the python path',
                               func=main.updatePythonPath),
    'pickTypesMaxCountPerSta':Pref(tag='pickTypesMaxCountPerSta',val={'P':1,'S':1},dataType=dict,
                                   func=main.updatePagePicks,condition={'bound':[1,999]},
                                   tip='Max number of picks of a given phase type allowed on any individual trace widget'),
    'basePen':Pref(tag='basePen',val={'widgetText':[14474460,1.0,0.0,False], # [Color,Width,Depth,UpdateMe]
                                      'traceBackground':[0,1.0,0.0,False],
                                      'timeBackground':[0,1.0,0.0,False],
                                      'imageBackground':[0,1.0,0.0,False],
                                      'imageBorder':[16777215,2.0,-1.0,False],
                                      'mapBackground':[0,1.0,0.0,False],
                                      'mapStaDefault':[16777215,4.0,0.0,False],
                                      'mapCurEve':[16776960,3.0,2.0,False],
                                      'mapPrevEve':[13107400,2.0,1.0,False],
                                      'mapPolygon':[3289800,1.0,10.0,False],
                                      'archiveBackground':[0,1.0,0.0,False],
                                      'archiveAvailability':[65280,1.0,0.0,False],
                                      'archiveSpanSelect':[3289800,1.0,0.0,False],
                                      'archiveCurEve':[16711680,3.0,1.0,False],
                                      'archivePrevEve':[48865,1.0,0.0,False],},
                    dataType=dict,dialog='BasePenDialog',func=main.updateBaseColors,
                    tip='Defines the base pen values for the main widgets'),
    'customPen':Pref(tag='customPen',val={'default':[16777215,1.0,0.0], # [Color,Width,Depth]
                                          'noStaData':[3289650,1.0,0.0],
                                          'noTraceData':[8224125,1.0,0.0],
                                          'goodMap':[65280,1.0,0.0],
                                          'poorMap':[16711680,1.0,0.0],
                                          'highlight':[255,1.0,2.0],
                                          'lowlight':[13158600,0.3,1.0],},dataType=dict,
                    dialog='CustomPenDialog',func=main.updateCustomPen,
                    tip='Defines the custom pen values referenced by PenAssign hot variables'),
    'pickPen':Pref(tag='pickPen',val={'default':[16777215,1.0,4.0],
                                      'P':[65280,1.0,5.0],
                                      'S':[16776960,1.0,5.0],},dataType=dict,
                    dialog='CustomPenDialog',func=main.updatePagePicks,
                    tip='Defines the pen values for the pick lines'),
    }
    return pref

# Capabilities for preferences
class Pref(object):
    def __init__(self,tag=None,val=None,dataType=str,
                 dialog='LineEditDialog',
                 func=None,condition={},tip='',loadOrder=999):
        self.tag=tag # The preference key, and user visible name
        self.val=val # The preference value
        self.dataType=dataType # What kind of data is expected upon update
        self.dialog=dialog # The dialog which will pop up to return a value
        self.func=func # Function which is called on successful updates
        self.condition=condition # Key-word conditionals (see "LineEditDialog" in this file)
        self.tip=tip # Short description of the preference
        self.loadOrder=loadOrder # Priority in loading the preferencing (lower value loaded first)

    # Return a deep copy of the objects value
    def getVal(self):
        val=deepcopy(self.val)
        return val

    # If the key was asked to be updated
    def update(self,hostWidget,init=False):
        # If this is the original initalization, don't ask for new value
        if not init:
            # Use the correct dialog...
            # ...text entry dialog
            if self.dialog=='LineEditDialog':
                if self.dataType==dict:
                    initText=dict2Text(self.val)
                else:
                    initText=str(self.val)
                val,ok=LineEditDialog.returnValue(tag=self.tag,initText=initText,
                                                  condition=self.condition,
                                                  dataType=self.dataType)
            # ...selecting from a list
            elif self.dialog=='ComboBoxDialog':
                val,ok=ComboBoxDialog.returnValue(self.val,self.condition['isOneOf'],self.tag)
            # ...default widget colors
            elif self.dialog=='BasePenDialog':
                BasePenDialog(self.val).exec_()
                # The checks and updates to the preference value are done within the dialog
                val,ok=self.val,True
            # ...custom colors and widths
            elif self.dialog=='CustomPenDialog':
                CustomPenDialog(self.val,self.tag).exec_()
                # The checks and updates to the preference value are done within the dialog
                val,ok=self.val,True
            # ...defining the map projection
            elif self.dialog=='MapProjDialog':
                val,ok=ProjDialog.returnValue(self.val)
            # ...editing items on a list
            elif self.dialog=='ListEntryDialog':
                val,ok=ListDialog.returnValue(self.tag,self.val)
            else:
                print('New dialog?')
                val,ok=None,False
            # If the dialog was canceled, skip
            if not ok:
                return
            # If the no value was returned, skip
            if val==None:
                print('Value did not conform to '+str(self.dataType)+' and conditionals '+str(self.condition))
                return
            # Update the preference value
            self.val=val
        # If the value was updated, queue off its function
        if self.func!=None:
            self.func(init=init)

# Dialog with line edit, allows for some initial text, and forced data type and condition
class LineEditDialog(QtWidgets.QDialog):
    def __init__(self,parent,tag,initText,condition,dataType):
        super(LineEditDialog, self).__init__(parent)
        self.setWindowTitle(tag)
        self.cond=condition
        self.dataType=dataType

        # Set up the layout and line edit
        layout = QtWidgets.QVBoxLayout(self)
        self.le = QtWidgets.QLineEdit(self)
        self.le.setText(initText)
        layout.addWidget(self.le)

        # OK and Cancel buttons
        self.buttons = QtWidgets.QDialogButtonBox(
            QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel,
            Qt.Horizontal, self)
        layout.addWidget(self.buttons)
        self.buttons.accepted.connect(self.accept)
        self.buttons.rejected.connect(self.reject)

    # Get the text from the line edit
    def lineEditValue(self):
        val=self.le.text()
        # See first that the data type conforms
        try:
            # Allow user to type in the dictionary a bit easier
            if self.dataType==dict:
                val=text2Dict(val)
                if val=={}:
                    print('No keys are present in the dictionary')
                    return None
            else:
                val=self.dataType(val)
        except:
            return None
        # Loop through all values (usually just one, can be more for dictionary)
        if self.dataType==dict:
            vals=[aVal for key,aVal in iteritems(val)]
        else:
            vals=[val]
        # ...and check against the built in conditionals
        keys=[key for key in self.cond.keys()]
        for aVal in vals:
            if 'bound' in keys:
                if aVal<self.cond['bound'][0] or aVal>self.cond['bound'][1]:
                    return None
        return val

    # Static method to create the dialog and return value
    @staticmethod
    def returnValue(parent=None,tag='',initText='',condition={},dataType=str):
        dialog = LineEditDialog(parent,tag,initText,condition,dataType)
        result = dialog.exec_()
        return dialog.lineEditValue(), result==QtWidgets.QDialog.Accepted

# Dialog window for editing the basic widget colors
class BasePenDialog(QtWidgets.QDialog, Ui_basePenDialog):
    def __init__(self,tpDict,parent=None):
        QtWidgets.QDialog.__init__(self,parent)
        self.setupUi(self)
        self.tpDict=tpDict
        self.keyOrder=sorted(self.tpDict.keys()) # Used to reference back before the last user change
        # Fill in the current customPen information (before setting functionality, as functionality has a "onChanged" signal)
        self.fillDialog()
        # Give the dialog some functionaly
        self.setFunctionality()

    # Set up some functionality to the custom pen dialog
    def setFunctionality(self):
        self.tpTable.itemDoubleClicked.connect(self.updatePenColor)
        self.tpTable.itemChanged.connect(self.updateItemText)

    # Fill the dialog with info relating to the current customPen dictionary
    def fillDialog(self):
        # Make the table large enough to hold all current information
        self.tpTable.setRowCount(len(self.tpDict))
        self.tpTable.setColumnCount(4)
        # Set the headers
        self.tpTable.setHorizontalHeaderLabels(['Tag','Color','Width','Depth'])
        # Enter data onto Table
        for m,key in enumerate(sorted(self.tpDict.keys())):
            # Generate the table items...
            tagItem=QtWidgets.QTableWidgetItem(key)
            tagItem.setFlags(Qt.ItemIsEnabled)
            penItem=QtWidgets.QTableWidgetItem('')
            penItem.setFlags(Qt.ItemIsEnabled)
            penItem.setBackground(QtGui.QColor(self.tpDict[key][0]))
            widItem=QtWidgets.QTableWidgetItem(str(self.tpDict[key][1]))
            depItem=QtWidgets.QTableWidgetItem(str(self.tpDict[key][2]))
            # Put items in wanted position
            self.tpTable.setItem(m,0,tagItem)
            self.tpTable.setItem(m,1,penItem)
            self.tpTable.setItem(m,2,widItem)
            self.tpTable.setItem(m,3,depItem)
        # Resize the dialog
        self.tpTable.resizeColumnsToContents()

    # Update the pen color for a given tag
    def updatePenColor(self,item):
        if item.column()!=1:
            return
        itemKey=self.keyOrder[item.row()]
        # Go get a new color
        colorDialog=QtWidgets.QColorDialog()
        val=colorDialog.getColor(QtGui.QColor(self.tpDict[itemKey][0]),self)
        # Set the new color (if the dialog was not canceled)
        if val.isValid():
            val=val.rgba()
            item.setBackground(QtGui.QColor(val))
            self.tpDict[itemKey][0]=val
            # Mark that this item was edited
            self.tpDict[itemKey][3]=True

    # Update the text values, if changed
    def updateItemText(self,item):
        if item.column()in [0,1]:
            return
        # The key, prior to changes
        itemKey=self.keyOrder[item.row()]
        # Updating the width or depth values
        if item.column() in [2,3]:
            prefIdx=item.column()-1
            try:
                val=float(item.text())
            except:
                val=-99999
            # Ensure the width value is reasonable, if not change back the original
            if prefIdx==1 and (val<0 or val>10):
                print('Width should be in the range [0,10]')
                item.setText(str(self.tpDict[itemKey][prefIdx]))
            elif prefIdx==2 and (val<-10 or val>=10):
                print('Depth should be in the range [-10,10)')
                item.setText(str(self.tpDict[itemKey][prefIdx]))
            # If passed checks, update the tpDict with the new width or depth
            else:
                self.tpDict[itemKey][prefIdx]=float(item.text())
                # Mark that this item was edited
                self.tpDict[itemKey][3]=True

# Dialog window for editing the custom colors and widths
class CustomPenDialog(QtWidgets.QDialog, Ui_customPenDialog):
    def __init__(self,tpDict,tag,parent=None):
        QtWidgets.QDialog.__init__(self,parent)
        self.setupUi(self)
        self.setWindowTitle(tag)
        self.tpDict=tpDict
        self.keyOrder=sorted(self.tpDict.keys()) # Used to reference back before the last user change
        # Fill in the current customPen information (before setting functionality, as functionality has a "onChanged" signal)
        self.fillDialog()
        # Give the dialog some functionaly
        self.setFunctionality()

    # Set up some functionality to the custom pen dialog
    def setFunctionality(self):
        self.tpTable.itemDoubleClicked.connect(self.updatePenColor)
        self.tpTable.itemChanged.connect(self.updateItemText)
        self.tpInsertButton.clicked.connect(self.insertPen)
        self.tpDeleteButton.clicked.connect(self.deletePen)

    # Fill the dialog with info relating to the current customPen dictionary
    def fillDialog(self):
        # Make the table large enough to hold all current information
        self.tpTable.setRowCount(len(self.tpDict))
        self.tpTable.setColumnCount(4)
        # Set the headers
        self.tpTable.setHorizontalHeaderLabels(['Tag','Color','Width','Depth'])
        # Enter data onto Table
        for m,key in enumerate(sorted(self.tpDict.keys())):
            # Generate the table items...
            tagItem=QtWidgets.QTableWidgetItem(key)
            if key=='default':
                tagItem.setFlags(Qt.ItemIsEnabled)
            penItem=QtWidgets.QTableWidgetItem('')
            penItem.setFlags(Qt.ItemIsEnabled)
            penItem.setBackground(QtGui.QColor(self.tpDict[key][0]))
            widItem=QtWidgets.QTableWidgetItem(str(self.tpDict[key][1]))
            depItem=QtWidgets.QTableWidgetItem(str(self.tpDict[key][2]))
            # Put items in wanted position
            self.tpTable.setItem(m,0,tagItem)
            self.tpTable.setItem(m,1,penItem)
            self.tpTable.setItem(m,2,widItem)
            self.tpTable.setItem(m,3,depItem)

    # Update the pen color for a given tag
    def updatePenColor(self,item):
        if item.column()!=1:
            return
        itemKey=self.keyOrder[item.row()]
        # Go get a new color
        colorDialog=QtWidgets.QColorDialog()
        val=colorDialog.getColor(QtGui.QColor(self.tpDict[itemKey][0]),self)
        # Set the new color (if the dialog was not canceled)
        if val.isValid():
            val=val.rgba()
            item.setBackground(QtGui.QColor(val))
            self.tpDict[itemKey][0]=val

    # Update the text values, if changed
    def updateItemText(self,item):
        if item.column()==1:
            return
        # Disconnect itself, changes occur here weird looping otherwise
        self.tpTable.itemChanged.disconnect(self.updateItemText)
        # The key, prior to changes
        itemKey=self.keyOrder[item.row()]
        # Updating the width or depth values
        if item.column() in [2,3]:
            prefIdx=item.column()-1
            try:
                val=float(item.text())
            except:
                val=-99999
            # Ensure the width value is reasonable, if not change back the original
            if prefIdx==1 and (val<0 or val>10):
                print('Width should be in the range [0,10]')
                item.setText(str(self.tpDict[itemKey][prefIdx]))
            elif prefIdx==2 and (val<-10 or val>=10):
                print('Depth should be in the range [-10,10)')
                item.setText(str(self.tpDict[itemKey][prefIdx]))
            # If passed checks, update the tpDict with the new width or depth
            else:
                self.tpDict[itemKey][prefIdx]=float(item.text())
        # Updating the tag
        elif item.column()==0:
            # If the same, do nothing
            if item.text()==itemKey:
                pass
            # Do not allow duplicate tags
            elif item.text() in [key for key in self.tpDict.keys() if key!=itemKey]:
                print('This tag is already present, update denied')
                item.setText(itemKey)
            else:
                # If passed checks, update the tpDict with the new tag
                self.tpDict[str(item.text())]=self.tpDict[itemKey]
                self.tpDict.pop(itemKey)
                # Update the key order, so can reference back before changes
                self.keyOrder[item.row()]=str(item.text())
        # Reconnect to OnChanged signal
        self.tpTable.itemChanged.connect(self.updateItemText)

    # Insert a new pen
    def insertPen(self):
        # If the "NewPen" key is still present, ask to change it...
        if 'NewPen' in [key for key in self.tpDict.keys()]:
            print('Change the tag "NewPen", to be able to add another custom pen"')
            return
        # Disconnect the OnChanged signal (as this would trigger it)
        self.tpTable.itemChanged.disconnect(self.updateItemText)
        # Add a row with the default parameters
        m=self.tpTable.rowCount()
        self.tpTable.setRowCount(m+1)
        self.tpTable.setItem(m,0,QtWidgets.QTableWidgetItem('NewPen'))
        penItem=QtWidgets.QTableWidgetItem('')
        penItem.setFlags(Qt.ItemIsEnabled)
        self.tpTable.setItem(m,1,penItem)
        self.tpTable.setItem(m,2,QtWidgets.QTableWidgetItem('1.0'))
        self.tpTable.setItem(m,3,QtWidgets.QTableWidgetItem('0.0'))
        # Add it also to the dictionary
        self.tpDict['NewPen']=[4294967295,1.0,0.0]
        # Update the keyOrder
        self.keyOrder.append('NewPen')
        # Reconnect to OnChanged signal
        self.tpTable.itemChanged.connect(self.updateItemText)

    # Delete the currently selected pen
    def deletePen(self):
        idx=self.tpTable.currentRow()
        key=str(self.tpTable.item(idx,0).text())
        # Not allowed to delete default
        if key=='default':
            print('Cannot delete the pen tagged default')
            return
        # Disconnect the OnChanged signal (as this would trigger it)
        self.tpTable.itemChanged.disconnect(self.updateItemText)
        self.tpTable.removeRow(idx)
        self.keyOrder.pop(idx)
        self.tpDict.pop(key)
        # Reconnect to OnChanged signal
        self.tpTable.itemChanged.connect(self.updateItemText)

# Dialog to get a specific date time back
class DateDialog(QtWidgets.QDialog):
    def __init__(self, parent = None):
        super(DateDialog, self).__init__(parent)

        # Give a window title
        self.setWindowTitle('Date Time Select')

        layout = QtWidgets.QVBoxLayout(self)
        # Widget for editing the date
        self.datetime = QtWidgets.QDateTimeEdit(self)
        self.datetime.setDisplayFormat('yyyy-MM-dd hh:mm:ss')
        layout.addWidget(self.datetime)

        # OK and Cancel buttons
        buttons = QtWidgets.QDialogButtonBox(
            QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel,
            Qt.Horizontal, self)
        buttons.accepted.connect(self.accept)
        buttons.rejected.connect(self.reject)
        layout.addWidget(buttons)

    # Get current date and time from the dialog
    def dateTime(self):
        return self.datetime.dateTime()

    # Static method to create the dialog and return [timestamp, accepted]
    @staticmethod
    def getDateTime(bound,parent = None):
        # Start dialog
        dialog = DateDialog(parent)
        # Set the previous string value
        prevDateTime=QtCore.QDateTime()
        prevDateTime.setTimeSpec(Qt.UTC)
        prevDateTime.setTime_t(int(bound))
        dialog.datetime.setDateTime(prevDateTime)
        # Get and return value
        result = dialog.exec_()
        dateTime=dialog.dateTime()
        dateTime.setTimeSpec(Qt.UTC)
        newBound = dateTime.toTime_t()
        return newBound, result == QtWidgets.QDialog.Accepted

# Dialog window for selecting one of a few options
class ComboBoxDialog(QtWidgets.QDialog, Ui_comboBoxDialog):
    def __init__(self,parent,curVal,acceptVals,tag):
        QtWidgets.QDialog.__init__(self,parent)
        self.setupUi(self)
        self.setWindowTitle(tag)
        # Fill in the combo dialog box
        self.comboBox.addItems(acceptVals)
        # Set the current text to be the current preference value
        index = self.comboBox.findText(curVal)
        if index >= 0:
            self.comboBox.setCurrentIndex(index)

    # Static method to create the dialog and return the selected value
    @staticmethod
    def returnValue(curVal,acceptVals,tag,parent=None):
        dialog=ComboBoxDialog(parent,curVal,acceptVals,tag)
        result=dialog.exec_()
        return dialog.comboBox.currentText(), result==QtWidgets.QDialog.Accepted

# Dialog to hold a list of strings
class ListDialog(QtWidgets.QDialog, Ui_listEntryDialog):
    def __init__(self,tag,initList,parent=None):
        QtWidgets.QDialog.__init__(self,parent)
        self.setupUi(self)
        # Set name of the preference being changed
        self.setWindowTitle(tag)
        self.setFunctionality()
        self.fillDialog(initList)

    # Set up some functionality to the list entry dialog
    def setFunctionality(self):
        self.entryAddButton.clicked.connect(lambda x:self.addEntry())
        self.entryDelButton.clicked.connect(self.removeEntry)
        self.entryListWidget.keyPressedSignal.connect(self.doKeyPress)

    # Fill the list with current preference entries
    def fillDialog(self,initList):
        for entry in initList:
            self.addEntry(entry)

    # Handle the keys given by the list widget
    def doKeyPress(self):
        if self.entryListWidget.key==Qt.Key_Insert:
            self.addEntry()
        elif self.entryListWidget.key==Qt.Key_Delete:
            self.removeEntry()
        elif self.entryListWidget.key==Qt.Key_Backspace:
            self.editEntry()

    # Remove an entry from the list
    def removeEntry(self):
        try:
            if not self.entryListWidget.currentItem().isSelected():
                return
            self.entryListWidget.takeItem(self.entryListWidget.currentRow())
        except:
            pass

    # Add an item to the list
    def addEntry(self,text='#NewEntry'):
        if text in self.entryListWidget.visualListOrder():
            return
        item=QtWidgets.QListWidgetItem()
        item.setText(text)
        # Allow the item to be edited by clicking on it
        item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable)
        self.entryListWidget.addItem(item)

    # Edit an entry in the list
    def editEntry(self):
        index = self.entryListWidget.currentIndex()
        if index.isValid():
            item = self.entryListWidget.itemFromIndex(index)
            if not item.isSelected():
                item.setSelected(True)
            self.entryListWidget.edit(index)

    # Return the lists unique entries
    @staticmethod
    def returnValue(tag,initList):
        dialog=ListDialog(tag,initList)
        result=dialog.exec_()
        return list(set(dialog.entryListWidget.visualListOrder())),result==QtWidgets.QDialog.Accepted

# Dialog window for selecting the projection to be used on the map
class ProjDialog(QtWidgets.QDialog,Ui_mapProjDialog):
    def __init__(self,projDict,parent=None):
        QtWidgets.QDialog.__init__(self,parent)
        self.setupUi(self)
        self.projDict=projDict
        # Fill the dialog with current values
        self.fillDialog()
        # Give the dialog some functionality
        self.setFunctionality()

    # Set up some functionality to the action set up dialog
    def setFunctionality(self):
        self.epsgLineEdit.editingFinished.connect(self.checkValidEpsg)
        self.simpleProjComboBox.currentIndexChanged.connect(self.toggleUnitCombo)
        self.simpleProjRadio.clicked.connect(self.toggleProjType)
        self.customProjRadio.clicked.connect(self.toggleProjType)

    # Fill the dialog with info relating to the current map projection
    def fillDialog(self):
        # Fill combo boxes
        self.simpleProjComboBox.addItems(['None','AEA Conic','UTM'])
        # Set values with current projection...
        # ...current epsg code
        self.epsgLineEdit.setText(str(self.projDict['epsg']))
        # ...type of the projection (simple/custom)
        if self.projDict['type']=='Simple':
            self.simpleProjRadio.setChecked(True)
        else:
            self.customProjRadio.setChecked(True)
        self.setComboValue(self.simpleProjComboBox,self.projDict['simpleType'])
        # ...direction of the z-axis (positive up/down)
        self.zDirCheckBox.setChecked(self.projDict['zDir']=='end')
        # ...projection type and units
        self.toggleProjType() # Toggle type first to fill unit combo box
        self.setComboValue(self.unitsComboBox,self.projDict['units'])

    # Set a combo lists value
    def setComboValue(self,comboBox,value):
        # Set the current text to be the current preference value
        index = comboBox.findText(value)
        if index >= 0:
            comboBox.setCurrentIndex(index)
        else:
            comboBox.setCurrentIndex(0)

    # Ensure EPSG code is valid
    def checkValidEpsg(self):
        if not self.epsgLineEdit.isEnabled():
            return
        curText=str(self.epsgLineEdit.text())
        # Ensure the code is an integer value
        try:
            int(curText)
            # Check if the EPSG code exists
            try:
                self.getProjFunc(curText)
                # If possible to get the code, toggle the units
                self.toggleUnitCombo()
                return
            except:
                print('The EPSG code was not valid')
        except:
            print('The EPSG code must be an integer value')
        # If got an invalid code revert to old value
        print('Reverting to intial EPSG')
        self.epsgLineEdit.setText(str(self.projDict['epsg']))

    # Get the projection function for a given EPSG code
    def getProjFunc(self,epsg):
        return pyproj.Proj(init='EPSG:'+epsg)

    # Get the current map projection dictionary
    def getCurProjDict(self):
        curDict={'type':'Simple' if self.simpleProjRadio.isChecked() else 'Custom',
                 'epsg':self.epsgLineEdit.text(),
                 'simpleType':self.simpleProjComboBox.currentText(),
                 'zDir':'end' if self.zDirCheckBox.isChecked() else 'enu',
                 'units':self.unitsComboBox.currentText(),
                 'func':None,
                 'funcInv':None}
        return curDict

    # Toggle between simple and custom projection types
    def toggleProjType(self):
        isSimple=self.simpleProjRadio.isChecked()
        self.epsgLineEdit.setEnabled(not isSimple)
        self.simpleProjComboBox.setEnabled(isSimple)
        # Toggle the available units (as can change between simple/custom)
        self.toggleUnitCombo()

    # Toggle which units are available for the current projection
    def toggleUnitCombo(self):
        curDict=self.getCurProjDict()
        self.unitsComboBox.clear()
        # If the projection is in degrees only
        if ((curDict['type']=='Simple' and curDict['simpleType']=='None') or
            (curDict['type']=='Custom' and self.getProjFunc(curDict['epsg']).is_latlong())):
            self.unitsComboBox.addItems(['deg'])
        else:
            self.unitsComboBox.addItems(['m','km','ft','yd','mi'])
        # Set the units to original if still present
        self.setComboValue(self.unitsComboBox,curDict['units'])

    # Static method to create the dialog and return the selected value
    @staticmethod
    def returnValue(projDict):
        dialog=ProjDialog(projDict)
        result=dialog.exec_()
        # Recheck the projection
        dialog.checkValidEpsg()
        return dialog.getCurProjDict(),result==QtWidgets.QDialog.Accepted


import argparse
import create_subnet
import create_router
import users_utility
import random

NAME = 'Name'
CIDR = 'Subnet'
PROVIDER = 'Provider'
USERNAME = 'Username'
EXTERNAL = 'External'

def create_linked_network(domain, project, network):
  ln = []
  print('create_linked_network(domain: {}, project: {}, network: {})'.format(domain, project, network))
  external = network[EXTERNAL] if network[EXTERNAL] != None else False
  domain_name = domain.name
  project_name = project.name
  # The sequence of steps is important, we must first create the network [n], then the subnet [s].
  # afterwards we get the provider network [p], and the provider networks subnet [b].
  # we then select an ip-address on the provider subnet's ip address range.
  # finally you can create a router using all the pieces that links your new network to the external network.
  n = create_network(domain_name, project_name, network[NAME], external)
  s = create_subnet.create_subnet(domain_name, project_name, network[NAME], n['id'], network[CIDR])
  p = create_network(domain_name, project_name, network[PROVIDER])
  i = p['subnets'][0]
  b = create_subnet.create_subnet(domain_name, project_name, i)
  a = select_ip(b['cidr'], b['allocation_pools'])
  k = network[PROVIDER] + "-Router"
  r = create_router.create_router(domain_name, project_name, k, p['id'], i, a, s['id'])
  ln.append(n)
  return ln

def create_network(domain, project, name, is_external=False):
  '''
  attempts to find a given network by name inside of a project, if the network is found we return it.
  otherwise we create a new network.
  '''

  print('create_network(domain, project, name, is_external)', domain, project, name, is_external)
  try:
    client = users_utility.create_neutron_client(domain, project)
    # find network:
    netz = client.list_networks()['networks']
    networks = list(filter(lambda a : a['name'] == name, netz))
    numNetworks = len(networks)

    print('create_network: networks found {}'.format(numNetworks))
    if numNetworks == 1:
      return networks[0]
    elif numNetworks == 0:
      return client.create_network({'network': {'name': name, 'admin_state_up': True, 'router:external': is_external}})['network']
    else:
      return None

  except Exception as ex:
      print("an error occured getting the network", ex, domain, project, name, is_external)

def select_ip(cidr, allocation_pools):
  print('select_ip(cidr, allocation_pools):', cidr, allocation_pools)
  ar = cidr.split('/')
  ip = ar[0]
  rg = ar[1]
  c = ip.split('.')
  print('select_ip: ip, r', ip, rg)

  if (rg == '24'): #subnet range /24 means the last value 0 can be replaced with any value within 0-255
    v = 0
    if(allocation_pools and len(allocation_pools) == 1):
      start, end = get_ip_range(allocation_pools[0])
      v = random.randint(start, end)
    else:
      v = random.randint(0,256)
    return c[0] + "." + c[1] + "." + c[2] + "." + str(v)
  else:
    return c[0] + "." + c[1] + "." + c[2] + "." + "?"

def get_ip_range(allocation_pool):
  print('get_ip_range(allocation_pool)', allocation_pool)
  start = allocation_pool['start'].split('.')[-1]
  end = allocation_pool['end'].split('.')[-1]
  return (int(start), int(end) + 1)

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument("--domain", help="name of domain for network")
    parser.add_argument("--project", help="name of project for network")
    parser.add_argument("--name", help="name of network")
    parser.add_argument("--external", help="adding this flag allows the current network to provide a connection to other networks")
    args = parser.parse_args()

    if args.domain and args.project and args.name:
      external = True if args.external else False
      print(create_network(args.domain, args.project, args.name, external))
    else:
        raise Exception('create_network.py usage: --domain <name> --project <name> --name <name> [--external]')


from requests import get
from pprint import PrettyPrinter

BASE_URL ='https://status.aws.amazon.com'
ALL_JSON = '/data.json'

printer = PrettyPrinter()

# accesses the json file and stores its data
data = get(BASE_URL + ALL_JSON).json()
archive = data["archive"]

# creates a list containing issues found in said area
west_USA = []
east_USA = []
europe = []
asia = []
s_america = []
oceania = []
undefined = []

# organises the data based on its geographical location
for i in range (0, len(archive)):
    item = archive[i]['service_name']

    if 'Ohio' in item or 'N. Virginia' in item or 'US-East' in item:
        east_USA.append(item)

    elif 'Oregon' in item or 'California' in item or 'US-West' in item:
        west_USA.append(item)

    elif 'Ireland' in item or 'Frankfurt' in item or 'London' in item or 'Paris' in item or 'Milan' in item or 'Stockholm' in item:
        europe.append(item)

    elif 'Seoul' in item or 'Singapore' in item or 'Hong Kong' in item or 'Tokyo' in item or 'Mumbai' in item:
        asia.append(item)

    elif 'Sao Paulo' in item:
        s_america.append(item)

    elif 'Sydney' in item:
        oceania.append(item)

    else:
        undefined.append(item)


from tkinter import *

root = Tk()
root.title('калькулятор')  # делаем заголовок

buttons = (('7', '8', '9', '/',),
           ('4', '5', '6', '*',),
           ('1', '2', '3', '-',),
           ('0', '.', '=', '+',))

start = True  # для понимания, вводится ли первое число
lastcommand = '='
result = 0

def click(text):  # принимает текст с кнопки на которую нажали
    global start
    global lastcommand
    global result
    if text.isdigit() or text == '.':
        if start:
            display.configure(text='')  # убираем 0 если пользователь нажал на цифру
            start = False
        # если текст не точка, либо точка, но ее еще не было
        if text != '.' or display.cget('text').find('.') == -1:
            # дальше добавляем цифры в конец display, если пользователь ввел не точку
            display.configure(text=(display.cget('text') + text))
    else:
        if start:
            lastcommand = text
        else:
            calculate(float(display.cget('text')))
            lastcommand = text
            start = True

def calculate(x):
    global lastcommand
    global result
    global display
    if lastcommand == '+':
        result += x
    elif lastcommand == '-':
        result -= x
    elif lastcommand == '*':
        result *= x
    elif lastcommand == '/':
        try:
            result /= x
        except ZeroDivisionError:
            pass
    elif lastcommand == '=':
        result = x

    display.configure(text=result)

# делаем табло
display = Label(root, text='0', font='Tahoma 20', bd=10)
# размещаем в самом верху,
display.grid(row=0, column=0, columnspan=4)

# выводим кнопки, перебираем в цикле
for row in range(4):
    for column in range(4):
        # создаем кнопку
        button = Button(root, text=buttons[row][column], font='Tahoma 20',
                        command=lambda text=buttons[row][column]: click(text))
        button.grid(row=row + 1, column=column, padx=5, pady=5, ipadx=20, ipady=20, sticky='nsew')
        # ^^^ размещаем кнопки(ряд+1, так как первый ряд занят таблом, padx/pady = растягивает кнопку
        # ipadx/ipady = отступы от других кнопок)

# чтобы расположить название по центру, нужнро узнать размер окна
w = root.winfo_reqwidth()  # ширина
h = root.winfo_reqheight()  # высота
# далее узнаем разрешение экрану у пользователя
ws = root.winfo_screenwidth()
hs = root.winfo_screenheight()
x = int(ws / 2 - w / 2)
y = int(hs / 2 - h / 2)
root.geometry("+{0}+{1}".format(x, y))
root.mainloop()


import random
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt

c = 13/24
a = []
b = []
q = 0

for i in range(1000):
    a_temp = np.array([random.uniform(0, 2/3) for i in range(100)]).mean()
    b_temp = np.array([random.uniform(1/2, 1) for i in range(100)]).mean()

    a.append(a_temp)
    b.append(b_temp)

    if a[i] < c < b[i]:
        q += 1

fig, ax = plt.subplots(figsize=(15, 8))

ax = sns.distplot([a, b], bins=100, kde=True)
plt.xticks(np.linspace(0, 1, 25))
plt.show()
print(q)


from analyzer import Analyzer, AnalyzerManager, plotable, CorruptAnalyzer
from dataio import File, Globbler
import os
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
from scipy.signal import argrelmax
import pandas as pd
import subprocess
import sys

try:
    import seaborn as sns
    sns.set()
except ImportError:
    pass

class Lattice:
    """ Wrapper around useful parameters """
    def __init__(self, parameters, xyzPath):
        self.nx = parameters['nx']
        self.ny = parameters['ny']
        self.numBottom = parameters['numBottomNodes']
        self.numTop = parameters['numTopNodes']
        self.numNodes = parameters['numNodes']
        self.height = parameters['grooveHeight']
        self.size = parameters['grooveSize']
        self.speed = parameters['vD']
        self.angle = parameters['beamAngle']
        self.beamMass = parameters['beamMass']
        self.xyzPath = xyzPath
        self.structurePath = os.path.join(os.path.split(xyzPath)[0],
                                          'lattice.png')

    def render(self):
        if not os.path.exists(self.structurePath):
            sys.stdout.write("Rendering " + self.xyzPath + "...")
            subprocess.call(['ovitos', 'renderxyz.py', self.xyzPath])
            sys.stdout.write('done\n')

class FrictionAnalyzer(Analyzer):
    def readNumberOfNodes(self):
        filename = 'lattice.xyz'
        blockSize = 10000  # Each xyz-block is atleast larger than 0
        numberOfBottomNodes = 0
        numberofTopNodes = 0
        with open(os.path.join(self.input, filename)) as xyz:
            for i, line in enumerate(xyz):
                if i == 0:
                    blockSize = int(line)
                elif 1 < i < blockSize+2:  # Second line is the comment
                    if 'B' in line:
                        numberOfBottomNodes += 1
                    if 'T' in line:
                        numberofTopNodes += 1
                elif i > blockSize+2:
                    break
        # Since dicts are muteable, self.datareader.params is
        # simulanously updated.
        self.parameters['numBottomNodes'] = numberOfBottomNodes
        self.parameters['numTopNodes'] = numberofTopNodes
        self.parameters['numNodes'] = blockSize

    def setUpFiles(self):
        nFile = lambda s: File(s, self.parameters, self.output)
        self.interfacePosition = nFile('interfacePosition.bin')
        self.interfaceVelocity = nFile('interfaceVelocity.bin')
        self.interfaceAttachedSprings = nFile('interfaceAttachedSprings.bin')
        self.allPosition = nFile('allPosition.bin')
        self.allVelocity = nFile('allVelocity.bin')
        self.allEnergy = nFile('allEnergy.bin')
        self.allForce = nFile('allForce.bin')
        self.pusherForce = nFile('pusherForce.bin')
        self.xyz = nFile('model.xyz')
        self.beamTorque = nFile('beamTorque.bin')
        self.beamShearForce = nFile('beamShearForce.bin')
        self.interfaceNormalForce = nFile("interfaceNormalForce.bin")
        self.snapshotInterfaceNormalForce = File("interfaceNormalForce.bin",\
                                    self.parameters,self.output + "/snapshot/")
        self.interfaceShearForce = nFile("interfaceShearForce.bin")
        self.snapshotInterfaceShearForce = File("interfaceShearForce.bin",\
                                    self.parameters,self.output + "/snapshot/")
        self.activeFiles = []
        self.files = [self.interfacePosition, self.interfaceVelocity,
                      self.interfaceAttachedSprings, self.allPosition,
                      self.allVelocity, self.allEnergy, self.allForce,
                      self.pusherForce, self.xyz, self.beamTorque,
                      self.beamShearForce,self.interfaceNormalForce,
                      self.snapshotInterfaceNormalForce,
                      self.interfaceShearForce,
                      self.snapshotInterfaceShearForce]

    def getInterfaceStructure(self):
        print(self.input)
        filename = 'interfaceStructure.txt'
        return
        try:
            with open(os.path.join(self.input, filename)) as xyz:
                self.parameters['interfaceStructure'] = eval(xyz.readline())
        except Exception as exc:
            raise CorruptAnalyzer("Could not read interface structure")

    def readAll(self):
        """ Reads all binary files available as described in parameters.

        Reads the file writing flags in the parameter file to
        determine which binary files to load.
        """
        writtenOutput = {param for param, flag in
                         self.parameters.items()
                         if param.startswith('write')
                         and flag == 1}

    @plotable
    def plotPositionInterface(self):
        pos = self.interfacePosition.get() - self.interfacePosition.get()[0, :]
        plt.pcolormesh(pos[:, :, 1], cmap=plt.get_cmap('jet'))
        plt.colorbar()
        plt.title("Relative position of the interface")

    @Analyzer.restrict
    @plotable
    def plotAttachedSprings(self, axis, figure):
        savepath = 'attachedSprings.png'
        data = self.interfaceAttachedSprings.get()
        time = self.interfaceAttachedSprings.time()
        points = axis.pcolormesh(data, cmap=plt.get_cmap('jet'))
        ticks = [int(t) for t in np.linspace(0, data.shape[0]-1, 10)]
        axis.yaxis.set(ticks=ticks, ticklabels=time[ticks])
        cb = figure.colorbar(points, ax=axis)
        cb.set_label("Fraction of pinned springs")
        cb.ax.get_yaxis().set_ticks([])
        for i, lab in enumerate(['0%', '100%']):
            cb.ax.text(1, 0.95*i, lab)
        axis.set_title(("Percent of pinned springs for "
                   "size {} and height {}".format(self.lattice.size,
                                                  self.lattice.height)))
        axis.set_xlabel("Block index")
        axis.set_ylabel("Time [s]")

    def plotNormalForce(self,axis,figure):
        savepath = 'normalForce.png'
        data = self.interfaceNormalForce.get()
        time = self.interfaceNormalForce.time()
        points = axis.pcolormesh(data, cmap=plt.get_cmap('jet'))
        ticks = [int(t) for t in np.linspace(0, data.shape[0]-1, 10)]
        axis.yaxis.set(ticks=ticks, ticklabels=time[ticks])
        cb = figure.colorbar(points, ax=axis)
        cb.set_label("Force [N]")
        cb.ax.get_yaxis().set_ticks([])
        axis.set_title(("Normal Force for "
                   "size {} and height {}".format(self.lattice.size,
                                                  self.lattice.height)))

    def getFrontVelocities(self, cutoffPoint=0.1):
        """Finds the front velocity of the rapture of the springs.

        Symmetric differentiation is used to find the velocity.
        """
        data = self.getReducedSpringAttachments(cutoffPoint)
        v = np.zeros(len(data) - 2)
        h = self.interfaceAttachedSprings.time()
        for i in range(1, len(data)-1):
            v[i-1] = (data[i+1] - data[i-1])/(2.0*h)

        return np.abs(v)*self.parameters['d']

    @plotable
    def plotFrontVelocities(self, cutoffPoint=0.1):
        """ Plots the Front Velocities.
        """
        data = self.getFrontVelocities(cutoffPoint)
        time = np.arange(len(data))*self.parameters['step']*self.parameters['freqNodeSpringsAttachedInterface']
        plt.plot(time, data)
        plt.title("Front Velocity for size %g and height %g" % (self.getGrooveDim()[1],
                                                                self.getGrooveDim()[0]))
        plt.xlabel("Time [s]")
        plt.ylabel("Velocity [m/s]")

    def getStaticFrictionCoefficient(self, mean=False):
        """Reads the shear forces on the top rod.

        The array is then resized to a matrix with the shape [time,block].
        If the parameter mean == True, mean forces is calculated,
        otherwise the total force is calculated.
        The static friction coefficent is returned as the
        maximum value of the mean or total shear force  on the rod
        divided by the normal force.

        TODO: Get the friction force from the flat case,
        and use that to normalize the static friction for grooves.
        """
        shearForce_on_rod = self.beamShearForce.get()

        if mean:
            shearForce_on_rod = np.mean(shearForce_on_rod, axis=1)
        else:
            shearForce_on_rod = np.sum(shearForce_on_rod, axis=1)

        staticCoefficient = np.max(-shearForce_on_rod)/np.float(self.parameters['fn']) *\
                             float(self.parameters["nx"])/self.parameters["grooveSize"]
        return staticCoefficient

    def getLocalMax(self):
        shearForce = -1*self.beamShearForce.get()
        force = np.sum(shearForce, axis=1)/self.parameters['fn']* float(self.parameters["nx"])/self.parameters["grooveSize"]
        locMaxIndex = argrelmax(force)[0]
        # timeArray = self.getTimeArray(force, 'freqBeamShearForce')[argrelmax(force)[0]]
        time = self.beamShearForce.time()[locMaxIndex]
        locMax = force[locMaxIndex]
        # return time, locMax
        return self.beamShearForce.time(), force

    @plotable
    @Analyzer.restrict
    def plotLocalMax(self, axis, i=[0]):
        lines = ['-', '--', '-.']
        time, data = self.getLocalMax()
        axis.plot(time, data,
                  label='Size {}, height {}'.format(self.lattice.size,
                                                    self.lattice.height))
                 # linestyle=lines[i[0]])
        self.plotReleaseTime(axis)
        self.plotDrivingTime(axis)
        axis.set_xlabel("Time [s]")
        axis.set_ylabel(r"$F_S/F_N$")
        axis.set_title(("Local Max for Beam Shear Force for "
                        "size {} height {}".format(self.lattice.size,
                                                   self.lattice.height)))
        i[0] += 1
        i[0] %= len(lines)

    def plotReleaseTime(self, axis):
        axis.axvline(x=self.parameters['releaseTime']*self.parameters['step'],
                     linestyle='--', color='k')

    def plotDrivingTime(self, axis):
        axis.axvline(x=self.parameters['drivingTime']*self.parameters['step'],
                     linestyle='-.', color='k')

    @plotable
    @Analyzer.restrict
    def plotNormalForceAtMax(self,axis):
        data = self.snapshotInterfaceNormalForce.get()[0]
        time = self.getTimeOfMax()
        axis.plot(data)
        axis.set_xlabel("Block")
        axis.set_ylabel("Normal Force [N]")
        axis.set_title("Normal Force over the Tooth as {} s".format(time))

    def getNormalForceAtMax(self):
        data = self.snapshotInterfaceNormalForce.get()
        try:
            return data[0]
        except:
            return data

    def getShearForceAtMax(self):
        data = self.snapshotInterfaceShearForce.get()
        try:
            return data[0]
        except:
            return data

    def getTimeOfMax(self):
        """
        This is a easy, temp solution since Erlends file class didn't give the
        the time, only the data. So this was easier than finding the
        place where that happens in the file class...
        """
        with open(self.output + "/snapshot/model.xyz") as f:
            f.readline()
            time = f.readline().split()[1]

        return time

    def plotLattice(self, axis, figure=None):
        if not os.path.exists(self.lattice.xyzPath):
            print(self.lattice.xyzPath, " does not exist")
            return
        self.lattice.render()
        img = mpimg.imread(self.lattice.structurePath)
        axis.imshow(img)

class FrictionManager(AnalyzerManager):
    def setUp(self, args):
        globbler = Globbler(args.search_path)
        self.analyzers = globbler.glob(className=FrictionAnalyzer,
                                       pattern=args.pattern,
                                       outputPattern=args.output,
                                       inputPattern=args.input)
        for analyzer in self.analyzers:
            try:
                analyzer.setPlotPath(args.plotpath)
                analyzer.readParameters(args.paramname)
                analyzer.readNumberOfNodes()
                analyzer.getInterfaceStructure()
                analyzer.lattice = Lattice(analyzer.parameters,
                                           os.path.join(analyzer.info,
                                                        'lattice.xyz'))
                analyzer.setUpFiles()
            except CorruptAnalyzer as exc:
                self.handleException(analyzer, exc)

        self.inspect()

class GroupAnalyzer():
    """

    Since numpy arrays are several order of magnitudes faster than
    panda dataframes, numpy arrays are used for large datasets while
    dataframes are used for small datasets.
    """
    def __init__(self, manager):
        self.manager = manager
        self.df = self.createDataFrame()
        self.cids = []
        self.scatterData = {}

    def createDataFrame(self):
        manager = self.manager
        lattices = manager.lattice
        return pd.DataFrame({'nx': [l.nx for l in lattices],
                             'ny': [l.ny for l in lattices],
                             'nodes': [l.numNodes for l in lattices],
                             'top nodes': [l.numTop for l in lattices],
                             'bottom nodes': [l.numBottom for l in lattices],
                             'height': [l.height for l in lattices],
                             'size': [l.size for l in lattices],
                             'angle': [l.angle for l in lattices],
                             'driver speed': [l.speed for l in lattices],
                             'static coefficient': manager.getStaticFrictionCoefficient()},
                            index=manager.id)

    def entryFromEvent(self, event):
        x, y = self.scatterData.columns
        dist = ((self.scatterData[x]-event.mouseevent.xdata)**2 +
                (self.scatterData[y]-event.mouseevent.ydata)**2)
        nearest = np.argmin(dist)
        return nearest, self.df.iloc[nearest]

    def plot(self):
        self.fig = plt.figure(1)
        self.scatterData = self.df[['size', 'static coefficient']]
        plt.scatter(self.scatterData['size'],
                    self.scatterData['static coefficient'], picker=5)
        self.cids.append(self.fig.canvas.mpl_connect('pick_event',
                                                     self.plotAnalyzer))
        plt.show()
        # self.fig.canvas.mpl_disconnect(self.cids.pop())

    def plotAnalyzer(self, event):
        id, entry = self.entryFromEvent(event)
        analyzer = self.manager.get(id)
        plt.subplots()
        plt.subplot(2, 1, 1)
        analyzer.plotLocalMax()
        plt.subplot(2, 1, 2)
        analyzer.plotAttachedSprings()
        plt.show()


from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, redirect, get_object_or_404
from django.urls import reverse, reverse_lazy
import datetime
from .models import Project, Assignment, Reminder
from .forms import CreateProjectForm, CreateAssignmentForm, CreateReminderForm

def dashboard_view(request, *args, **kwargs):
    if request.user.is_authenticated:
        g1 = len(Project.objects.filter(user=request.user)) > 0
        context = {
            'projects' : Project.objects.filter(user=request.user),
            'countG1'  : g1
    }
        return render(request, 'dash.html', context)
    else:
        return redirect('login')

def class_view(request, id, *args, **kwargs):
    if request.user.is_authenticated:
        project = Project.objects.get(id=id)
        classPresent = len(Assignment.objects.filter(project=project)) > 0
        reminderPresent = len(Reminder.objects.filter(project=project)) > 0
        context = {
            'classPresent'     : classPresent,
            'reminderPresent'    : reminderPresent,
            'project'     : project,
            'assignments' : Assignment.objects.filter(project=project),
            'reminders'  : Reminder.objects.filter(project=project),
        }
        print(Reminder.objects.filter(project=project))
        return render(request, 'class.html', context)
    else:
        return redirect('login')

def create_class_view(request, *args, **kwargs):
    form = CreateProjectForm(request.POST or None)
    context = { 'form' : form }
    if request.user.is_authenticated:
        if request.method == 'POST':
            if request.POST.get('create') == 'Create Class':
                instance = form.save(commit=False)
                instance.user = request.user
                instance.save()
            return redirect('dashboard')
        return render(request, 'create_class.html', context)
    else:
        return redirect('login')

def delete_class_view(request, id, *args, **kwargs):
    if request.user.is_authenticated:
        project = Project.objects.get(id=id)
        context = {
            'item' : project
        }
        if request.method == 'POST':
            if request.POST.get('delete') == 'Delete Class':
                project.delete()
            return redirect('dashboard')
        return render(request, 'delete_class.html', context)
    else:
        return redirect('login')

def create_assignment(request, id, *args, **kwargs):
    if request.user.is_authenticated:
        form = CreateAssignmentForm()
        context = { 'form' : form }
        if request.method == 'POST':
            if request.POST.get('create') == 'Create Assignment':
                instance = Assignment()
                instance.title = request.POST.get('title')
                instance.description = request.POST.get('description')
                instance.due = request.POST.get('due')
                instance.project = Project.objects.get(id=id)
                instance.save()
            return redirect('class', id=id)
        return render(request, 'create_assignment.html', context)
    else:
        return redirect('login')

def delete_assignment(request, id, *args, **kwargs):
    assignment = Assignment.objects.get(id=id)
    class_id = assignment.project.id
    assignment.delete()
    return HttpResponseRedirect(reverse_lazy('class', kwargs={'id':class_id}))

def create_reminder(request, id, *args, **kwargs):
    if request.user.is_authenticated:
        form = CreateReminderForm()
        context = { 'form' : form }
        if request.method == 'POST':
            if request.POST.get('back') == 'Go Back':
                return redirect('class', id=id)
            instance = Reminder()
            instance.description = request.POST.get('description')
            instance.project = Project.objects.get(id=id)
            instance.save()
            return redirect('class', id=id)
        return render(request, 'create_todo.html', context)
    else:
        return redirect('login')

def delete_reminder(request, id, *args, **kwargs):
    assignment = Reminder.objects.get(id=id)
    class_id = assignment.project.id
    assignment.delete()
    return HttpResponseRedirect(reverse_lazy('class', kwargs={'id':class_id}))