# Copyright (C) 2020 Cancer Care Associates

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

#     http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import base64
import functools
import os
import pathlib
import subprocess
import sys
from datetime import datetime

from pymedphys._imports import imageio
from pymedphys._imports import numpy as np
from pymedphys._imports import pandas as pd
from pymedphys._imports import plt
from pymedphys._imports import streamlit as st
from pymedphys._imports import streamlit_ace, timeago, tomlkit

import pymedphys
from pymedphys._streamlit.utilities import exceptions as _exceptions
from pymedphys._streamlit.utilities import misc as st_misc

from . import _config, _dicom, _icom, _monaco, _mosaiq, _trf

DATA_OPTION_LABELS = {
    "monaco": "Monaco tel.1 filepath",
    "dicom": "DICOM RTPlan file upload",
    "icom": "iCOM record timestamp",
    "trf": "Linac Backup `.trf` filepath",
    "mosaiq": "Mosaiq SQL query",
}

LEAF_PAIR_WIDTHS = (10,) + (5,) * 78 + (10,)
MAX_LEAF_GAP = 410
GRID_RESOLUTION = 1
GRID = pymedphys.metersetmap.grid(
    max_leaf_gap=MAX_LEAF_GAP,
    grid_resolution=GRID_RESOLUTION,
    leaf_pair_widths=LEAF_PAIR_WIDTHS,
)
COORDS = (GRID["jaw"], GRID["mlc"])

def sidebar_overview():
    overview_placeholder = st.sidebar.empty()

    def set_overview_data(patient_id, patient_name, total_mu):
        overview_placeholder.markdown(
            f"Patient ID: `{patient_id}`\n\n"
            f"Patient Name: `{patient_name}`\n\n"
            f"Total MU: `{total_mu}`"
        )

    return set_overview_data

def get_most_recent_file_and_print(linac_id, filepaths):
    if not isinstance(filepaths, list):
        raise ValueError("Filepaths needs to be a list")

    try:
        latest_filepath = max(filepaths, key=os.path.getmtime)
    except ValueError:
        st.sidebar.markdown(f"{linac_id}: `Never`")
        return

    most_recent = datetime.fromtimestamp(os.path.getmtime(latest_filepath))
    now = datetime.now()

    if most_recent > now:
        most_recent = now

    human_readable = timeago.format(most_recent, now)

    st.sidebar.markdown(f"{linac_id}: `{human_readable}`")

def icom_status(linac_id, icom_directory):
    filepaths = list(pathlib.Path(icom_directory).glob("*.txt"))
    get_most_recent_file_and_print(linac_id, filepaths)

def trf_status(linac_id, backup_directory):
    directory = pathlib.Path(backup_directory).joinpath(linac_id)
    filepaths = list(directory.glob("*.zip"))
    get_most_recent_file_and_print(linac_id, filepaths)

def show_status_indicators(config):
    if st.sidebar.button("Check status of iCOM and backups"):
        try:
            linac_icom_live_stream_directories = (
                _config.get_icom_live_stream_directories(config)
            )
            linac_indexed_backups_directory = _config.get_indexed_backups_directory(
                config
            )
        except KeyError:
            st.sidebar.write(
                _exceptions.ConfigMissing(
                    "iCOM and/or TRF backup configuration is missing. "
                    "Unable to show status."
                )
            )

            return

        linac_ids = list(linac_icom_live_stream_directories.keys())

        st.sidebar.markdown(
            """
            ## Last recorded iCOM stream
            """
        )

        for linac_id, icom_directory in linac_icom_live_stream_directories.items():
            icom_status(linac_id, icom_directory)

        st.sidebar.markdown(
            """
            ## Last indexed backup
            """
        )

        for linac_id in linac_ids:
            trf_status(linac_id, linac_indexed_backups_directory)

def display_deliveries(deliveries):
    if not deliveries:
        return 0

    st.write(
        """
        #### Overview of selected deliveries
        """
    )

    data = []
    for delivery in deliveries:
        num_control_points = len(delivery.mu)

        if num_control_points != 0:
            total_mu = delivery.mu[-1]
        else:
            total_mu = 0

        data.append([total_mu, num_control_points])

    columns = ["MU", "Number of Data Points"]
    df = pd.DataFrame(data=data, columns=columns)
    st.write(df)

    total_mu = round(df["MU"].sum(), 1)

    st.write(f"Total MU: `{total_mu}`")

    return total_mu

