#!/usr/bin/env python3
import re

H=open("options_header.h","w")
C=open("options_header.cpp","w")

C.write("""
template<class T> void Write_Helper(std::ostream& out,const std::string& obj_name,const std::string& var,const OPTION<T>& value)
{
    if(!value.Valid()) return;
    LOG::fprintf(out,"set %s %s ",std::quoted(obj_name),std::quoted(var));
    RAW_OSTREAM ro(out);
    UNTYPED_OSTREAM<char> uo(ro);
    Write_Binary(uo,value.Value());
    LOG::fprintf(out,"\\n");
}

template<class T> void Read_Helper(std::istream& in,OPTION<T>& value)
{
    RAW_ISTREAM ri(in);
    UNTYPED_ISTREAM<char> ui(ri);
    T x;
    Read_Binary(ui,x);
    value.Set(x);
}

template<class T> void Merge_Helper(OPTION<T>& o,const OPTION<T>& n)
{
    if(n.Valid()) o=n;
}
""")

opts_hash={}
cre=re.compile(r"\bCOLOR\b")
int_d=re.compile(r"class d")
seen_templates={}

def generate_opts(cl_all,qt_all,prefix,entries):
    name,tl=cl_all
    qt_name,qt_tl=qt_all
    t0=""
    t1=""
    if len(tl)>0:
        t0="template<> "
        t1="<"+(",".join(tl))+">"
    qt_t0=""
    qt_t1=""
    if len(qt_tl)>0:
        qt_t0="template<class "+(",class ".join(qt_tl))+"> "
        qt_t1="<"+(",".join(qt_tl))+">"
        qt_t0=int_d.sub("int d",qt_t0)
    opts_hash[name]=entries
    H.write("{}struct {}{}\n".format(t0,name,t1))
    H.write("{\n")
    H.write("    static {} def;\n".format(name))
    H.write("    static const char* st_type_prefix;\n")
    for e in entries:
        var,type,deft,desc=e
        if cre.search(type):
            H.write("    typedef VECTOR<float,3> COLOR;\n")
            break
    for e in entries:
        var,type,deft,desc=e
        H.write("    OPTION<{}> {};\n".format(type,var))
    H.write("    void Read_Entry(std::istream& in);\n")
    H.write("    void Write(std::ostream& out,const std::string& obj_name) const;\n")
    H.write("    void Merge(const {}& o);\n".
            format(name,name))
    H.write("};\n\n")

    C.write("""void {}{}::Read_Entry(std::istream& in)
{{
    std::string var;
    in>>std::quoted(var);
""".format(name,t1))
    for e in entries:
        var,type,deft,desc=e
        C.write("    if(var==\"{}\"){{Read_Helper(in,{});return;}}\n".format(var,var))
    C.write("    LOG::printf(\"Unrecognized entry: \\\"%s\\\"\\n\",var);\n")
    C.write("};\n\n")
    
    C.write("void {}{}::Write(std::ostream& out,const std::string& obj_name) const\n".format(name,t1))
    C.write("{\n")
    for e in entries:
        var,type,deft,desc=e
        C.write("    Write_Helper(out,obj_name,\"{}\",{});\n".format(var,var))
    C.write("};\n\n")

    C.write("void {}{}::Merge(const {}& o)\n".
            format(name,t1,name,name))
    C.write("{\n")
    for e in entries:
        var,type,deft,desc=e
        C.write("    Merge_Helper({},o.{});\n".format(var,var))
    C.write("}\n\n")

    C.write("{}{} {}{}::def=\n".format(name,t1,name,t1))
    C.write("{\n")
    for e in entries:
        var,type,deft,desc=e
        if deft==None: deft="{}{{}}".format(type)
        C.write("    .{}={},\n".format(var,deft))
    C.write("};\n\n")

    C.write("const char* {}{}::st_type_prefix=\"{}\";\n".format(name,t1,prefix))

    T=open("gen-"+qt_name+".cpp","w")
    T.write("""
//#####################################################################
// Function Make_Dialog
//#####################################################################
{}void {}{}::
Make_Dialog(QT_WINDOW& window)
{{
    QT_COMPONENT_OPTIONS* dialog=new QT_COMPONENT_OPTIONS(window,*this);
    dialog->setWindowTitle(QString::fromStdString(name));
    OPT opt2,raw_opt2;
    opt2=opt;
    raw_opt2=raw_opt[0];
""".format(qt_t0,qt_name,qt_t1))
    for e in entries:
        var,type,deft,desc=e
        T.write("    dialog->Add(\"{}\",opt.{},raw_opt[0].{});\n".format(desc,var,var))
    T.write("""    dialog->Add_End_Buttons();
    int result=dialog->exec();
    if(result!=QDialog::Accepted)
    {{
        opt=opt2;
        raw_opt[0]=raw_opt2;
    }}
    window.update();
}}
//#####################################################################
// Function Read_Option
//#####################################################################
{}void {}{}::
Read_Option(std::istream& in,int level)
{{
    auto& o=raw_opt[level];
    o.Read_Entry(in);
    opt=raw_opt[3];
    opt.Merge(raw_opt[2]);
    opt.Merge(raw_opt[1]);
    opt.Merge(raw_opt[0]);
}}
//#####################################################################
// Function Set_Draw
//#####################################################################
{}void {}{}::
Set_Draw(bool draw)
{{
    SET(draw,draw);
}}
//#####################################################################
// Function Draw
//#####################################################################
{}bool {}{}::
Draw() const
{{
    return GET(draw);
}}
//#####################################################################
// Function Save_Settings
//#####################################################################
{}void {}{}::
Save_Settings(std::ostream& out)
{{
    raw_opt[0].Write(out,name);
}}
//#####################################################################
// Function Clear_Settings
//#####################################################################
{}void {}{}::
Clear_Settings()
{{
    raw_opt[0]={{}};
    opt=raw_opt[3];
    opt.Merge(raw_opt[2]);
    opt.Merge(raw_opt[1]);
    opt.Merge(raw_opt[0]);
}}
""".format(qt_t0,qt_name,qt_t1,qt_t0,qt_name,qt_t1,qt_t0,qt_name,qt_t1,qt_t0,qt_name,qt_t1,qt_t0,qt_name,qt_t1))
    
