# -*- coding: utf-8 -*-
import json

import pandas as pd
import requests
from py2cytoscape.data.edge_view import EdgeView
from py2cytoscape.data.node_view import NodeView

from . import BASE_URL, HEADERS
from py2cytoscape.data.util_network import NetworkUtil

BASE_URL_NETWORK = BASE_URL + 'networks'


class CyNetworkView(object):

    def __init__(self, network=None, suid=None):
        if network is None:
            raise ValueError('Network is required.')

        # Validate required argument
        if pd.isnull(suid):
            raise ValueError("View SUID is missing.")
        else:
            self.__network = network
            self.__id = suid

        self.__url = BASE_URL_NETWORK + '/' \
                     + str(self.__network.get_id()) + '/views/' + str(self.__id)

    def get_id(self):
        """
        Get session-unique ID of this network view

        :return: SUID as integer
        """
        return self.__id

    def get_model_id(self):
        """
        Get network model SUID

        :return: model SUID as integer
        """
        return self.__network.get_id()

    def get_node_views(self):
        return self.__get_views('nodes')

    def get_edge_views(self):
        return self.__get_views('edges')

    def get_node_views_as_dict(self):
        return self.__get_views('nodes', format='dict')

    def get_edge_views_as_dict(self):
        return self.__get_views('edges', format='dict')

    def get_network_view_as_dict(self):
        return self.__get_views('network', format='dict')

    def get_node_views_as_dataframe(self):
        return self.__get_views('nodes', format='df')

    def get_edge_views_as_dataframe(self):
        return self.__get_views('edges', format='df')

    def __get_views(self, obj_type=None, format='view'):
        url = self.__url + '/' + obj_type
        views = requests.get(url).json()
        if format is 'dict':
            if obj_type is 'network':
                return self.__get_network_view_dict(views)
            else:
                return self.__get_view_dict(views)

        elif format is 'view':
            return self.__get_view_objects(views, obj_type)

        else:
            raise ValueError('Format not supported: ' + format)

    def __get_view_objects(self, views, obj_type):
        view_list = []
        if obj_type is 'nodes':
            for view in views:
                view = NodeView(self, view['SUID'], obj_type)
                view_list.append(view)

        elif obj_type is 'edges':
            for view in views:
                view = EdgeView(self, view['SUID'], obj_type)
                view_list.append(view)

        else:
            raise ValueError('No such object type: ' + obj_type)

        return view_list

    def __get_view_dict(self, views):
        # reformat return value to simple dict
        view_dict = {}
        for view in views:
            key = view['SUID']
            values = view['view']
            # Flatten the JSON
            key_val_pair = {}
            for entry in values:
                vp = entry['visualProperty']
                value = entry['value']
                key_val_pair[vp] = value
            view_dict[key] = key_val_pair

        return view_dict

    def __get_view_df(self, views):
        # reformat return value to simple dict
        view_dict = {}

        for view in views:
            key = view['SUID']
            values = view['view']
            # Flatten the JSON
            key_val_pair = {}
            for entry in values:
                vp = entry['visualProperty']
                value = entry['value']
                key_val_pair[vp] = value
            view_dict[key] = key_val_pair

        return view_dict

    def __get_network_view_dict(self, values):
        # reformat return value to simple dict
        view_dict = {}
        # Flatten the JSON
        for entry in values:
            vp = entry['visualProperty']
            value = entry['value']
            view_dict[vp] = value

        return view_dict

    def update_node_views(self, visual_property=None, values=None, key_type='suid'):
        self.__update_views(visual_property, values, 'nodes', key_type)

    def batch_update_node_views(self, value_dataframe=None):
        self.__batch_update(value_dataframe, 'nodes')

    def batch_update_edge_views(self, value_dataframe=None):
        self.__batch_update(value_dataframe, 'edges')

    def update_edge_views(self, visual_property=None, values=None, key_type='suid'):
        self.__update_views(visual_property, values, 'edges', key_type)

    def update_network_view(self, visual_property=None, value=None):
        """
        Updates single value for Network-related VP.

        :param visual_property:
        :param value:
        :return:
        """

        new_value = [
            {
                "visualProperty": visual_property,
                "value": value
            }
        ]
        requests.put(self.__url + '/network', data=json.dumps(new_value),
                     headers=HEADERS)

    def __update_views(self, visual_property, values,
                       object_type=None, key_type='suid'):
        if key_type is 'name':
            name2suid = NetworkUtil.name2suid(self.__network)

        body = []
        for key in values.keys():
            if key_type is 'name':
                suid = name2suid[key]
                if suid is None:
                    continue
            else:
                suid = key

            new_value = self.__create_new_value(suid, visual_property,
                                                values[key])
            body.append(new_value)
        requests.put(self.__url + '/' + object_type, data=json.dumps(body), headers=HEADERS)

    def __create_new_value(self, suid, visual_property, value):
        return {
            "SUID": suid,
            "view": [
                {
                    "visualProperty": visual_property,
                    "value": value
                }
            ]
        }

    def __batch_update(self, df, object_type=None):
        body = []

        columns = df.columns
        for index, row in df.iterrows():
            entry = {
                'SUID': int(index),
                'view': self.__create_new_values_from_row(columns, row)
            }
            body.append(entry)

        requests.put(self.__url + '/' + object_type, data=json.dumps(body), headers=HEADERS)

    def __create_new_values_from_row(self, columns, row):
        views = []
        for column in columns:
            view = {
                "visualProperty": column,
                "value": row[column]
            }
            views.append(view)

        return views
