#include "Visual_style.h"

#include "Flaw.h"
#include "Atom.h"
#include "Bond.h"
#include "View_atom_type_manager.h"
#include "View_atom_type.h"
#include "HSV.h"
#include "Program_title.h"

#if defined WIN32
#pragma warning( disable : 4244 )
#endif

namespace MM
{

Visual_style::
Visual_style ()
: 
    type_(CPK), 
    special_color_(false), 
    r_(0.f), 
    g_(0.f), 
    b_(0.f),
    a_(1.f)
{
}

Visual_style::
Visual_style (Type type) 
: 
    type_(type),
    special_color_(false), 
    r_(0.f), 
    g_(0.f), 
    b_(0.f),
    a_(1.f)  
{
}

void Visual_style::
color (Atom const & atom, float & r, float & g, float & b, float & a) const
{
    if (has_special_color())
    {
        r = r_;
        g = g_;
        b = b_;
        a = a_;
        return;
    }

    char const * element = atom.element().c_str();
    View_atom_type & type = View_atom_type_manager::singleton().type (element);

    double k = 1. / 255.; 

    r = (float) k * type.r();
    g = (float) k * type.g();
    b = (float) k * type.b();
    a = 1.F;
}

void Visual_style::
color (Bond const & bond, float & r, float & g, float & b, float & a) const
{
    float stub;
    color (bond, r, g, b, a, stub, stub, stub, stub);
}

namespace
{
    Element H = Element("H");
    Element C = Element("C");
    Element S = Element("S");
}

void Visual_style::
color (Bond const & bond,
       float & r0, float & g0, float & b0, float & a0,
       float & r1, float & g1, float & b1, float & a1) const
{
    if (has_special_color())
    {
        r0 = r1 = r_;
        g0 = g1 = g_;
        b0 = b1 = b_;
        a0 = a1 = a_;
        return;
    }

    Order  order = bond.order();

    if (order == Order::Dummy)
    {
        r0 = r1 = 0.8f;
        g0 = g1 = 0.7f;
        b0 = b1 = 0.5f;
        a0 = a1 = 1.f;
        return;
    }

    switch (type_)
    {
    case CPK:
        {
            break;
        }

    case stick:
        {
            Atom const & atom_0 = bond.atom(0);
            Atom const & atom_1 = bond.atom(1);

            char const * element_0 = atom_0.element().c_str();
            View_atom_type & type_0 = View_atom_type_manager::singleton().
                type (element_0);

            char const * element_1 = atom_1.element().c_str();
            View_atom_type & type_1 = View_atom_type_manager::singleton().
                type (element_1);

            double k = 1. / 255.; 
            double R, G, B;
            R = type_0.r();   G = type_0.g();   B = type_0.b();

            r0 = (float)(k * R);
            g0 = (float)(k * G);
            b0 = (float)(k * B);

            R = type_1.r();   G = type_1.g();   B = type_1.b();

            r1 = (float)(k * R);
            g1 = (float)(k * G);
            b1 = (float)(k * B);

            break;
        }

    case ball_and_stick:
        {
            if (order == Order::Single)
                {r0 = 0.58f;    g0 = 0.6f;    b0 = 0.5f;}

            else if (order == Order::Double)
                {r0 = 0.99f;    g0 = 0.5f;    b0 = 0.99f;}

            else if (order == Order::Triple)
                {r0 = 0.5f;     g0 = 0.99f;   b0 = 0.5f;}

            else if (order == Order::Aromatic)
                {r0 = 0.5f;     g0 = 0.8f;    b0 = 0.99f;}

            else 
                {r0 = 0.f;      g0 = 0.f;     b0 = 0.f;}

            break;
        }

    case wireframe:
        {
            Atom const & atom_0 = bond.atom(0);
            Atom const & atom_1 = bond.atom(1);

            if (atom_0.element() == S || atom_1.element() == S) 
            {
#ifdef ASCALAPH
                r0 = r1 = 1.f;    g0 = g1 = 1.f;    b0 = b1 = .2f;
#else
                r0 = r1 = 1.1f;    g0 = g1 = 1.1f;    b0 = b1 = .2f;
#endif
                break;
            }

            if (bond.is_backbone())
#ifdef ASCALAPH
                //{r0 = r1 = .60f;    g0 = g1 = .9f;    b0 = b1 = .99f;}
                {r0 = r1 =  .95f;   g0 = g1 =  .98f;  b0 = b1 = 1.f;}
#else
                {r0 = r1 = .0f;    g0 = g1 = 0.6f;    b0 = b1 = 0.6f;}
#endif
            else
#ifdef ASCALAPH
                //{r0 = r1 =  .95f;   g0 = g1 =  .98f;  b0 = b1 = 1.f;}
                {r0 = r1 =  .75f;   g0 = g1 =  .78f;  b0 = b1 = 0.8f;}
#else
                {r0 = r1 =  1.1f;   g0 = g1 =  1.15f;  b0 = b1 = 1.1f;}
#endif
            break;
        }

    case wireframe_CPK:
        {
            Atom const & atom_0 = bond.atom(0);
            Atom const & atom_1 = bond.atom(1);

            char const * element_0 = atom_0.element().c_str();
            View_atom_type & type_0 = View_atom_type_manager::singleton().
                type (element_0);

            char const * element_1 = atom_1.element().c_str();
            View_atom_type & type_1 = View_atom_type_manager::singleton().
                type (element_1);

            double k = 1. / 255.; 
            double R, G, B,     H, S, V;

            RGB_to_HSV (type_0.r(),   type_0.g(),   type_0.b(),  H, S, V);
            //S *= 2.5;    S = S > 1. ? 1. : S;
            S = 0.9;
            if (atom_0.element () == Element(6))
            {
                V = 80;
                S = 1.;
            }
            else
                V = 255;

            HSV_to_RGB (H, S, V, R, G, B);
        
            r0 = (float)(k * R);
            g0 = (float)(k * G);
            b0 = (float)(k * B);

            if (atom_0.element () == Element(1))
                r0 = g0 = b0 = k * 100;

            RGB_to_HSV (type_1.r(),   type_1.g(),   type_1.b(),  H, S, V);
            //S *= 2.5;    S = S > 1. ? 1. : S;
            S = 0.9;
            if (atom_1.element () == Element(6))
            {
                V = 80;
                S = 1.;
            }
            else
                V = 255;

            HSV_to_RGB (H, S, V, R, G, B);

            r1 = (float)(k * R);
            g1 = (float)(k * G);
            b1 = (float)(k * B);

            if (atom_1.element () == Element(1))
                r1 = g1 = b1 = k * 100;

            break;
        }

    default:
        FLAW ("Unknown visual style    @ Visual_style::color");
    }
    a0 = a1 = 1.f;
}

float Visual_style::
radius  (Atom const & atom) const
{
    char const * element = atom.element().c_str();

    View_atom_type & type = View_atom_type_manager::singleton().
        type (element);

    float radius = (float)type.radius();

    switch (type_)
    {
    case CPK:               return radius;
    case stick:             return .3F;
    case ball_and_stick:    return float(radius / 3.5);
    case wireframe:         return 0.F;
    case wireframe_CPK:     return float(radius / 15);

    default:
        FLAW ("Unknown visual style    @ Visual_style::radius");
    }

    return radius;
}

bool Visual_style::
set_special_color (float r, float g, float b, float a, bool on)
{
    special_color_ = on;

    r_ = r;
    g_ = g;
    b_ = b;
    a_ = a;

    return special_color_;
}

}//MM
