%%
%%  wings_menu_util.erl --
%%
%%     Menu utilities and helpers.
%%
%%  Copyright (c) 2002-2004 Bjorn Gustavsson
%%
%%  See the file "license.terms" for information on usage and redistribution
%%  of this file, and for a DISCLAIMER OF ALL WARRANTIES.
%%
%%     $Id: wings_menu_util.erl,v 1.39 2004/01/29 06:58:07 bjorng Exp $
%%

-module(wings_menu_util).
-export([directions/1,directions/2,scale/1,rotate/1,flatten/0,all_xyz/0]).

-include("wings.hrl").

directions(#st{selmode=Mode}) ->
    fun(B, Ns) ->
	    dirs(B, Mode, Ns)
    end.

dirs(1, Mode, Ns) -> dirs_1(Mode, Ns);
dirs(2, body, [duplicate|_]) -> {body,duplicate};
dirs(2, body, [move|_]) -> ignore;
dirs(2, body, [move_light|_]) -> ignore;
dirs(2, _Mode, Ns) ->
    Flags = magnet_props(normal, Ns),
    wings_menu:build_command({'ASK',{[],[normal],Flags}}, Ns);
dirs(3, _Mode, Ns) ->
    Flags = magnet_props(some_axis, Ns),
    wings_menu:build_command({'ASK',{[axis],[],Flags}}, Ns);
dirs(help, body, [move|_]) ->
    {"Move along std. axis",[],"Pick axis to move along"};
dirs(help, _Mode, Ns) -> dirs_help(Ns).

dirs_help([move|_]) ->
    {"Move along std. axis","Move along selection's normal",
     "Pick axis to move along"};
dirs_help([extrude|_]) ->
    {"Extrude along std. axis","Extrude along selection's normal",
     "Pick axis to extrude along"};
dirs_help([extrude_region|_]) ->
    {"Extrude along std. axis","Extrude along selection's normal",
     "Pick axis to extrude along"};
dirs_help([extract_region|_]) ->
    {"Extract along std. axis","Extract along selection's normal",
     "Pick axis to extract along"};
dirs_help([duplicate|_]) ->
    {"Duplicate; move along std. axis","Duplicate; don't move",
     "Duplicate; pick axis to move along"};
dirs_help(_) -> "".

dirs_1(body, Ns) -> directions([free,x,y,z], Ns);
dirs_1(_, Ns) -> directions([normal,free,x,y,z], Ns).

all_xyz() ->
    [{"All",all},
     {"X",x},
     {"Y",y},
     {"Z",z}].

%%%
%%% Scale sub-menu.
%%%
scale(St) ->
    case wings_pref:get_value(advanced_menus) of
	false -> basic_scale();
	true -> adv_scale(St)
    end.

%% Basic menu Scale command.

basic_scale() ->
    Names = [scale],
    Dirs = [uniform,x,y,z,{radial,x},{radial,y},{radial,z}],
    {"Scale",{scale,basic_scale_1(Dirs, Names)}}.

basic_scale_1([Dir|Dirs], Names) ->
    DirString = stringify_dir(Dir),
    Help = dir_help(Dir, Names),
    F = fun(_, Ns) -> wings_menu:build_command({Dir,center}, Ns) end,
    [{DirString,F,Help}|basic_scale_1(Dirs, Names)];
basic_scale_1([], _) -> [].

%% Advanced menu Scale commands.

adv_scale(#st{selmode=body}) -> adv_scale_1([]);
adv_scale(_) -> adv_scale_1([magnet]).

adv_scale_1(MagFlags) ->
    [{"Scale Uniform",{scale,fun(B, Ns) -> uniform_scale(B, Ns, MagFlags) end},
      [],MagFlags},
     {"Scale Axis",{scale,fun(B, Ns) -> scale(B, Ns, [], MagFlags) end},
      [],MagFlags},
     {"Scale Radial",{scale,fun(B, Ns) -> scale(B, Ns, [radial], MagFlags) end},
      [],MagFlags}].

uniform_scale(help, _, _) ->
    ChoosePoint = "Choose point to scale from",
    {"Scale uniformly from midpoint of selection",ChoosePoint,ChoosePoint};
uniform_scale(1, Ns, Flags) ->
    wings_menu:build_command({'ASK',{[],[center,uniform],Flags}}, Ns);
uniform_scale(_, Ns, Flags) ->
    wings_menu:build_command({'ASK',{[point],[],Flags}}, Ns).

scale(help, _, [radial],_) ->
    {"Scale outward from std. axis",
     "Pick axis and point to scale from",
     "Pick axis to scale out from"};
scale(help, _, [], _) ->
    {"Scale along std. axis",
     "Pick axis and point to scale from",
     "Pick axis to scale along"};
scale(1, Ns, Flags, _MagFlags) ->
    [scale_fun(x, Ns, Flags),
     scale_fun(y, Ns, Flags),
     scale_fun(z, Ns, Flags),
     {advanced,separator},
     scale_fun(last_axis, Ns, Flags),
     scale_fun(default_axis, Ns, Flags)];
scale(2, Ns, Flags, MagFlags) ->
    wings_menu:build_command({'ASK',{[axis,point],Flags,MagFlags}}, Ns);
scale(3, Ns, Flags, MagFlags) ->
    wings_menu:build_command({'ASK',{[axis_point],Flags,MagFlags}}, Ns).

scale_fun(Dir, Names, [radial]) ->
    scale_fun({radial,Dir}, Names, []);
scale_fun(Dir, Names, _Flags) ->
    DirString = stringify_dir(Dir),
    F = magnet_scale_rot_fun(Dir, center),
    Help0 = dir_help(Dir, Names),
    Help = {Help0,[],"Pick point to scale from"},
    {DirString,F,Help,magnet_props(Dir, Names)}.

stringify_dir({radial,Axis}) -> "Radial " ++ wings_util:stringify(Axis);
stringify_dir(Dir) -> wings_util:stringify(Dir).

%%%
%%% Rotate sub-menu.
%%%

rotate(#st{selmode=body}) -> rotate_1([]);
rotate(_) -> rotate_1([magnet]).

rotate_1(Flags) ->    
    {"Rotate",{rotate,fun rotate/2},[],Flags}.

rotate(help, _) ->
    {"Rotate around std. axis",
     "Pick axis and ref point",
     "Pick axis to rotate around"};
rotate(1, [rotate,Mode]=Ns) when Mode == vertex; Mode == body ->
    rotate_common(Ns);
rotate(1, Ns) ->
    [rotate_fun(normal, Ns)|rotate_common(Ns)];
rotate(2, Ns) ->
    MagFlags = magnet_props(any, Ns),
    wings_menu:build_command({'ASK',{[axis,point],[],MagFlags}}, Ns);
rotate(3, Ns) ->
    MagFlags = magnet_props(any, Ns),
    wings_menu:build_command({'ASK',{[axis_point],[],MagFlags}}, Ns).

rotate_common(Ns) ->
    [rotate_fun(free, Ns),
     rotate_fun(x, Ns),
     rotate_fun(y, Ns),
     rotate_fun(z, Ns),
     {advanced,separator},
     rotate_fun(last_axis, Ns),
     rotate_fun(default_axis, Ns)].

rotate_fun(Dir, Names) ->
    DirString = wings_util:stringify(Dir),
    F = magnet_scale_rot_fun(Dir, center),
    Help0 = dir_help(Dir, Names),
    Help = {Help0,[],"Pick point for axis to pass through"},
    Ps = magnet_props(Dir, Names),
    {DirString,F,Help,Ps}.

magnet_scale_rot_fun(Vec, Point) ->
    fun(1, Ns) ->
	    MagFlags = magnet_props(Vec, Ns),
	    wings_menu:build_command({'ASK',{[],[Point,Vec],MagFlags}}, Ns);
       (2, _Ns) ->
	    ignore;
       (3, Ns) ->
	    MagFlags = magnet_props(Vec, Ns),
	    wings_menu:build_command({'ASK',{[point],[Vec],MagFlags}}, Ns)
    end.

%%%
%%% Flatten submenu.
%%%

flatten() ->
    {"Flatten",{flatten,fun flatten/2}}.

flatten(help, _) ->
    {"Flatten to std. planes",
     "Pick plane and ref point on plane",
     "Pick plane"};
flatten(1, [flatten,vertex]) ->
    %% Vertex mode flatten.
    flatten_common();
flatten(1, _) ->
    %% Face mode flatten.
    [flatten_fun(normal)|flatten_common()];
flatten(2, Ns) ->
    wings_menu:build_command({'ASK',{[axis,point],[],[]}}, Ns);
flatten(3, Ns) ->
    wings_menu:build_command({'ASK',{[axis],[],[]}}, Ns).

flatten_common() ->
    [flatten_fun(x),
     flatten_fun(y),
     flatten_fun(z),
     {advanced,separator},
     {advanced,flatten_fun(last_axis)},
     {advanced,flatten_fun(default_axis)}].

flatten_fun(Vec) ->
    flatten_fun_1(Vec, Vec, wings_util:stringify(Vec)).

flatten_fun_1(Vec, Axis, String) ->
    F = fun(1, Ns) ->
		wings_menu:build_command(Vec, Ns);
	   (2, _Ns) ->
		ignore;
	   (3, Ns) ->
		wings_menu:build_command({'ASK',{[point],[Vec]}}, Ns)
	end,
    Help0 = dir_help(Axis, [flatten]),
    Help = {Help0,[],"Pick point on plane"},
    {String,F,Help,[]}.

%%%
%%% General directions.
%%%

directions([D|Dirs], Ns) ->
    [direction(D, Ns)|directions(Dirs, Ns)];
directions([], Ns) ->
    [{advanced,separator},
     {advanced,direction(last_axis, Ns)},
     {advanced,direction(default_axis, Ns)}].

direction(Dir, Ns) ->
    Str = wings_util:stringify(Dir),
    Help = dir_help(Dir, Ns),
    Ps = magnet_props(Dir, Ns),
    {Str,Dir,Help,Ps}.

magnet_props(normal, [rotate|_]) -> [];
magnet_props(_, [_,body]) -> [];
magnet_props(_, [move|_]) -> [magnet];
magnet_props(_, [scale|_]) -> [magnet];
magnet_props(_, [rotate|_]) -> [magnet];
magnet_props(_, _) -> [].

dir_help(Axis, Ns) when Axis == x; Axis == y; Axis == z ->
    dir_help_1(Ns, "the " ++ wings_util:stringify(Axis) ++ " axis");
dir_help(last_axis, Ns) ->
    dir_help_1(Ns, "the last axis");
dir_help(default_axis, Ns) ->
    dir_help_1(Ns, "the default axis");
dir_help({radial,Axis}, Ns) ->
    dir_help_1(Ns, [around|"the " ++ wings_util:stringify(Axis) ++ " axis"]);
dir_help(radial_x, Ns) ->
    dir_help_1(Ns, [around|"around the X axis"]);
dir_help(radial_y, Ns) ->
    dir_help_1(Ns, [around|"around the Y axis"]);
dir_help(radial_z, Ns) ->
    dir_help_1(Ns, [around|"around the Z axis"]);
dir_help(normal, Ns) ->
    dir_help_1(Ns, [normal|"along its normal"]);
dir_help(free, Ns) ->
    dir_help_1(Ns, [free|"freely in all directions"]);
dir_help(uniform, [scale|_]) ->
    "Scale equally in all directions".

%% Normal/Free.
dir_help_1([move|_], [NF|Text]) when NF == normal; NF == free ->
    "Move each element " ++ Text;
dir_help_1([rotate|_], [free|_Text]) ->
    "Rotate freely";
dir_help_1([rotate|_], [normal|_Text]) ->
    "Rotate around each element's normal";
dir_help_1([extrude|_], [NF|Text]) when NF == normal; NF == free ->
    "Extrude each element, then move it " ++ Text;
dir_help_1([extrude_region|_], [normal|_]) ->
    "Extrude faces as region, then move faces along the region's normal";
dir_help_1([extrude_region|_], [free|Text]) ->
    "Extrude faces as region, then move faces " ++ Text;
dir_help_1([extract_region|_], [normal|_]) ->
    "Extract faces, then move faces along the region's normal";
dir_help_1([extract_region|_], [free|Text]) ->
    "Extract faces, then move faces " ++ Text;
dir_help_1([flatten|_], [normal|_Text]) ->
    "Flatten elements to normal plane";
dir_help_1([lift|_], [normal|_]) ->
    "Lift face along its normal";
dir_help_1([lift|_], [free|Text]) ->
    "Lift face and move it " ++ Text;
dir_help_1([duplicate|_], [free|Text]) ->
    "Duplicate and move freely " ++ Text;

%% Axis
dir_help_1([move|_], Text) ->
    "Move each element along " ++ Text;
dir_help_1([extrude|_], Text) ->
    "Extrude elements, then move along " ++ Text;
dir_help_1([extrude_region|_], Text) ->
    "Extrude faces as region, then move along " ++ Text;
dir_help_1([extract_region|_], Text) ->
    "Extract faces, then move along " ++ Text;
dir_help_1([rotate|_], Text) ->
    "Rotate around " ++ Text;
dir_help_1([scale|_], [around|Text]) ->
    "Scale " ++ Text;
dir_help_1([scale|_], Text) ->
    "Scale along " ++ Text;
dir_help_1([flatten|_], Text) ->
    "Flatten to " ++ Text;
dir_help_1([flatten_move|_], Text) ->
    "Flatten and move to " ++ Text;
dir_help_1([lift|_], Text) ->
    "Lift face along " ++ Text;
dir_help_1([duplicate|_], Text) ->
    "Duplicate, then move along " ++ Text;
dir_help_1(_, _) -> "".