def get_input_data_ui(
    overview_updater_map,
    data_method_map,
    default_method,
    key_namespace,
    advanced_mode,
    **previous_results,
):
    if advanced_mode:
        data_method_options = list(data_method_map.keys())
        data_method = st.selectbox(
            "Data Input Method",
            data_method_options,
            index=data_method_options.index(default_method),
        )

    else:
        data_method = default_method

    results = data_method_map[data_method](  # type: ignore
        key_namespace=key_namespace,
        advanced_mode=advanced_mode,
        **previous_results,
    )

    try:
        total_mu = round(display_deliveries(results["deliveries"]), 1)
    except KeyError:
        total_mu = 0

    try:
        patient_id = results["patient_id"]
    except KeyError:
        patient_id = ""

    try:
        patient_name = results["patient_name"]
    except KeyError:
        patient_name = ""

    overview_updater_map[key_namespace](patient_id, patient_name, total_mu)

    return results

@st.cache
def to_tuple(array):
    return tuple(map(tuple, array))

def plot_gamma_hist(gamma, percent, dist):
    valid_gamma = gamma[~np.isnan(gamma)]

    plt.hist(valid_gamma, 50, density=True)
    pass_ratio = np.sum(valid_gamma <= 1) / len(valid_gamma)

    plt.title(
        "Local Gamma ({0}%/{1}mm) | Percent Pass: {2:.2f} % | Mean Gamma: {3:.2f} | Max Gamma: {4:.2f}".format(
            percent, dist, pass_ratio * 100, np.mean(valid_gamma), np.max(valid_gamma)
        )
    )

def plot_and_save_results(
    reference_metersetmap,
    evaluation_metersetmap,
    gamma,
    gamma_options,
    png_record_directory,
    header_text="",
    footer_text="",
):
    reference_filepath = png_record_directory.joinpath("reference.png")
    evaluation_filepath = png_record_directory.joinpath("evaluation.png")
    diff_filepath = png_record_directory.joinpath("diff.png")
    gamma_filepath = png_record_directory.joinpath("gamma.png")

    diff = evaluation_metersetmap - reference_metersetmap

    imageio.imwrite(reference_filepath, reference_metersetmap)
    imageio.imwrite(evaluation_filepath, evaluation_metersetmap)
    imageio.imwrite(diff_filepath, diff)
    imageio.imwrite(gamma_filepath, gamma)

    largest_metersetmap = np.max(
        [np.max(evaluation_metersetmap), np.max(reference_metersetmap)]
    )
    largest_diff = np.max(np.abs(diff))

    widths = [1, 1]
    heights = [0.5, 1, 1, 1, 0.4]
    gs_kw = dict(width_ratios=widths, height_ratios=heights)

    fig, axs = plt.subplots(5, 2, figsize=(10, 16), gridspec_kw=gs_kw)
    gs = axs[0, 0].get_gridspec()

    for ax in axs[0, 0:]:
        ax.remove()

    for ax in axs[1, 0:]:
        ax.remove()

    for ax in axs[4, 0:]:
        ax.remove()

    ax_header = fig.add_subplot(gs[0, :])
    ax_hist = fig.add_subplot(gs[1, :])
    ax_footer = fig.add_subplot(gs[4, :])

    ax_header.axis("off")
    ax_footer.axis("off")

    ax_header.text(0, 0, header_text, ha="left", wrap=True, fontsize=21)
    ax_footer.text(0, 1, footer_text, ha="left", va="top", wrap=True, fontsize=6)

    plt.sca(axs[2, 0])
    pymedphys.metersetmap.display(
        GRID, reference_metersetmap, vmin=0, vmax=largest_metersetmap
    )
    axs[2, 0].set_title("Reference MetersetMap")

    plt.sca(axs[2, 1])
    pymedphys.metersetmap.display(
        GRID, evaluation_metersetmap, vmin=0, vmax=largest_metersetmap
    )
    axs[2, 1].set_title("Evaluation MetersetMap")

    plt.sca(axs[3, 0])
    pymedphys.metersetmap.display(
        GRID, diff, cmap="seismic", vmin=-largest_diff, vmax=largest_diff
    )
    plt.title("Evaluation - Reference")

    plt.sca(axs[3, 1])
    pymedphys.metersetmap.display(GRID, gamma, cmap="coolwarm", vmin=0, vmax=2)
    plt.title(
        "Local Gamma | "
        f"{gamma_options['dose_percent_threshold']}%/"
        f"{gamma_options['distance_mm_threshold']}mm"
    )

    plt.sca(ax_hist)
    plot_gamma_hist(
        gamma,
        gamma_options["dose_percent_threshold"],
        gamma_options["distance_mm_threshold"],
    )

    return fig

@st.cache(hash_funcs={pymedphys.Delivery: hash})
def calculate_metersetmap(delivery):
    return delivery.metersetmap(
        max_leaf_gap=MAX_LEAF_GAP,
        grid_resolution=GRID_RESOLUTION,
        leaf_pair_widths=LEAF_PAIR_WIDTHS,
    )

