#include "OpenRM_bond.h"

#include "Flaw.h"
#include "OpenRMWidget.h"
#include "Bond.h"
#include "Bond_kit.h"
#include "Atom.h"
#include "Program_title.h"

#include <rm/rm.h>

namespace MM
{

OpenRM_bond::
OpenRM_bond (Bond_kit & kit, RMnode *parent_node, OpenRMWidget *rm_widget) 
: 
    Visual_bond_frame(kit),
    bond_node_  (0),
    parent_node_(parent_node),
    rm_widget_  (rm_widget),
    show_flag_  (true),
    
    style_              (Visual_style::CPK),
    //style_              (Visual_style::wireframe),
    //style_              (Visual_style::stick),
    wireframe_node_     (0),
    line_               (0),
    wireframe_CPK_node_ (0),
    bi_line_            (0),
    stick_node_         (0),
    bi_cylinder_        (0),
    ball_and_stick_node_(0),
    cylinder_           (0)
{
    REQUIRE ("", parent_node != 0); 
    REQUIRE ("", rm_widget   != 0);
}

OpenRM_bond::
~OpenRM_bond ()
{
    RMenum result;

    // for prototype:
    if (wireframe_node_ == 0)
        return;

    if (show_flag_ == true && bond_node_ !=0)
    {
        rmNodeRemoveChild (parent_node_, bond_node_);
        show_flag_ = false;
    }

    result = rmNodeDelete (wireframe_node_);
    if (result == RM_WHACKED)
        FLAW ("rmNodeDelete (wireframe_node_)    "
              "@ OpenRM_bond::~OpenRM_bond");
    
    result = rmNodeDelete (wireframe_CPK_node_);
    if (result == RM_WHACKED)
        FLAW ("rmNodeDelete (wireframe_CPK_node_)    "
              "@ OpenRM_bond::~OpenRM_bond");
    
    result = rmNodeDelete (stick_node_);
    if (result == RM_WHACKED)
        FLAW ("rmNodeDelete (stick_node_)    "
              "@ OpenRM_bond::~OpenRM_bond");
    
    result = rmNodeDelete (ball_and_stick_node_);
    if (result == RM_WHACKED)
        FLAW ("rmNodeDelete (ball_and_stick_node_)    "
              "@ OpenRM_bond::~OpenRM_bond");
    
    show_flag_ = false;
    rm_widget_->update ();
}

Visual_bond * OpenRM_bond::
clone (Bond_kit & kit) const
{
    OpenRM_bond *result = new OpenRM_bond (kit, parent_node_, rm_widget_);

    RMenum res = RM_WHACKED;

    // wireframe ==============================================================
    {
        result->line_ = rmPrimitiveNew (RM_LINES);
        if (result->line_ == 0)
            FLAW ("rmPrimitiveNew @ OpenRM_bond::clone");

        result->wireframe_node_
            = rmNodeNew ("Bond",  RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE);
        if (result->wireframe_node_ == 0)
            FLAW ("rmNodeNew @ OpenRM_bond::clone");

        res = rmNodeAddPrimitive (result->wireframe_node_, result->line_);      
        if (res == RM_WHACKED)
            FLAW ("rmNodeAddPrimitive @ OpenRM_bond::clone");

        res = rmNodeSetClientData (result->wireframe_node_, result, NULL);
        if (res == RM_WHACKED)
            FLAW ("rmNodeSetClientData @ OpenRM_bond::clone");
    }
    // wireframe_CPK ==========================================================
    {
        result->bi_line_ = rmPrimitiveNew (RM_LINES);
        if (result->bi_line_ == 0)
            FLAW ("rmPrimitiveNew @ OpenRM_bond::clone");

        result->wireframe_CPK_node_
            = rmNodeNew ("Bond",  RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE);
        if (result->wireframe_CPK_node_ == 0)
            FLAW ("rmNodeNew @ OpenRM_bond::clone");

        res =rmNodeAddPrimitive(result->wireframe_CPK_node_, result->bi_line_);
        if (res == RM_WHACKED)
            FLAW ("rmNodeAddPrimitive @ OpenRM_bond::clone");

        res = rmNodeSetClientData (result->wireframe_CPK_node_, result, NULL);
        if (res == RM_WHACKED)
            FLAW ("rmNodeSetClientData @ OpenRM_bond::clone");
    }
    // stick ==================================================================
    {
        result->bi_cylinder_ = rmPrimitiveNew (RM_CYLINDERS);
        if (result->bi_cylinder_ == 0)
            FLAW ("rmPrimitiveNew @ OpenRM_bond::clone");

        result->stick_node_
            = rmNodeNew ("Bond",  RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE);
        if (result->stick_node_ == 0)
            FLAW ("rmNodeNew @ OpenRM_bond::clone");

        float r[] = {0.3f, 0.3f};
        rmPrimitiveSetRadii (result->bi_cylinder_, 2, r, RM_COPY_DATA,NULL);
        //rmPrimitiveSetModelFlag (result->bi_cylinder_, RM_CYLINDERS_16);
        rmPrimitiveSetModelFlag (result->bi_cylinder_, RM_CYLINDERS_32);
        //rmPrimitiveSetModelFlag (result->bi_cylinder_, RM_CYLINDERS_64);
        //rmPrimitiveSetModelFlag (result->bi_cylinder_, RM_CYLINDERS_128);

        res = rmNodeAddPrimitive (result->stick_node_, result->bi_cylinder_);      
        if (res == RM_WHACKED)
            FLAW ("rmNodeAddPrimitive @ OpenRM_bond::clone");

        res = rmNodeSetClientData (result->stick_node_, result, NULL);
        if (res == RM_WHACKED)
            FLAW ("rmNodeSetClientData @ OpenRM_bond::clone");
    }

    // ball and stick =========================================================
    {
        result->cylinder_ = rmPrimitiveNew (RM_CYLINDERS);
        if (result->cylinder_ == 0)
            FLAW ("rmPrimitiveNew @ OpenRM_bond::clone");

        result->ball_and_stick_node_
            = rmNodeNew ("Bond",  RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE);
        if (result->ball_and_stick_node_ == 0)
            FLAW ("rmNodeNew @ OpenRM_bond::clone");

        float r[] = {0.3f, 0.3f};
        rmPrimitiveSetRadii (result->cylinder_, 1, r, RM_COPY_DATA,NULL);
        rmPrimitiveSetModelFlag (result->cylinder_, RM_CYLINDERS_32);

        res = rmNodeAddPrimitive (result->ball_and_stick_node_, 
                                  result->cylinder_);
        if (res == RM_WHACKED)
            FLAW ("rmNodeAddPrimitive @ OpenRM_bond::clone");

        res = rmNodeSetClientData (result->ball_and_stick_node_, result, NULL);
        if (res == RM_WHACKED)
            FLAW ("rmNodeSetClientData @ OpenRM_bond::clone");
    }

    result->set_order ();
    result->set_style (style_);
    //result->renew ();

    ENSURE ("", result->line_               != 0);

    ENSURE ("", result->wireframe_node_     != 0);
    ENSURE ("", result->wireframe_CPK_node_ != 0);
    ENSURE ("", result->stick_node_         != 0);
    ENSURE ("", result->ball_and_stick_node_!= 0);

    ENSURE ("", result->parent_node_        != 0);
    ENSURE ("", result->rm_widget_          != 0);
    return result;
}

void OpenRM_bond::
set_order ()
{
    Bond & bond  = kit().bond();
    if (bond.atom_count() != 2)
        return;

    Order  order = bond.order();

    //RMcolor4D    dummy_color[2];
    //dummy_color[0].r = dummy_color[1].r = 0.9F;
    //dummy_color[0].g = dummy_color[1].g = 0.8F;
    //dummy_color[0].b = dummy_color[1].b = 0.0F;

    // wireframe ==============================================================
    {
        Visual_style style (Visual_style::wireframe);
        RMcolor4D    color;
        style.color (bond, color.r, color.g, color.b, color.a);

        if (order == Order::Single)
        {
            if (bond.atom(0).element() == "H" || bond.atom(1).element() == "H")
                rmNodeSetLineWidth (wireframe_node_, RM_LINEWIDTH_1);
            else
                rmNodeSetLineWidth (wireframe_node_, RM_LINEWIDTH_2);
        }
        else if (order == Order::Double)
        {
            rmNodeSetLineWidth (wireframe_node_, RM_LINEWIDTH_4);
        }
        else if (order == Order::Triple)
        {
            rmNodeSetLineWidth (wireframe_node_, RM_LINEWIDTH_5);
        }
        else if (order == Order::Aromatic)
        {
            rmNodeSetLineWidth (wireframe_node_, RM_LINEWIDTH_2);
            rmNodeSetLineStyle (wireframe_node_, RM_LINES_DOTTED);
        }
        else if (order == Order::Dummy)
        {
            rmNodeSetLineWidth (wireframe_node_, RM_LINEWIDTH_1);
            rmNodeSetLineStyle (wireframe_node_, RM_LINES_DOTTED);
        }

        if (bond.is_backbone())
        {
            rmNodeSetLineWidth (wireframe_node_, RM_LINEWIDTH_3);
        }

        rmPrimitiveSetColor4D (line_, 1, &color, RM_COPY_DATA, NULL);

    }
    // wireframe_CPK ==========================================================
    {
        Visual_style style (Visual_style::wireframe_CPK);
        RMcolor4D    color[2];
        style.color (bond, color[0].r, color[0].g, color[0].b, color[0].a,
                           color[1].r, color[1].g, color[1].b, color[1].a);

        if (   bond.atom(0).element() == "H" 
            || bond.atom(1).element() == "H" 
            || order == Order::Dummy)
            rmNodeSetLineWidth (wireframe_CPK_node_, RM_LINEWIDTH_1);
        else
            rmNodeSetLineWidth (wireframe_CPK_node_, RM_LINEWIDTH_2);

        rmPrimitiveSetColor4D(bi_line_, 2, color, RM_COPY_DATA, NULL);
    }
    // stick ==================================================================
    {
        Visual_style style (Visual_style::stick);
        RMcolor4D    color[2];
        style.color (bond, color[0].r, color[0].g, color[0].b, color[0].a,
                           color[1].r, color[1].g, color[1].b, color[1].a);

        rmPrimitiveSetColor4D (bi_cylinder_, 2, color, RM_COPY_DATA, NULL);
    }
    // ball and stick =========================================================
    {
        Visual_style style (Visual_style::ball_and_stick);
        RMcolor4D    color;
        style.color (bond, color.r, color.g, color.b, color.a);

        rmPrimitiveSetColor4D (cylinder_, 1, &color, RM_COPY_DATA, NULL);
    }

    if (order == Order::Dummy)
    {
        float r[] = {0.1f, 0.1f};
        rmPrimitiveSetRadii (bi_cylinder_, 2, r, RM_COPY_DATA,NULL);
        rmPrimitiveSetRadii (cylinder_,    1, r, RM_COPY_DATA,NULL);
    }
}

void OpenRM_bond::
show (bool on)
{
    if (on)
    {
        if (show_flag_ == false)
        {
            rmNodeAddChild (parent_node_, bond_node_);
            show_flag_ = true;
            renew ();
        }
        //show_flag_ = true;
    }
    else 
    {
        if (show_flag_ == true && bond_node_ !=0)
        {
            rmNodeRemoveChild (parent_node_, bond_node_);
            show_flag_ = false;
            renew ();
        }
        //show_flag_ = false;
    }
}

void OpenRM_bond::
renew_view ()
{
    RMcolor4D color[2];
    style_.color (kit().bond(), 
        color[0].r, color[0].g, color[0].b, color[0].a,
        color[1].r, color[1].g, color[1].b, color[1].a);

    if (kit().was_frozen())
    {
        ////color[0].r += 2.5;     color[0].g += 2.5;     color[0].b += .7;
        ////color[1].r += 2.5;     color[1].g += 2.5;     color[1].b += .7;

        //color[0].r += 2.5;   color[0].g += 2.5;     color[0].b += 2.5;
        //color[1].r += 2.5;   color[1].g += 2.5;     color[1].b += 2.5;

        //color[0].r *= .28F;  color[0].g *= 1.5F;    color[0].b *= 1.5F;
        //color[1].r *= .28F;  color[1].g *= 1.5F;    color[1].b *= 1.5F;
        color[0].r = 0.85F;  color[0].g = 0.99F;    color[0].b = 1.F;
        color[1].r = 0.85F;  color[1].g = 0.99F;    color[1].b = 1.F;
    }

    if (kit().is_selected())
    {
        //color[0].r *= .5F;
        //color[0].g *= .25F;
        //color[0].b *= .5F;

        //color[1].r *= .5F;
        //color[1].g *= .25F;
        //color[1].b *= .5F;

        color[0].r *= .6F;
        color[0].g *= .25F;
        color[0].b *= .6F;

        color[1].r *= .6F;
        color[1].g *= .25F;
        color[1].b *= .6F;
    }

    if (kit().is_picked())
    {
        color[0].r = float ((color[0].r * 0.5) + .40);
        color[0].g = float ((color[0].g * 0.5) + .40);
        color[0].b = float ((color[0].b * 0.5) + .30);

        color[1].r = float ((color[1].r * 0.5) + .40);
        color[1].g = float ((color[1].g * 0.5) + .40);
        color[1].b = float ((color[1].b * 0.5) + .30);
    }

    if (style_.type() == Visual_style::wireframe)
    {
#ifdef ASCALAPH
        color[0].r *= 3.5F;        color[0].g *= 3.5F;        color[0].b *= 3.5F;
        color[1].r *= 3.5F;        color[1].g *= 3.5F;        color[1].b *= 3.5F;
#else
        color[0].r *= 1.7F;        color[0].g *= 1.7F;        color[0].b *= 1.7F;
        color[1].r *= 1.7F;        color[1].g *= 1.7F;        color[1].b *= 1.7F;
#endif

        rmPrimitiveSetColor4D (line_, 1, color, RM_COPY_DATA, NULL);
    }

    else if (style_.type() == Visual_style::wireframe_CPK)
    {
        color[0].r *= 3.F;        color[0].g *= 3.F;        color[0].b *= 3.F;
        color[1].r *= 3.F;        color[1].g *= 3.F;        color[1].b *= 3.F;

        rmPrimitiveSetColor4D (bi_line_, 2, color, RM_COPY_DATA, NULL);
    }

    else if (style_.type() == Visual_style::stick)
        rmPrimitiveSetColor4D (bi_cylinder_, 2, color, RM_COPY_DATA, NULL);

    else if (style_.type() == Visual_style::ball_and_stick)
        rmPrimitiveSetColor4D (cylinder_, 1, color, RM_COPY_DATA, NULL);

    else if (style_.type() == Visual_style::CPK)
        ;

    else
        FLAW ("Unknown visual type    @ OpenRM_bond::renew_view");

    rm_widget_->update ();
}

void OpenRM_bond::
renew_coordinates ()
{
    Bond & bond   = kit().bond ();
    if (bond.atom_count() != 2)
        return;

    Atom & atom_0 = bond.atom(0);
    Atom & atom_1 = bond.atom(1);

    if (bond_node_ == wireframe_node_)
    {
        RMvertex3D  vertex[2];    
        vertex[0].x = float(atom_0.x());    
        vertex[0].y = float(atom_0.y());    
        vertex[0].z = float(atom_0.z());
        vertex[1].x = float(atom_1.x());    
        vertex[1].y = float(atom_1.y());    
        vertex[1].z = float(atom_1.z());

        RMvertex3D norm[] = {0,0,0, 0,0,0};
        rmPrimitiveSetNormal3D (line_, 2, norm,  RM_COPY_DATA, NULL);

        rmPrimitiveSetVertex3D (line_, 2, vertex, RM_COPY_DATA, NULL);
    }
    if (bond_node_ == ball_and_stick_node_)
    {
        RMvertex3D  vertex[2];    
        vertex[0].x = float(atom_0.x());    
        vertex[0].y = float(atom_0.y());    
        vertex[0].z = float(atom_0.z());
        vertex[1].x = float(atom_1.x());    
        vertex[1].y = float(atom_1.y());    
        vertex[1].z = float(atom_1.z());

        rmPrimitiveSetVertex3D (cylinder_, 2, vertex, RM_COPY_DATA, NULL);
    }
    else if (bond_node_ == wireframe_CPK_node_)
    {
        RMvertex3D  vertex[4];    
        vertex[0].x = float(atom_0.x());    
        vertex[0].y = float(atom_0.y());    
        vertex[0].z = float(atom_0.z());
        vertex[1].x = vertex[2].x = float(0.5*(atom_1.x() + atom_0.x()));    
        vertex[1].y = vertex[2].y = float(0.5*(atom_1.y() + atom_0.y()));    
        vertex[1].z = vertex[2].z = float(0.5*(atom_1.z() + atom_0.z()));
        vertex[3].x = float(atom_1.x());    
        vertex[3].y = float(atom_1.y());    
        vertex[3].z = float(atom_1.z());

        RMvertex3D norm[] = {0,0,0, 0,0,0, 0,0,0, 0,0,0};
        rmPrimitiveSetNormal3D (bi_line_, 4, norm,  RM_COPY_DATA, NULL);

        //RMvertex3D norm;
        //rmVertex3DDiff (&vertex[0], &vertex[3], &norm);
        //rmVertex3DNormalize (&norm);
        //norm.x *= 2.4;
        //norm.y *= 1.4;
        //norm.z *= 1.4;
        //rmPrimitiveSetNormal3D (bi_line_, 4, &norm,  RM_COPY_DATA, NULL);

        rmPrimitiveSetVertex3D (bi_line_, 4, vertex, RM_COPY_DATA, NULL);
    }

    else if (bond_node_ == stick_node_)
    {
        RMvertex3D  vertex[4];    
        vertex[0].x = float(atom_0.x());    
        vertex[0].y = float(atom_0.y());    
        vertex[0].z = float(atom_0.z());
        vertex[1].x = vertex[2].x = float(0.5*(atom_1.x() + atom_0.x()));    
        vertex[1].y = vertex[2].y = float(0.5*(atom_1.y() + atom_0.y()));    
        vertex[1].z = vertex[2].z = float(0.5*(atom_1.z() + atom_0.z()));
        vertex[3].x = float(atom_1.x());    
        vertex[3].y = float(atom_1.y());    
        vertex[3].z = float(atom_1.z());

        rmPrimitiveSetVertex3D (bi_cylinder_, 4, vertex, RM_COPY_DATA, NULL);
    }

    rm_widget_->update ();
}

void OpenRM_bond::
set_color (int R, int G, int B)
{
    style_.set_special_color (float (R / 255.0), 
                              float (G / 255.0), 
                              float (B / 255.0));
    renew_view ();
}

void OpenRM_bond::
set_style (Visual_style const & style)
{
    style_ = style;

    if (!show_flag_
        || kit().bond().atom_count() != 2 //fix prototype hack
       ) 
        return;
        
    if (bond_node_ !=0)
        rmNodeRemoveChild (parent_node_, bond_node_);


    if (style.type() == Visual_style::wireframe)
        bond_node_ = wireframe_node_;

    else if (style.type() == Visual_style::wireframe_CPK)
        bond_node_ = wireframe_CPK_node_;

    else if (style.type() == Visual_style::stick)
        bond_node_ = stick_node_;

    else if (style.type() == Visual_style::ball_and_stick)
        bond_node_ = ball_and_stick_node_;

    else if (style.type() == Visual_style::CPK)
        bond_node_ = 0;

    else
        FLAW ("Unknown visual type    @ OpenRM_bond::set_type");

    if (bond_node_ != 0)
    {
        RMenum res = rmNodeAddChild (parent_node_, bond_node_);
        if (res == RM_WHACKED)
            FLAW ("rmNodeAddChild    @ OpenRM_bond::set_type");
    }

    renew_coordinates ();
    renew_view ();
}

void OpenRM_bond::
renew_model ()
{
    rm_widget_->update ();
}

void OpenRM_bond::
renew ()
{
    set_order ();
    set_style (style_);
    //renew_view ();
    renew_model ();
}

}//MM

