import math
from math import log
import plotly.plotly as py
import plotly
import plotly.graph_objs as go
import random

force = 1
optimal_dist = 10
damping = .5  # % loss of velocity between moves
iterations = 40


def pos_or_neg(number):
    if number > 0:
        return 1
    elif number < 0:
        return -1
    else:
        return 0

def three_d_dist(x, y, z, xo=0, yo=0, zo=0):
    absdist = math.sqrt(math.pow(x-xo, 2) + math.pow(y-yo, 2) + pow(z-zo, 2))
    if absdist > 0:
        return math.sqrt(math.pow(x-xo, 2) + math.pow(y-yo, 2) + pow(z-zo, 2))
    else:
        return random.random() * .01


class Node:
    def __init__(self, x, y, z, parent=None, children=[], nodelabel='', nodename=''):
        self.parent = parent
        self.children = children
        self.nodelabel = nodelabel
        self.nodename = nodename
        self.x = x
        self.y = y
        self.z = z
        self.vx = 0
        self.vy = 0
        self.vz = 0

    def add_force(self, fx, fy, fz):
        # f=m*a   mass = 1, so f=a
        self.vx += fx
        self.vy += fy
        self.vz += fz

    def move(self):
        self.vx *= (1 - damping)
        self.vy *= (1 - damping)
        self.vz *= (1 - damping)
        self.x += self.vx
        self.y += self.vy
        self.z += self.vz


nodesdict = {}
pass
nodesdict['rootnode'] = Node(0, 0, 0, nodelabel='rootnode')

with open('nodelist.txt', 'r') as nlf:
    #for line in nlf:
    for i in range(40):
        line = nlf.readline()
        #print('------------------')
        rline = line[:-1]
        labels = rline.split('__')
        parent = 'rootnode'
        for nodelabel in labels:
            if parent != 'rootnode':
                nodename = parent + '__' + nodelabel
            else:
                nodename = nodelabel
            nodesdict[nodename] = Node((random.random() - .5) * 10, (random.random() - .5) * 10,
                                       0, parent=parent, nodelabel=nodelabel)
            #print(parent, nodelabel, nodename)
            parent = nodename


for i in range(iterations):
    # collect all forces from other nodes (excluding the rootnode, and itself)
    for key, node in nodesdict.items():
        for okey, onode in nodesdict.items():
            if key != okey: # a node exerts no force unto itself
                dist = three_d_dist(node.x, node.y, node.z, onode.x, onode.y, onode.z)
                xforce = force / pow(dist, 2) * (node.x - onode.x)/dist
                yforce = force / pow(dist, 2) * (node.y - onode.y)/dist
                zforce = force / pow(dist, 2) * (node.z - onode.z)/dist
                node.add_force(xforce, yforce, zforce)
        if key == 'rootnode': # rootnode is an immovable object, ignore forces exerted on it
            continue
        pdist = three_d_dist(node.x, node.y, node.z, nodesdict[node.parent].x, nodesdict[node.parent].y, nodesdict[node.parent].z)
        pforcex = -force * log(pdist / optimal_dist) * (node.x - nodesdict[node.parent].x) / pdist
        pforcey = -force * log(pdist / optimal_dist) * (node.y - nodesdict[node.parent].y) / pdist
        pforcez = -force * log(pdist / optimal_dist) * (node.z - nodesdict[node.parent].z) / pdist
        node.add_force(pforcex, pforcey, pforcez)
        nodesdict[node.parent].add_force(-pforcex, -pforcey, -pforcez)

        node.move()


def build_vector(node, nx=[], ny=[], nz=[], text=[]):
    nx.append(node.x)
    ny.append(node.y)
    nz.append(node.z)
    text.append(node.nodelabel)

    parent_node = node.parent
    #print('parent: ', parent_node)
    if parent_node is not None:
        build_vector(nodesdict[node.parent], nx, ny, nz, text)


def build_trace(node):
    nx = []
    ny = []
    nz = []
    text = []
    build_vector(node, nx, ny, nz, text)
    # Create a trace for all nodes
    trace = go.Scatter(
        x=nx,
        y=ny,
        #z=nz,
        #mode='text+lines+markers',
        name='',
        text=text,
        hoverinfo='text',
        #line=dict(
        #    width=4)
    )

    return trace

data =[]
for key, node in nodesdict.items():
    if len(node.children) == 0:
        data.append(build_trace(node))


xaxis = dict(showbackground=True,
            showline=False,
            zeroline=True,
            showgrid=False,
            showticklabels=False,
            title='',
            showspikes=False
            )
yaxis = dict(showbackground=True,
             showline=False,
             zeroline=True,
             showgrid=False,
             showticklabels=False,
             title='',
             showspikes=False
             )
zaxis = dict(showbackground=True,
             showline=False,
             zeroline=True,
             showgrid=False,
             showticklabels=False,
             title='',
             showspikes=False
             )


layout = go.Layout(
    title="CXF JSON Schema",
    showlegend=False,
    scene=go.Scene(
        xaxis=go.XAxis(xaxis),
        yaxis=go.YAxis(yaxis),
        zaxis=go.ZAxis(zaxis),
    ),
    margin=go.Margin(
        t=100
    ),
    hovermode='closest',
)

fig = go.Figure(data=data, layout=layout)
plotly.offline.plot(fig, filename='cxf-json-schema-graph.html')