def calculate_batch_metersetmap(deliveries):
    metersetmap = calculate_metersetmap(deliveries[0])

    for delivery in deliveries[1::]:
        metersetmap = metersetmap + calculate_metersetmap(delivery)

    return metersetmap

@st.cache
def calculate_gamma(reference_metersetmap, evaluation_metersetmap, gamma_options):
    gamma = pymedphys.gamma(
        COORDS,
        to_tuple(reference_metersetmap),
        COORDS,
        to_tuple(evaluation_metersetmap),
        **gamma_options,
    )

    return gamma

def advanced_debugging(config):
    st.sidebar.markdown("# Advanced Debugging")
    if st.sidebar.button("Compare Baseline to Output Directory"):
        st.write(
            """
            ## Comparing Results to Baseline
            """
        )

        baseline_directory = pathlib.Path(
            config["debug"]["baseline_directory"]
        ).resolve()

        png_baseline_directory = baseline_directory.joinpath("png")

        baseline_png_paths = [
            path for path in (png_baseline_directory.rglob("*")) if path.is_file()
        ]

        relative_png_paths = [
            path.relative_to(png_baseline_directory) for path in baseline_png_paths
        ]

        output_dir = pathlib.Path(config["output"]["png_directory"]).resolve()

        evaluation_png_paths = [
            output_dir.joinpath(path) for path in relative_png_paths
        ]

        for baseline, evaluation in zip(baseline_png_paths, evaluation_png_paths):

            st.write(f"### {baseline.parent.name}/{baseline.name}")

            st.write(f"`{baseline}`\n\n**vs**\n\n`{evaluation}`")

            baseline_image = imageio.imread(baseline)

            try:
                evaluation_image = imageio.imread(evaluation)
            except FileNotFoundError as e:
                st.write(
                    """
                    #### File was not found
                    """
                )

                st.write(e)

                st.write(
                    f"""
                    For debugging purposes, here are all the files that
                    were found within {str(output_dir)}
                    """
                )

                st.write(
                    [str(path) for path in output_dir.rglob("*") if path.is_file()]
                )

                return

            agree = np.allclose(baseline_image, evaluation_image)
            st.write(f"Images Agree: `{agree}`")

def run_calculation(
    reference_results,
    evaluation_results,
    gamma_options,
    escan_directory: pathlib.Path,
    png_output_directory: pathlib.Path,
):
    st.write("Calculating Reference MetersetMap...")
    reference_metersetmap = calculate_batch_metersetmap(reference_results["deliveries"])

    st.write("Calculating Evaluation MetersetMap...")
    evaluation_metersetmap = calculate_batch_metersetmap(
        evaluation_results["deliveries"]
    )

    st.write("Calculating Gamma...")
    gamma = calculate_gamma(
        reference_metersetmap, evaluation_metersetmap, gamma_options
    )

    patient_id = reference_results["patient_id"]

    st.write("Creating figure...")
    output_base_filename = (
        f"{patient_id} {reference_results['identifier']} vs "
        f"{evaluation_results['identifier']}"
    )
    pdf_filepath = str(
        escan_directory.joinpath(f"{output_base_filename}.pdf").resolve()
    )
    png_record_directory = png_output_directory.joinpath(output_base_filename)
    png_record_directory.mkdir(exist_ok=True, parents=True)
    png_filepath = str(png_record_directory.joinpath("report.png").resolve())

    try:
        patient_name_text = f"Patient Name: {reference_results['patient_name']}\n"
    except KeyError:
        patient_name_text = ""

    header_text = (
        f"Patient ID: {patient_id}\n"
        f"{patient_name_text}"
        f"Reference: {reference_results['identifier']}\n"
        f"Evaluation: {evaluation_results['identifier']}\n"
    )

    reference_path_strings = "\n    ".join(
        [str(path.resolve()) for path in reference_results["data_paths"]]
    )
    evaluation_path_strings = "\n    ".join(
        [str(path.resolve()) for path in evaluation_results["data_paths"]]
    )

    footer_text = (
        f"reference path(s): {reference_path_strings}\n"
        f"evaluation path(s): {evaluation_path_strings}\n"
        f"png record: {png_filepath}"
    )

    fig = plot_and_save_results(
        reference_metersetmap,
        evaluation_metersetmap,
        gamma,
        gamma_options,
        png_record_directory,
        header_text=header_text,
        footer_text=footer_text,
    )

    fig.tight_layout()

    st.write("## Results")
    st.pyplot(fig)

    st.write("## Saving reports")
    st.write("### PNG")
    st.write("Saving figure as PNG...")
    plt.savefig(png_filepath, dpi=100)
    st.write(f"Saved:\n\n`{png_filepath}`")
    convert_png_to_pdf(png_filepath, pdf_filepath)