#generate_opts("VIEWER_OPTIONS",None,None,None,"QT_COMPONENT",None,
#              [
#                  ["draw","bool","true"],
#              ])

generate_opts(
    ["GLOBAL_OPTIONS",[]],
    ["QT_WORLD",[]],
    "global",
    [
        ["draw","bool","true","Show object in viewer"],
        ["name","std::string","std::string{}","Name to display in viewer"],
        ["is_2d","bool","false","Use 2d mode"],
        ["scale","float","1","Global scaling factor"],
        ["point_size","float","4","Global size for points"],
        ["view_scale","float","{}","Camera scale"],
        ["substeps_level","int","0","Substeps level"],
        ["window_size","VECTOR<int,2>","{640,480}","Window size"],
        ["target_position","VECTOR<float,3>","{}","Camera target position"],
        ["rotation_matrix","MATRIX<float,3>","{}","Camera rotation matrix"]
    ])

generate_opts(
    ["VECTOR_FIELD_OPTIONS",[]],
    ["QT_VECTOR_FIELD",["TV"]],
    "grid_vector",
    [
        ["draw","bool","true","Show object in viewer"],
        ["name","std::string","std::string{}","Name to display in viewer"],
        ["color","COLOR","COLOR{1,0,1}","color"],
        ["cell_centered","bool","false","data is stored at cells"],
        ["node_selectors","bool","false","Show node selection guides"],
        ["face_selectors","bool","false","Show face selection guides"],
        ["cell_selectors","bool","false","Show cell selection guides"]
    ])

generate_opts(
    ["AXES_OPTIONS",[]],
    ["QT_AXES",["d"]],
    "axes",
    [
        ["draw","bool","true","Show object in viewer"],
        ["name","std::string","std::string{}","Name to display in viewer"],
        ["length","float","1","Axis length"]
    ])

generate_opts(
    ["DEBUG_PARTICLES_OPTIONS",[]],
    ["QT_DEBUG_PARTICLES",["TV"]],
    "debug",
    [
        ["draw","bool","true","Show object in viewer"],
        ["name","std::string","std::string{}","Name to display in viewer"],
    ])

H.write("template<class T2> struct FACE_FIELD_OPTIONS;\n")

generate_opts(
    ["FACE_FIELD_OPTIONS",["bool"]],
    ["QT_FACE_FIELD",[]],
    "grid_face",
    [
        ["draw","bool","true","Show object in viewer"],
        ["name","std::string","std::string{}","Name to display in viewer"],
        ["cell_centered","bool","false","cell_centered"],
        ["draw_points","bool","false","draw_points"],
        ["draw_diamonds","bool","false","draw_diamonds"],
        ["color","COLOR","COLOR{1,0,0}","color"]
    ])

