#
# 08_ramas.py
#
# Open one TCP socket.
# Read ASCII commands line-by-line until the client disconnects.
#
# Example:
#
# CLEAR
# TREE X=0 Y=0 Z=0 HEIGHT=2.5 RBASE=0.08 RTOP=0.04
# TREE X=2 Y=0 Z=0 HEIGHT=2.4 RBASE=0.08 RTOP=0.04
# BRANCH HEIGHT=2.4 LENGTH=1.2 ANGLE=45 AZIMUTH=125 
#

import bpy
import socket
from mathutils import Matrix
from mathutils import Vector
import math

###########################################
class Tree: 
    def __init__(self,
        x,
        y,
        z,
        height,
        radiusBase,
        radiusTop):

        self.x = x
        self.y = y
        self.z = z
        self.height = height
        self.radiusBase = radiusBase
        self.radiusTop = radiusTop
        self.blenderObject = None
        self.branches=[]

tree = None

class Branch: 
    def __init__(self,
        height,
        length,
        branchAngle,
        azimuth,
        radiusBase,
        radiusTop):

        self.height = height
        self.length = length
        self.branchAngle = branchAngle
        self.azimuth=azimuth
        self.radiusBase = radiusBase
        self.radiusTop = radiusTop
        self.blenderObject = None
        self.branches=[]
        self.buds=[]

# ------------------------------------------------------------
# Configuration
# ------------------------------------------------------------
HOST = "127.0.0.1"
PORT = 5000

# ------------------------------------------------------------
# Scene utilities
# ------------------------------------------------------------
def clear_scene():
    bpy.ops.object.select_all(action='SELECT')
    bpy.ops.object.delete(use_global=False)

# ------------------------------------------------------------
# Geometry
# ------------------------------------------------------------
def create_tree(x,
                 y,
                 z,
                 height,
                 radius_base,
                 radius_top):

    bpy.ops.mesh.primitive_cone_add(
        vertices=32,
        radius1=radius_base,
        radius2=radius_top,
        depth=height,
        location=(x,
                  y,
                  z + height / 2.0)
    )

    tree = Tree(
        x,
        y,
        z,
        height,
        radius_base,
        radius_top
       )
    tree.blenderObject = bpy.context.active_object
    tree.blenderObject.name = "TelephoneTree"

    print("created tree @ ", tree.x, " ", tree.y)

    return tree

# -----------------------------------
# BRANCH HEIGHT=2.4 LENGTH=1.2 ANGLE=45 AZIMUTH=125 
# -----------------------------------
def create_branch(
    tree,
    height,
    length,
    axial,
    azimuth,
    radius_base,
    radius_end,
    ):

    print("     Create_branch for tree @ ", tree.x, ", ", tree.y)
    bpy.ops.mesh.primitive_cone_add(
        vertices=32,
        radius1=radius_base,
        radius2=radius_end,
        depth=length,
        location=(0,0,0)
        )

    branch = Branch(
        height,
        length,
        axial,
        azimuth,
        radius_base,
        radius_end)

    branch.blenderObject = bpy.context.active_object
    branch.blenderObject.name = "Branch"
    mesh = branch.blenderObject.data

    offset = Vector((0.0, 0.0, length/2.0))

    for vertex in mesh.vertices:
        vertex.co += offset

    branch.blenderObject.rotation_euler = (
       0.0,
       math.radians(axial),
       math.radians(azimuth)
       )

    aP = attachmentPoint(tree, branch)

    bpy.ops.mesh.primitive_uv_sphere_add( radius=0.13, location=(aP.x, aP.y, aP.z) )

    branch.blenderObject.location.x  = aP.x
    branch.blenderObject.location.y  = aP.y
    branch.blenderObject.location.z  = aP.z

    print("Length: ", branch.length,  " Axial: ", branch.branchAngle," Azimuth: ", branch.azimuth,
          " Base: ", branch.radiusBase," End: ", branch.radiusTop);

    return branch

# ------------------------------------------------------------
def attachmentPoint(tree, branch):

    attachment = Vector((0.0, 0.0, 0.0))

#    print("     tree.RadiusBase ", tree.radiusBase)
    radius = tree.radiusBase - (tree.radiusBase - tree.radiusTop) * branch.height / tree.height

    az = math.radians(branch.azimuth)

    attachment.x = tree.x + radius * math.cos(az)
    attachment.y = tree.y + radius * math.sin(az)
    attachment.z = tree.z + branch.height
    print(" azimuth degrees: ", branch.azimuth, "attach.x: ", attachment.x, " .y: ", attachment.y);

    return attachment


# ------------------------------------------------------------
# Parse KEY=VALUE parameters
# ------------------------------------------------------------
def parse_parameters(tokens):
    values = {}

    for token in tokens:
        if "=" not in token:
            continue
        key, value = token.split("=", 1)
        values[key.upper()] = float(value)

    return values

# ############################################################
# Command handlers
# ------------------------------------------------------------
def command_clear():
    print("CLEAR scene")
    clear_scene()


# ----------------------------------------------------
def command_tree(tokens):
    values = parse_parameters(tokens)
    tree = create_tree(
        x=values["X"],
        y=values["Y"],
        z=values["Z"],
        height=values["HEIGHT"],
        radius_base=values["RBASE"],
        radius_top=values["RTOP"]
        )
    return(tree)

#-----------------------------------------------------
# BRANCH LENGTH=1.40 AZIMUTH=30.00 ANGLE=72.00
def command_branch(tree, tokens):

    values = parse_parameters(tokens)
    branch = create_branch(
        tree,
        height=values["HEIGHT"],
        length=values["LENGTH"],
        axial=values["ANGLE"],
        azimuth=values["AZIMUTH"], 
        radius_base=0.08,
        radius_end=0.02
    )

    return         

# ------------------------------------------------------------
# Split buffer into tokens, and select command to execute
#    tree must be created before branch-----
# ------------------------------------------------------------
def process_command(line):
    global tree

    line = line.strip()

    if line == "":
        return True
    tokens = line.split()
    command = tokens[0].upper()
    print ("Command received: ", command)
    if command == "END":
        return False

    elif command == "CLEAR":
        command_clear()

    elif command == "TREE":
        tree = command_tree(tokens[1:])

    elif command == "BRANCH":
        command_branch(tree, tokens[1:])

    return True


# ------------------------------------------------------------
# Main
# ------------------------------------------------------------
print()
print("----------------------------------------")
print("08_ramas.py")
print("Listening on {}:{}...".format(HOST, PORT))
print("----------------------------------------")

server = socket.socket(socket.AF_INET,
                       socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET,
                  socket.SO_REUSEADDR, 1)
server.bind((HOST, PORT))
server.listen(1)
connection, address = server.accept()
print("Connection from", address)
sockfile = connection.makefile("r")

while True:
    line = sockfile.readline()

    if line == "":
        print("Client disconnected.")
        break
    if not process_command(line):
        print("END received.")
        break

sockfile.close()
connection.close()
server.close()

print("Finished.")
print("----------------------------------------")