def convert_png_to_pdf(png_filepath, pdf_filepath):
    st.write("### PDF")
    st.write("Converting PNG to PDF...")

    try:
        subprocess.check_call(
            f'magick convert "{png_filepath}" "{pdf_filepath}"', shell=True
        )
        success = True
    except subprocess.CalledProcessError:
        try:
            subprocess.check_call(
                f'convert "{png_filepath}" "{pdf_filepath}"', shell=True
            )
            success = True
        except subprocess.CalledProcessError:
            success = False

    if success:
        st.write(f"Created:\n\n`{pdf_filepath}`")

        with open(pdf_filepath, "rb") as f:
            pdf_contents = f.read()

        pdf_filename = pathlib.Path(pdf_filepath).name

        pdf_b64 = base64.b64encode(pdf_contents).decode()
        href = f"""
            <a href="data:file/zip;base64,{pdf_b64}" download='{pdf_filename}'>
                Click to download {pdf_filename}
            </a>
        """
        st.markdown(href, unsafe_allow_html=True)

    else:
        if sys.platform == "win32":
            url_hash_parameter = "#windows"
        else:
            url_hash_parameter = ""

        download_url = (
            f"https://imagemagick.org/script/download.php{url_hash_parameter}"
        )

        st.write(
            _exceptions.UnableToCreatePDF(
                "Please install Image Magick to create PDF reports "
                f"<{download_url}>."
            )
        )

def main():
    st.write(
        """
        Tool to compare the MetersetMap between planned and delivery.
        """
    )

    st.sidebar.markdown(
        """
        # Configuration Choice
        """
    )

    config_options = list(_config.CONFIG_OPTIONS.keys())

    try:
        _config.get_config(config_options[0])
    except FileNotFoundError:
        config_options.pop(0)

    config_mode = st.sidebar.radio("Config Mode", options=config_options)
    config = _config.get_config(config_mode)

    show_config = st.sidebar.checkbox("Show/edit config", False)
    if show_config:
        st.write("## Configuration")
        config = tomlkit.loads(
            streamlit_ace.st_ace(value=tomlkit.dumps(config), language="toml")
        )

    st.sidebar.markdown(
        """
        # MetersetMap Overview
        """
    )

    st.sidebar.markdown(
        """
        ## Reference
        """
    )

    set_reference_overview = sidebar_overview()

    st.sidebar.markdown(
        """
        ## Evaluation
        """
    )

    set_evaluation_overview = sidebar_overview()

    overview_updater_map = {
        "reference": set_reference_overview,
        "evaluation": set_evaluation_overview,
    }

    st.sidebar.markdown(
        """
        # Status indicators
        """
    )

    show_status_indicators(config)

    st.sidebar.markdown(
        """
        # Advanced options

        Enable advanced functionality by ticking the below.
        """
    )
    advanced_mode = st.sidebar.checkbox("Run in Advanced Mode")

    gamma_options = _config.get_gamma_options(config, advanced_mode)

    data_option_functions = {
        "monaco": _monaco.monaco_input_method,
        "dicom": _dicom.dicom_input_method,
        "icom": _icom.icom_input_method,
        "trf": _trf.trf_input_method,
        "mosaiq": _mosaiq.mosaiq_input_method,
    }

    default_reference_id = config["data_methods"]["default_reference"]
    default_evaluation_id = config["data_methods"]["default_evaluation"]
    available_data_methods = config["data_methods"]["available"]

    default_reference = DATA_OPTION_LABELS[default_reference_id]
    default_evaluation = DATA_OPTION_LABELS[default_evaluation_id]

    data_method_map = {}
    for method in available_data_methods:
        data_method_map[DATA_OPTION_LABELS[method]] = functools.partial(
            data_option_functions[method], config=config
        )

    st.write(
        """
        ## Selection of data to compare
        """
    )

    st.write("---")

    ref_col, eval_col = st.beta_columns(2)

    with ref_col:
        st.write(
            """
            ### Reference
            """
        )

        reference_results = get_input_data_ui(
            overview_updater_map,
            data_method_map,
            default_reference,
            "reference",
            advanced_mode,
        )

    with eval_col:
        st.write(
            """
            ### Evaluation
            """
        )

        evaluation_results = get_input_data_ui(
            overview_updater_map,
            data_method_map,
            default_evaluation,
            "evaluation",
            advanced_mode,
            **reference_results,
        )

    st.write("---")

    st.write(
        """
        ## Output Locations
        """
    )

    st.write(
        """
        ### eSCAN Directory

        The location to save the produced pdf report.
        """
    )

    default_site = evaluation_results.get("site", None)
    if default_site is None:
        default_site = reference_results.get("site", None)

    _, escan_directory = st_misc.get_site_and_directory(
        config,
        "eScan Site",
        "escan",
        default=default_site,
        key="escan_export_site_picker",
    )

    escan_directory = pathlib.Path(os.path.expanduser(escan_directory)).resolve()

    if advanced_mode:
        st.write(escan_directory)

    default_png_output_directory = config["output"]["png_directory"]

    if advanced_mode:
        st.write(
            """
            ### Image record

            Path to save the image of the results for posterity
            """
        )

        png_output_directory = pathlib.Path(
            st.text_input("png output directory", default_png_output_directory)
        )
        st.write(png_output_directory.resolve())

    else:
        png_output_directory = pathlib.Path(default_png_output_directory)

    png_output_directory = pathlib.Path(
        os.path.expanduser(png_output_directory)
    ).resolve()

    st.write(
        """
        ## Calculation
        """
    )

    if st.button("Run Calculation"):

        st.write(
            """
            ### MetersetMap usage warning
            """
        )

        st.warning(pymedphys.metersetmap.WARNING_MESSAGE)

        st.write(
            """
            ### Calculation status
            """
        )

        run_calculation(
            reference_results,
            evaluation_results,
            gamma_options,
            escan_directory,
            png_output_directory,
        )

    if advanced_mode:
        advanced_debugging(config)