generate_opts(
    ["FACE_FIELD_OPTIONS",["int"]],
    ["QT_FACE_FIELD",[]],
    "grid_face",
    [
        ["draw","bool","true","Show object in viewer"],
        ["name","std::string","std::string{}","Name to display in viewer"],
        ["cell_centered","bool","false","cell_centered"],
        ["draw_points","bool","false","draw_points"],
        ["draw_diamonds","bool","false","draw_diamonds"],
        ["color_map","ARRAY<COLOR>",None,"color_map"]
    ])

generate_opts(
    ["FACE_FIELD_OPTIONS",["float"]],
    ["QT_FACE_FIELD",["T","d"]],
    "grid_face",
    [
        ["draw","bool","true","Show object in viewer"],
        ["name","std::string","std::string{}","Name to display in viewer"],
        ["cell_centered","bool","false","cell_centered"],
        ["draw_vorticity","bool","false","draw_vorticity"],
        ["draw_divergence","bool","false","draw_divergence"],
        ["draw_streamlines","bool","false","draw_streamlines"],
        ["use_seed_for_streamlines","bool","false","use_seed_for_streamlines"],
        ["draw_arrows","bool","true","draw_arrows"],
        ["draw_points","bool","false","draw_points"],
        ["draw_diamonds","bool","false","draw_diamonds"],
        ["use_fixed_color","bool","true","use color instead of color_map"],
        ["color","COLOR","COLOR{1,0,0}","color"],
        ["min","float","0","Map this value to 0 before color map"],
        ["max","float","1","Map this value to 1 before color map"],
        ["color_map","INTERPOLATED_COLOR_MAP<float>",None,"color_map"],
        ["number_of_steps","int","10","number_of_steps"],
        ["min_vorticity","float","FLT_MAX","min_vorticity"],
        ["max_vorticity","float","FLT_MIN","max_vorticity"],
        ["streamline_seed","int","0","streamline_seed"]
    ])

generate_opts(
    ["GRID_OPTIONS",[]],
    ["QT_GRID",["d"]],
    "grid",
    [
        ["draw","bool","true","Show object in viewer"],
        ["name","std::string","std::string{}","Name to display in viewer"],
        ["color","COLOR","COLOR{.5f,.5f,.5f}","color"],
        ["ghost","int","3","ghost"]
    ])

generate_opts(
    ["GROUP_OPTIONS",[]],
    ["QT_GROUP",[]],
    "group",
    [
        ["draw","bool","true","Show object in viewer"],
        ["name","std::string","std::string{}","Name to display in viewer"],
        ["add","ARRAY<std::string>",None,"add this component to the group"],
        ["source","std::string","std::string{}","Name of component from which to derive the transform"],
        ["attr","std::string","std::string{}","Name of PARTICLES attribute holding a FRAME"],
        ["particle_id","int","-1","PARTICLES index for FRAME"],
        ["frame","FRAME<VECTOR<float,3>>",None,"transformation for the group"]
    ])

generate_opts(
    ["PARTICLES_OPTIONS",[]],
    ["QT_PARTICLES",["TV"]],
    "particles",
    [
        ["draw","bool","true","Show object in viewer"],
        ["name","std::string","std::string{}","Name to display in viewer"],
        ["draw_vectors","bool","false","draw vectors"],
        ["default_color","COLOR","COLOR{1,0,0}","default_color"],
        ["min","float","0","Map this value to 0 before color map"],
        ["max","float","1","Map this value to 1 before color map"],
        ["color_map","INTERPOLATED_COLOR_MAP<float>",None,"Construct color from scalar data"],
        ["use_vector_color","bool","false","use fixed vector colors"],
        ["vector_color","COLOR","COLOR{1,0,0}","fixed vector colors"],
        ["visualize_matrix","bool","false","visualize matrix columns"],
        ["position_attr","std::string","std::string{\"X\"}","Particle position attribute name"],
        ["color_attr","std::string","std::string{\"color\"}","Particle color attribute name"],
        ["vector_attr","std::string","std::string{\"V\"}","Particle vector attribute name"],
        ["matrix_attr","std::string","std::string{}","Particle matrix attribute name"],
        ["use_color_prog","bool","false","Use program to compute value or color"],
        ["color_prog","std::string","std::string{}","Program to compute value or color"]
    ])

H.write("template<class T2> struct SCALAR_FIELD_OPTIONS;\n")