#!/usr/bin/python3

import sys
import json
import sqlite3

import numpy as np
from randpool import RandPool

from entitygenerator import EntityGenerator, EntityElement, ArrayElement
from namegen import USCensusName
from addrgen import USAddress
from phonegen import PhoneElement
from gendergen import GenderElement
from natidgen import NationalIDElement
from dobgen import DOBElement
from tradegen import USCreditAccount

# TODO - Modifier functions, two types: pre- and post-serialization.
# pre-modifiers change the object before it is serialized.  This might
# be useful for introducing intentional errors, nicknames, modifying
# structure, etc.  The post-serialization modifiers can be used to
# create changes to the object which was just serialized so that it
# can be serialized again.  This should be an efficient way to create
# relationships (where large parts of the object might be reused), such
# as husband/wife, parent/child, roommates, or co-applicants (credit).

# TODO - include nicknames, name variations (maiden/married)

# TODO - need pre-output modifier functions.  This might randomly disrupt
# otherwise too-clean data.

# TODO - need post-output modifier functions.  These operate on the entity
# which was just output, and might be employed to reuse large portions of
# the original record.  This might be useful for generating a spouse or a
# parent/child (biological) relationship.  Once a post-output modifier has
# operated on an entity, we will have to serialize the modified object
# again in its modified state.

# TODO - generate trade (credit account) data

# TODO - email generator?

# TODO - when generating an array of addresses, it would be nice to be able
# to specify "generate local addresses".  It's unrealistic that a person would
# move large distances frequently.  Instead, pick addresses (based on a
# probability) where the leading 1, 2, or 3 digits of the zip code (for US
# addresses) are the same.

# TODO - create address history, with from/to dates.

# TODO - need better phone generator:
#        - Create a CDF for each zip code with area code frequencies
#        - do not use invalid exchanges or sequence numbers

def serialize_csv(e):
    fname = e['name'].get('first', '')
    mname = e['name'].get('middle', '')
    lname = e['name'].get('last', '')
    dob = e.get('dob', '')
    ssn = e.get('ssn', '')
    phone = e.get('phone', '')
    gender = e.get('gender', '')

    for addr in e['addrs']:
        s = '|'.join(( fname, mname, lname,
                       ssn, dob, phone, gender,
                       addr['street1'], addr['street2'], addr['street3'],
                       addr['city'], addr['state'], addr['postalcode']) )
        print(s)

    return

def serialize_json(e):
    print(json.dumps(e))
    return

def main(argv):
    if len(argv) > 2:
        raise Exception('Invalid number of arguments received')

    n = 10
    if len(argv) == 2:
        try:
            n = int(argv[1])
        except:
            raise Exception('Invalid command line argument: ' + argv[1])

    egen = EntityGenerator()

    gender = GenderElement(name = 'gender')
    egen.addElement(gender)

    dob = DOBElement(name = 'dob',
                     dt_format = '%Y%m%d')
    egen.addElement(dob)

    ssn = NationalIDElement(name = 'ssn',
                            useDashes = False)
    egen.addElement(ssn)

    name = USCensusName(name = 'name',
                        order = 'LFM',
                        pctMidName=0.7,
                        pctMidInitial=0.5,
                        pctFirstInitial=0.15,
                        params = { 'gender': '/gender' } )
    egen.addElement(name)

    # we're going to get a bit crazy here (because we can!).  Each entity is
    # going to get a block of addresses.  Each address will be generated by
    # the USAddress Entity class, and the number of addresses in each block
    # will be random (range  [1..5) ).  Each address will get a block of phone
    # numbers, and there will be exactly two phone numbers in each block.

    addr = USAddress()
    addrBlock = ArrayElement(name = 'addresses',
                             count_fn = EntityElement.count_rand_fn(max = 5,
                                                                    min = 1),
                             generator = addr)

    # Create a block of phone numbers
    phone = PhoneElement()
    phoneBlock = ArrayElement(name = 'phones',
                              count_fn = EntityElement.count_const_fn(2),
                              generator = phone)

    addr.addElement(phoneBlock)  # add the block of phones to each address...
    egen.addElement(addrBlock)   # ...and finally add the addresses to the entity.

    trade = USCreditAccount()
    tradeBlock = ArrayElement(name = 'trades',
                              count_fn = EntityElement.count_rand_fn(max=9,
                                                                     min=0),
                              generator = trade)
    egen.addElement(tradeBlock)

    # create a few entities
    serialize = serialize_json
    #serialize = serialize_csv
    for i in range(n):
        entity = egen.create()
        serialize(entity)

    return 0

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


import random2
import sys
from PyQt5 import QtWidgets, QtTest, QtGui, QtCore as QC

fontTText = QtGui.QFont("Saira", 24)
fontLText = QtGui.QFont("Semi", 10)
fontBtn = QtGui.QFont("Kanit", 10)
fontQlabel = QtGui.QFont("Kanit", 10)
fontWait = QtGui.QFont("Kanit", 20)

class mainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Çekiliş V1.0")
        self.setWindowIcon(QtGui.QIcon("images/giveaway.png"))
        self.setGeometry(510, 100, 950, 850)

        self.setFixedSize(950, 850)

        self.titleImage = QtWidgets.QLabel()
        self.titleImage.setPixmap(QtGui.QPixmap("images/giveaway.png"))

        self.titleText = QtWidgets.QLabel("Çekiliş V1.0")
        self.titleText.setFont(fontTText)

        self.textEdit = QtWidgets.QTextEdit()
        self.textEdit.setFont(fontLText)

        self.listTitle = QtWidgets.QLabel("Kişi Listesi")
        self.listTitle.setAlignment(QC.Qt.AlignCenter)
        self.listTitle.setFont(fontTText)

        self.textList = QtWidgets.QListWidget()
        self.textList.setFont(fontLText)

        self.textEditTitle = QtWidgets.QLabel("Kişi Giriş")
        self.textEditTitle.setFont(fontTText)
        self.textEditTitle.setAlignment(QC.Qt.AlignCenter)

        self.persono = QtWidgets.QSpinBox()
        self.persono.setValue(1)
        self.persono.valueChanged.connect(self.valueError)

        self.personumber = QtWidgets.QLabel("Kaç Kişi Çekilecek ?")
        self.personumber.setAlignment(QC.Qt.AlignCenter)
        self.personumber.setFont(fontQlabel)

        self.personTransferBtn = QtWidgets.QPushButton(
            QtGui.QIcon("images/user-add.svg"), "Kişi Ekle")
        self.personTransferBtn.clicked.connect(self.persontransfer)
        self.personTransferBtn.setFont(fontBtn)
        self.personAllClearBtn = QtWidgets.QPushButton(
            QtGui.QIcon("images/trash.svg"), "Tüm Kişileri Çıkar")
        self.personAllClearBtn.clicked.connect(self.personAllClear)
        self.personAllClearBtn.setFont(fontBtn)
        self.personRemoveBtn = QtWidgets.QPushButton(
            QtGui.QIcon("images/user-remove.svg"), "Kişi Çıkar")
        self.personRemoveBtn.clicked.connect(self.personremove)
        self.personRemoveBtn.setFont(fontBtn)
        self.start = QtWidgets.QPushButton(
            QtGui.QIcon("images/start.png"), "Başlat")
        self.start.clicked.connect(self.startGiveaway)
        self.start.setFont(fontBtn)
        self.wait = QtWidgets.QLabel("")
        self.wait.setFont(fontWait)
        self.wait.setAlignment(QC.Qt.AlignCenter)
        self.result = QtWidgets.QPushButton(
            QtGui.QIcon("images/giveaway.png"), "Sonuç")
        self.result.clicked.connect(self.openResult)
        self.result.setFont(fontBtn)
        self.result.setHidden(True)

        horizontal1 = QtWidgets.QHBoxLayout()
        vertical1 = QtWidgets.QVBoxLayout()

        horizontal2 = QtWidgets.QHBoxLayout()
        vertical2 = QtWidgets.QVBoxLayout()

        vertical3 = QtWidgets.QVBoxLayout()

        vertical1.addWidget(self.textEditTitle)
        vertical1.addWidget(self.textEdit)

        horizontal1.addLayout(vertical1)
        horizontal1.addStretch()
        horizontal1.addLayout(vertical3)
        horizontal1.addStretch()
        horizontal1.addLayout(horizontal2)

        vertical2.addWidget(self.listTitle)
        vertical2.addWidget(self.textList)

        horizontal2.addLayout(vertical2)

        vertical3.addStretch()
        vertical3.addWidget(self.personTransferBtn)
        vertical3.addWidget(self.personRemoveBtn)
        vertical3.addWidget(self.personAllClearBtn)
        vertical3.addStretch()
        vertical3.addWidget(self.personumber)
        vertical3.addWidget(self.persono)
        vertical3.addWidget(self.start)
        vertical3.addWidget(self.wait)
        vertical3.addWidget(self.result)
        vertical3.addStretch()

        window = QtWidgets.QWidget()
        window.setLayout(horizontal1)

        self.setCentralWidget(window)
        self.show()

    def valueError(self):
        if self.persono.value() <= 0:
            self.persono.setValue(1)
            QtWidgets.QMessageBox.critical(
                self, "Hata !", "Kişi sayısı 0 olamaz!", QtWidgets.QMessageBox.Ok)

    def persontransfer(self):
        persons = self.textEdit.toPlainText()
        personlist = persons.split()
        for person in personlist:
            self.textList.addItem(person)
        self.textEdit.clear()

    def personAllClear(self):
        self.textList.clear()

    def personremove(self):
        for item in self.textList.selectedItems():
            self.textList.takeItem(self.textList.row(item))

    def startGiveaway(self):
        try:
            persons = []
            for index in range(self.textList.count()):
                persons.append(self.textList.item(index).text())
            global winperson
            winperson = []
            while True:
                rndm = random2.choice(persons)
                winperson.append(rndm)
                if len(winperson) == int(self.persono.value()):
                    self.wait.setText("Kişi Çekiliyor...")
                    QtTest.QTest.qWait(4000)
                    self.wait.setText("Tebrikler! 🎊")
                    QtTest.QTest.qWait(2000)
                    self.wait.setHidden(True)
                    self.result.show()
                    break
                elif self.persono.value() == 0:
                    QtWidgets.QMessageBox.critical(
                        self, "Hata!", "Lütfen Bir Kişi Sayısı Belirtiniz!")
                    break
        except IndexError:
            QtWidgets.QMessageBox.critical(
                self, "Hata!", "Liste İçerisi Boş!\nLütfen liste içerisine kişi ekleyiniz!")

    def openResult(self):
        self.resultOpen = resulT()
        self.resultOpen.show()