generate_opts(
    ["SCALAR_FIELD_OPTIONS",["bool"]],
    ["QT_SCALAR_FIELD",[]],
    "grid_scalar",
    [
        ["draw","bool","true","Show object in viewer"],
        ["name","std::string","std::string{}","Name to display in viewer"],
        ["draw_cell_colors","bool","true","draw cell colors"],
        ["draw_points","bool","false","draw_points"],
        ["color","COLOR","COLOR{1,0,0}","color"],
        ["cell_centered","bool","false","cell_centered"]
    ])

generate_opts(
    ["SCALAR_FIELD_OPTIONS",["int"]],
    ["QT_SCALAR_FIELD",[]],
    "grid_scalar",
    [
        ["draw","bool","true","Show object in viewer"],
        ["name","std::string","std::string{}","Name to display in viewer"],
        ["draw_cell_colors","bool","true","draw cell colors"],
        ["draw_points","bool","false","draw_points"],
        ["color_map","ARRAY<COLOR>",None,"color_map"],
        ["cell_centered","bool","false","cell_centered"] 
    ])

generate_opts(
    ["SCALAR_FIELD_OPTIONS",["float"]],
    ["QT_SCALAR_FIELD",["T","d"]],
    "grid_scalar",
    [
        ["draw","bool","true","Show object in viewer"],
        ["name","std::string","std::string{}","Name to display in viewer"],
        ["min","float","0","Map this value to 0 before color map"],
        ["max","float","1","Map this value to 1 before color map"],
        ["draw_cell_colors","bool","true","draw cell colors"],
        ["draw_smooth_field","bool","false","draw as smooth scalar field"],
        ["draw_points","bool","false","draw_points"],
        ["color_map","INTERPOLATED_COLOR_MAP<float>",None,"color_map"],
        ["draw_contours","bool","false","draw_contours"],
        ["num_contours","int","1","num_contours"],
        ["cell_centered","bool","false","cell_centered"]
    ])

H.write("template<int d> struct MESH_OPTIONS;\n")
generate_opts(
    ["MESH_OPTIONS",["1"]],
    ["QT_SEGMENTED_CURVE",["TV"]],
    "mesh",
    [
        ["draw","bool","true","Show object in viewer"],
        ["name","std::string","std::string{}","Name to display in viewer"],
        ["vertex_color","COLOR","COLOR{1,1,0}","vertex color"],
        ["draw_vertices","bool","false","display vertices"],
        ["color","COLOR","COLOR{1,0,0}","front color of triangles"],
        ["velocity_color","COLOR","COLOR{1,0,1}","velocity color"],
        ["draw_velocities","bool","false","display vertex velocites"]
    ])

generate_opts(
    ["MESH_OPTIONS",["2"]],
    ["QT_TRIANGULATED_OBJECT",["TV"]],
    "mesh",
    [
        ["draw","bool","true","Show object in viewer"],
        ["name","std::string","std::string{}","Name to display in viewer"],
        ["vertex_color","COLOR","COLOR{1,1,0}","vertex color"],
        ["draw_vertices","bool","false","display vertices"],
        ["edge_color","COLOR","COLOR{0,1,1}","edge color"],
        ["draw_edges","bool","false","display edges"],
        ["color","COLOR","COLOR{1,0,0}","front color of triangles"],
        ["bgcolor","COLOR","COLOR{0,0,1}","back color of triangles"],
        ["two_sided","bool","true","render triangles as two-sided"],
        ["velocity_color","COLOR","COLOR{1,0,1}","velocity color"],
        ["draw_velocities","bool","false","display vertex velocites"]
    ])

generate_opts(
    ["MESH_OPTIONS",["3"]],
    ["QT_TETRAHEDRALIZED_VOLUME",["T"]],
    "mesh",
    [
        ["draw","bool","true","Show object in viewer"],
        ["name","std::string","std::string{}","Name to display in viewer"],
        ["vertex_color","COLOR","COLOR{1,1,0}","vertex color"],
        ["draw_vertices","bool","false","display vertices"],
        ["edge_color","COLOR","COLOR{0,1,1}","edge color"],
        ["draw_edges","bool","false","display edges"],
        ["color","COLOR","COLOR{1,0,0}","front color of triangles"],
        ["bgcolor","COLOR","COLOR{0,0,1}","back color of triangles"],
        ["two_sided","bool","true","render triangles as two-sided"],
        ["velocity_color","COLOR","COLOR{1,0,1}","velocity color"],
        ["draw_velocities","bool","false","display vertex velocites"]
    ])