class resulT(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Kazananlar!")
        self.setGeometry(750, 150, 500, 500)
        self.setWindowIcon(QtGui.QIcon("images/giveaway.png"))

        fontTitle = QtGui.QFont("Aldrich", 28)
        fontText = QtGui.QFont("Cairo Semi-bold", 10)

        winlist = QtWidgets.QListWidget()
        winlist.setFont(fontText)
        winlist.setItemAlignment(QC.Qt.AlignCenter)

        for winper in winperson:
            winlist.addItem(winper)

        sonucTitle = QtWidgets.QLabel("🎉 Kazananlar 🎉")
        sonucTitle.setFont(fontTitle)
        sonucTitle.setAlignment(QC.Qt.AlignCenter)

        horizontal = QtWidgets.QHBoxLayout()
        vertical = QtWidgets.QVBoxLayout()

        horizontal.addStretch()
        horizontal.addWidget(winlist)
        horizontal.addStretch()

        vertical.addStretch()
        vertical.addWidget(sonucTitle)
        vertical.addStretch()
        vertical.addLayout(horizontal)
        vertical.addStretch()

        self.setLayout(vertical)
        self.show()

stylesheet = """
 QPushButton{
   border: 4px solid #E4E4E4;
   border-radius: 10px;
   width: 180px;
   height: 30px;
   color: #E4E4E4;
 }
 QPushButton:hover{
     color: black;
 }
 QTextEdit{
     color: white;
     font-size: 14px;
     border: 6px solid #E4E4E4;
     border-radius: 8px;
 }
 QListWidget{
     color: white;
     font-size: 14px;
     border: 6px solid #E4E4E4;
     border-radius: 8px;
 }
 QSpinBox{
     border: 4px solid #E4E4E4;
     border-radius: 6px;
     color: #E4E4E4;
 }
 QWidget{
     color: #E4E4E4;
     background-color: #7A7A7A;
 }
"""

app = QtWidgets.QApplication(sys.argv)
app.setStyleSheet(stylesheet)
main = mainWindow()
sys.exit(app.exec_())


# -*- coding: utf-8 -*-
"""
Created on Tue Feb 19 14:23:04 2019

@author: khaled.ghobashy
"""

import bpy
import numpy as np

from smbd.utilities.numerics.spatial_alg import triad

class cylinder_geometry(object):

    def __init__(self,arg1,arg2,ro=10,ri=0):
        self.name = 'cyl'
        self.p1 = arg1
        self.p2 = arg2
        self.ro = ro
        self.ri = ri
        self.n = 30
        self.axis = self.p2 - self.p1
        self.norm = self.axis/np.linalg.norm(self.axis)
        self.create_obj()

    @property
    def R(self):
        arr = np.array(self.obj.location)
        arr = np.reshape(arr, (3,1))
        return arr

    def create_obj(self):
        self._create_data()
        verts = self.data['verts']
        faces = self.data['faces']
        mesh = bpy.data.meshes.new('%s_mesh'%self.name)
        self.mesh = mesh
        obj  = bpy.data.objects.new(self.name, mesh)
        self.obj = obj
        mesh.from_pydata(verts, [], faces)
        mesh.update()
        bpy.context.scene.objects.link(obj)
        obj.rotation_mode = 'QUATERNION'
        obj.select = True
        bpy.context.scene.objects.active = obj
        bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS')
        obj.select = False

    def _create_data(self):
        c1_verts = self._create_circle_verts(self.p1)
        c2_verts = self._create_circle_verts(self.p2)
        faces = self._create_cylinder_faces()
        self.data = {'verts':c1_verts+c2_verts,'faces':faces}

    def _create_circle_verts(self,p):
        p = p[:,0]
        frame = triad(self.norm)
        v1 = frame[:,0]
        v2 = frame[:,1]
        angle = np.linspace(0,2*np.pi,self.n)
        verts = [tuple(p + self.ro*np.cos(i)*v1 + self.ro*np.sin(i)*v2) for i in angle]
        verts.append(verts[0])
        return verts

    def _create_cylinder_faces(self):
        n = self.n + 1
        faces = [(i, i+1, i+n+1, i+n) for i in range(self.n)]
        return faces

class triangular_prism(object):

    def __init__(self,p1,p2,p3,l=10):
        self.name = 'prism'
        self.p1 = p1
        self.p2 = p2
        self.p3 = p3
        self.l = l
        self.create()

    @property
    def R(self):
        arr = np.array(self.obj.location)
        arr = np.reshape(arr,(3,1))
        return arr

    def create(self):
        self._create_verts()
        self._create_faces()
        self._extrude()
        self._create_mesh()
        self._create_obj()

    def _create_verts(self):
        self._verts = [self.p1,self.p2,self.p3]
        self.n = len(self._verts)+1
        self._verts.append(self.p1)

    def _create_faces(self):
        top_face = tuple(range(self.n-1))
        bot_face = tuple(range(self.n,2*self.n,1))
        side_faces = [(i,i+1,i+self.n+1,i+self.n) for i in range(self.n-1)]
        self.faces = [top_face,bot_face] + side_faces

    def _extrude(self):
        v = self._get_normal() * self.l/2
        self.top_verts = [i+v for i in self._verts]
        self.bot_verts = [i-v for i in self._verts]
        self.top_verts = [tuple(i[:,0]) for i in self.top_verts]
        self.bot_verts = [tuple(i[:,0]) for i in self.bot_verts]

    def _create_mesh(self):
        self.mesh = bpy.data.meshes.new('%s_mesh'%self.name)
        side_verts = self.top_verts + self.bot_verts
        cape_verts = self.top_verts[:-1] + self.bot_verts[:-1]
        verts = side_verts + cape_verts
        self.mesh.from_pydata(verts, [], self.faces)
        self.mesh.update()

    def _create_obj(self):
        self.obj = obj  = bpy.data.objects.new(self.name, self.mesh)
        bpy.context.scene.objects.link(self.obj)
        obj.rotation_mode = 'QUATERNION'
        obj.select = True
        bpy.context.scene.objects.active = obj
        bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS')
        obj.select = False

    def _get_normal(self):
        verts = self._verts
        a1 = verts[1] - verts[0]
        a2 = verts[2] - verts[0]
        v = np.cross(a2,a1,axisa=0,axisb=0)
        v = np.reshape(v,(3,1))
        v = v/np.linalg.norm(v)
        return abs(v)

class composite_geometry(object):
    pass


import sys
import yaml
import operator

NUM_SYM = u"<num>"
EOS_SYM = u"<eos>"
RARE_SYM = u"<rare>"
UNK_SYM = u"<unk>"
lstProgressSymbol = ['-', '\\', '|', '/']

def sortDictByValue(dInput, reverse=False):
	return sorted(dInput.items(), key=operator.itemgetter(1), reverse=reverse)

def printProgressSymbol(status):
    idx = status % len(lstProgressSymbol)
    sys.stdout.write("\r")
    sys.stdout.write(lstProgressSymbol[idx])
    sys.stdout.flush()

def printSomething(str):
	sys.stdout.write(str)
	sys.stdout.flush()

def loadModelConfig(path2config):
	config = {}
	with open(path2config) as fin:
		config = yaml.load(fin)
	return config