package Cpanel::Easy::Apache::UI::CLI::Config;

# cpanel - Cpanel/Easy/Apache/UI/CLI/Config.pm    Copyright(c) 2017 cPanel, Inc.
#                                                           All rights Reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited

use FindBin;    # Curses: use the lib from the distribution, instead of system wide lib
use lib "$FindBin::RealBin/../lib";
use Curses::UI;
use Cpanel::Easy::Apache::Utils::Support ();
use Cpanel::Easy::Utils::cPVers          ();
use Cpanel::Easy::PHP5                   ();
no warnings qw(closure);

my $cpanel_ascii = <<'CPANEL_END';
        ____                  _
    ___|  _ \ __ _ _ __   ___| |    Easy
   / __| |_) / _` | '_ \ / _ \ |
  | (__|  __/ (_| | | | |  __/ |          Apache
   \___|_|   \__,_|_| |_|\___|_|

CPANEL_END

my $ENDOFLIFE    = $Cpanel::Easy::Apache::Utils::Support::ENDOFLIFE;
my $EXPERIMENTAL = $Cpanel::Easy::Apache::Utils::Support::EXPERIMENTAL;

sub main {
    my ( $self, $easy, $profile, $profile_setup ) = @_;    # already calculated file, hashref of that file

    if ( $easy->openssl_bug_check() ) {
        my $url = 'http://go.cpanel.net/eaissues';
        $easy->print_alert_color(
            'red',
            qq{\nOpenSSL Bug Notice\n\nYour server may be using a version of OpenSSL that contains a serious bug that can prevent Apache 2.x from running properly. \nBefore proceeding further, we ask that you please read the information provided at the following URL and take the proper steps to correct this issue on your server.\n\n$url\n}
        );
    }

    if ( !$easy->get_param('skip-xen-broken-xm-check') ) {
        my $envtype = '';
        if ( open my $et_fh, '<', '/var/cpanel/envtype' ) {
            $envtype = do { local $/; <$et_fh> };
            close $et_fh;
        }
        if ( $envtype =~ m{xen} ) {
            print "\n\n\n";
            $easy->print_alert_color( 'blue', q{'[_1]' environment detected}, 'Xen' );
            $easy->print_alert_color( 'red', q{Please read below carefully} );

            $easy->print_alert(qq{\nXen's 'xm console' does NOT support curses.\n\nIf you are logged in via 'xm console' you should exit now and re-login in via SSH or you will experience an unusable UI.\n\nOnce you are logged in via SSH you can use --skip-xen-broken-xm-check to avoid this prompt if you wish.\n});
            local $SIG{'ALRM'} = sub {
                print "\n\nTimed out waiting for response, exiting\n\n";
                exit;
            };
            $| = 1;
            print "\nHit enter (or wait 30 seconds) to exit or type 'not xm' and hit enter to continue: ";
            $| = 0;
            alarm(30);
            my $response = <STDIN>;
            chomp $response;
            if ( $response !~ m{^not\s+xm}i ) {
                exit;
            }
            else {
                alarm(0);
            }
        }
    }

    local $ENV{'return_after_fork_for_curses_ui'} = 0;
    local $ENV{'TERM'} = $ENV{'TERM'} =~ /^(xterm|xterm-256color)$/ ? 'xterm-color' : $ENV{'TERM'};    # case 2336

    pipe READ, WRITE;

    if ( fork == 0 ) {
        close READ;

        # if we do not want to coninue on after the Curses::UI:
        #    print WRITE "return_after_fork_for_curses_ui=1\n";
        # or make sure it will continue:
        #    print WRITE "return_after_fork_for_curses_ui=0\n";

        print WRITE "return_after_fork_for_curses_ui=1\n";    # turn it on here sp that any failure makes it stop

        my $cui = Curses::UI->new( '-clear_on_exit' => 1, '-color_support' => 0, '-mouse_support' => 1 );

        my $data_pass = {
            'step'  => 1,
            'steps' => {
                '1' => {
                    'name' => 'Deprecation Warning',
                    'code' => \&_dialog_deprecation,
                },
                '2' => {
                    'name' => 'Profile',
                    'code' => \&_dialog_profile,
                },
                '3' => {
                    'name' => 'Apache Version',
                    'code' => \&_dialog_apache,
                },
                '4' => {
                    'name' => 'PHP Version',
                    'code' => \&_dialog_php,
                },
                '5' => {
                    'name' => 'Short Options List',
                    'code' => \&_dialog_basic,
                },
                '6' => {
                    'name' => 'Exhaustive Options List',
                    'code' => \&_dialog_details,
                },
                '7' => {
                    'name' => 'Save Only',
                    'code' => \&_dialog_save_only,
                },
                '8' => {
                    'name' => 'Save and Build',
                    'code' => \&_dialog_save_and_build,
                },
            }
        };

        $easy->set_state_of_spec_includes_from_system($profile_setup);
        $easy->{'working_profile'} = $profile_setup;    # so CLI UI changes to arg are reflected in workgin_profile
        $data_pass->{'steps'}{ $data_pass->{'step'} }{'code'}->( $self, $easy, $profile, $profile_setup, $cui, $data_pass );

        $cui->mainloop();

        close WRITE;
        exit;
    }

    # waitpid( $pid, 0 ); # if wee need this create $pid above instead of checking fork() directly

    my $run_upcp = 0;

    close WRITE;
    while (<READ>) {
        chomp;

        if ( $_ =~ m{return_after_fork_for_curses_ui=(\d)} ) {
            $ENV{'return_after_fork_for_curses_ui'} = $1;
        }

        if ( $_ =~ m{set_param: ([^=]+)=(.*)} ) {
            $easy->set_param( $1, $2 );
        }

        if ( $_ =~ m{run_upcp_and_restart} ) {
            $run_upcp = 1;
        }
    }

    close READ;

    _run_upcp_and_restart($easy) if $run_upcp;

    return if $ENV{'return_after_fork_for_curses_ui'};
    return 1;
}

#### dialogs for main() ##

sub _dialog_deprecation {
    my ( $self, $easy, $profile, $profile_setup, $cui, $data_pass ) = @_;
    my $wid = 'deprecation';

    # breadcrumb
    my ( $prof_hr, $prof_order_ar ) = $self->get_profiles_hashref_and_order_arrayref($easy);
    my $win = __get_window( $cui, $wid, 'Deprecation Warning' );

    my $wye = 1;    # start on line 1, to give a lank line below window title
    __line_of_text( $win, "We will deprecate EasyApache 3 on December 31, 2018. After that date we will no longer update EasyApache 3. In cPanel & WHM version 78, we will remove support for EasyApache 3.", $wye++, 1 );
    $wye++;         # Blank line
    __line_of_text( $win, "If you do not upgrade to EasyApache 4, you cannot upgrade to cPanel & WHM version 78.", $wye++, 0 );
    $wye++;         # Blank line
    __line_of_text( $win, "We strongly recommend that you upgrade to EasyApache 4. Please use the EasyApache 4 Migration Tool in WHM UI to upgrade to EasyApache 4.", $wye++, 0 );
    $wye++;         # Blank line
    __line_of_text( $win, "For more information about EasyApache 4 read our EasyApache 4 documentation:", $wye++, 0 );
    __line_of_text( $win, "https://go.cpanel.net/EA4", $wye++, 0 );
    $wye++;         # Blank line
    __line_of_text( $win, "For more information about the deprecation, read our blog post:", $wye++, 0 );
    __line_of_text( $win, "https://go.cpanel.net/EA3Deprecation", $wye++, 0 );
    $wye++;         # Blank line
    $wye++;         # Blank line

    my $onpress = sub {
        shift->parent->loose_focus;
    };

    my $buttonbox = $win->add(
        'next'             => 'Buttonbox',
        '-buttonalignment' => 'middle',
        '-padbottom'       => 0,
        '-buttons'         => [
            {
                '-label'   => "[Continue]",
                '-onpress' => $onpress
            }
        ],
        '-y' => $wye++,
    );

    $wye++;    # Blank line
    __line_of_text( $win, "Press <Return>, <Enter>, or <Space> to continue.", $wye++, 0 );
    __line_of_text( $win, 'Press q to exit without any changes',              $wye++, 0 );
    __line_of_text( $win, 'Visit http://go.cpanel.net/ea for documentation',  $wye++, 1 );
    __line_of_text( $win, $easy->{'powered_by_line'},                         $wye++, 0 );

    # handle focus
    $buttonbox->focus();
    $win->onFocus( sub { $buttonbox->focus() } );
    $win->modalfocus();
    $cui->delete($wid);

    goto &_dialog_profile;
}

sub _dialog_profile {
    my ( $self, $easy, $profile, $profile_setup, $cui, $data_pass ) = @_;

    my $wid = 'profile';
    my $wye = 0;

    _dialog_upcp( $easy, $cui ) unless ( $easy->{'_'}{'cpanel_version_up_to_date'} );
    _dialog_last_failed( $easy, $cui ) if -s $easy->{'last_failed_file'};

    # breadcrumb
    my ( $prof_hr, $prof_order_ar ) = $self->get_profiles_hashref_and_order_arrayref($easy);
    my $profile_window = __get_window( $cui, $wid, 'Please choose a profile to load' );

    # This is used to determine if CloudLinux profile needs to be shown.
    my $cloud_linux_installed = Cpanel::Easy::Utils::CloudLinux::has_cloudlinux_support();

    # This is used to determine if 'Upgrade to CloudLinux' section needs
    # to be shown. (i.e. if cPanel is on VPS or OpenVZ mode, then it is not
    # CloudLinux compatible.)
    my $is_cloudlinux_supported = 1;
    if ( !$cloud_linux_installed ) {
        $is_cloudlinux_supported = Cpanel::Easy::Utils::CloudLinux::is_cloudlinux_supported();
    }

    # Basic profile (cpanel_default) should always be second in the list.
    # **DEFAULT** should always be the first in the list.
    my $p_list = $self->profile_names_arrayref( $easy, $cloud_linux_installed );
    my @profile_list = @{$p_list};

    # Find the selected profile index
    my $index = 0;
    if ( $profile ne $easy->{'profile_main'} ) {
        for ( my $i = 0; $i < @profile_list; $i++ ) {
            $index = $i if $profile_list[$i] eq $easy->get_param('profile');
        }
    }

    #########################################################################
    # If cPanel & WHM version 11.40 & CloudLinux compatible, then show
    # a section to upgrade to cloudlinux.
    #########################################################################
    my $disable_upgrade_cloudlinux = 0;
    if ( $easy->{'purchase_cloudlinux_data'} ) {
        $disable_upgrade_cloudlinux = $easy->{'purchase_cloudlinux_data'}->{'disable_upgrade'};
    }

    if ( !$cloud_linux_installed && $is_cloudlinux_supported && !$disable_upgrade_cloudlinux ) {
        my $cl_container = add_new_cloud_linux_section( $easy, $profile_window );
        $wye += $cl_container->height();
    }
    else {
        $wye++;    # add a space below window title bar if not showing message
    }

    my $info_container = new_profile_info_box( $easy, $profile_window, $wye );
    $wye += $info_container->height();

    my $prof_labels = {};
    for my $prof (@profile_list) {
        $prof_labels->{$prof} = $prof_hr->{$prof}{'_meta'}{'name'};
    }

    my ( $radio, $onpress, $wye2 ) = __get_generic_radio( $easy, $profile_window, $wye, $wid, \@profile_list, $prof_labels, $index );

    my $before_button = [
        {
            '-label'   => '[Profile Details]',
            '-onpress' => sub {
                my ($prof) = grep { defined } $radio->get();
                my $prof_name = $prof_hr->{$prof}{'_meta'}{'name'} || "Name: $prof";
                my $prof_note = $prof_hr->{$prof}{'_meta'}{'note'} || 'This profile does not have any information about itself.';
                my $rc        = $cui->dialog(
                    '-title'           => 'Profile Details',
                    '-message'         => "$prof_name\n\n$prof_note",
                    '-buttons'         => [ { '-label' => '[View Profile]', }, { '-label' => '[Close]', }, ],
                    '-selected'        => 0,
                    '-buttonalignment' => 'middle',
                );
                if ( $rc == 0 ) {

                    my ($prof) = grep { defined } $radio->get();
                    my $message = 'No information available';

                    $prof =~ s{ [.] yaml \z }{}xms;
                    if ( "$prof.yaml" eq $easy->{'profile_main'} || ( $prof =~ m{ \A \w+ \z }xms && -e "$easy->{'profile_custom_dir'}/$prof.yaml" ) ) {
                        $message = _get_profile_as_string( $easy, $prof );
                    }

                    my $dialog = $profile_window->add(
                        'scrolldialog' => 'Window',
                        '-title'       => 'Profile Details',
                        '-width'       => -1,
                        '-border'      => 1,
                        '-ipad'        => 1,
                        '-fg'          => -1,
                        '-bg'          => -1,
                    );
                    my $tv = $dialog->add(
                        'message', 'TextViewer',
                        -border     => 1,
                        -vscrollbar => 1,
                        -wrapping   => 1,
                        -padbottom  => 2,
                        -text       => $message,
                        -focusable  => 1,
                    );

                    my $bb = $dialog->add(
                        'buttons', 'Buttonbox',
                        -y         => -1,
                        -alignment => 'center',
                        -buttons   => [ { '-label' => '[Close]' } ],
                    );
                    $bb->set_routine(
                        'press-button',
                        sub {
                            my $this   = shift;
                            my $parent = $this->parent;
                            $parent->loose_focus();
                        }
                    );
                    $dialog->layout;
                    $bb->focus;
                    $dialog->modalfocus();
                    $profile_window->delete('scrolldialog');
                    $profile_window->draw();
                }
            },
        }
    ];
    my $after_button = [
        {
            '-label'   => '[Build Profile]',
            '-onpress' => sub {
                if ( __confirm_build($cui) ) {
                    $easy->set_param( 'profile', grep { defined } $radio->get() );
                    shift->parent->loose_focus;
                    $profile       = $easy->determine_profile( $easy->get_param('profile') );
                    $profile_setup = $easy->deserialize($profile);

                    _dialog_save_and_build( $self, $easy, $profile, $profile_setup, $cui, $data_pass );
                }
            },
        },
    ];

    __focus_and_add_next_button( $cui, $profile_window, $radio, $onpress, $wid, $wye2, $easy, $before_button, $after_button, 'Customize Profile', undef, "profiles" );

    $self->merge_profile( $easy, $easy->get_param('profile'), $profile_setup );

    $_[2] = $easy->get_param('profile');

    goto &_dialog_apache;
}

sub cloud_linux_upgrade_message {
    my $easy = shift;
    my $ref  = $easy->{'purchase_cloudlinux_data'};
    my $msg;

    if ($ref) {
        if ( $ref->{'server_timeout'} ) {
            $msg = "Contact your provider to upgrade to CloudLinux";
        }
        elsif ( $ref->{'url'} ) {
            $msg = sprintf( "Upgrade to CloudLinux at: %s", $ref->{'url'} );
        }
        else {
            $msg = sprintf( "Upgrade to CloudLinux by email: %s", $ref->{'email'} );
        }
    }
    else {
        $msg = '';
    }

    return $msg;
}

##
# This method is used to add a new section to show some information.
#
# Param: $profile_window - The window object to which the section
# needs to be added.
#
# Returns: $info_container - The information container.
##
sub new_profile_info_box {
    my ( $easy, $profile_window, $wye ) = @_;

    my $info_container = $profile_window->add(
        'infoContainer', 'Container',
        -y         => $wye,
        -border    => 1,
        -wrapping  => 1,
        -padbottom => 1,
        -height    => 6,      # message(3) + border(2) + txtoverflow(1)
        -focusable => 0,
    );

    my $y          = 0;
    my $info_label = $info_container->add(
        'infoLabel', 'Label',
        '-text'      => "Information",
        '-bold'      => 1,
        '-y'         => $y,
        '-padbottom' => 1,
    );
    $y += $info_label->height();

    # Check for cent os 7+ version for CentOS machines only if cloudlinux is not installed.
    my $is_centos7_plus = ( $easy->is_redhat() && $easy->{'getos_releaseversion'} >= 7 );

    my $basic22_24_msg = "Basic Apache 2.2 is the new name for the Basic profile. The new Basic Apache 2.4 profile includes Apache 2.4.";
    my $basic24_msg    = "Basic Apache 2.4 is the new name for the Basic profile. The profile includes Apache 2.4.";
    my $message        = $is_centos7_plus ? $basic24_msg : $basic22_24_msg;

    my $info_text = $info_container->add(
        'infoMessage', 'TextViewer',
        '-text'     => $message,
        '-wrapping' => 1,
        -x          => 1,
        '-y'        => $y,
    );

    return $info_container;
}

##
# This method is used to add a new section which explains the
# purpose of having CloudLinux.
#
# Param: $profile_window - The window object to which the section
# needs to be added.
#
# Returns: $cloud_linux_section - The section object.
##
sub add_new_cloud_linux_section {
    my ( $easy, $profile_window ) = @_;
    my $y = 0;

    my $cl_container = $profile_window->add(
        'cloudLinuxContainer', 'Container',
        -border    => 1,
        -wrapping  => 1,
        -padbottom => 1,
        -height    => 11,    # label(2) + message(6) + border(2) + txtoverflow(1)
        -focusable => 0,
    );

    my $cl_label = $cl_container->add(
        'cloudLinuxLabel', 'Label',
        '-text'      => "Upgrade to CloudLinux",
        '-bold'      => 1,
        '-y'         => $y,
        '-padbottom' => 1,
    );
    $y += $cl_label->height();

    my $cl_message = "Why CloudLinux? The CloudLinux Operating System provides a stable, secure and efficient environment for cPanel and EasyApache.\n\nLearn more about CloudLinux at: www.cloudlinux.com\n" . cloud_linux_upgrade_message($easy);
    my $cl_text    = $cl_container->add(
        'cloudLinuxMessage', 'TextViewer',
        '-text'     => $cl_message,
        '-wrapping' => 1,
        '-y'        => $y,
    );

    return $cl_container;
}

sub _dialog_apache {
    my ( $self, $easy, $profile, $profile_setup, $cui, $data_pass ) = @_;

    # breadcrumb

    my ( $ap_hr, $ap_order_ar, $cur ) = $self->get_apache_hashref_order_and_current($easy);

    my $wid = 'apache';
    my $wye = 1;

    my $index = 0;
    my $last  = ( scalar( @{$ap_order_ar} ) - 1 );

    my %support_info;

    # don't apply reverse optmod fix if loading saved template that has different Apache version than running
    my $status = $easy->check_apache_upgrade_direction( 'profile_ref' => $easy->{'working_profile'}, 'wanted_apache' => $easy->{'working_profile'}{'Apache'}{'version'} );

    # if upgrade/downgrade detected before apache selected, don't do anything..
    if ( $status ne q{nomajor} ) {
        $easy->set_param( 'disable_reverse_fix', 1 );
    }

  APV:
    for my $idx ( 0 .. $last ) {
        if ( $ap_order_ar->[$idx] eq $easy->get_param('apache') ) {
            $index = $idx;
            last APV;
        }
        elsif ( $ap_order_ar->[$idx] eq $profile_setup->{'Apache'}{'version'} ) {
            $index = $idx;
        }
    }

    my $ap_labels = {};
    for my $ap ( @{$ap_order_ar} ) {
        my $full = get_full_apache_version( $ap, $ap_hr );
        $ap_labels->{$ap} = "Apache $full";
        if ( $ap_hr->{$ap}{support} ) {
            my $support_txt = Cpanel::Easy::Apache::Utils::Support::get_short_deco( $ap_hr->{$ap}{support} );
            if ($support_txt) {
                $ap_labels->{$ap} .= "    *$support_txt*";
            }
        }
    }

    my $win = __get_window( $cui, $wid, 'Please choose which apache to build' );

    my $did_cur = 0;
    for my $v ( @{$ap_order_ar} ) {
        my $pretty = $v;
        $pretty = get_full_apache_version( $v, $ap_hr );

        if ( $profile_setup->{'Apache'}{'version'} eq $v ) {
            __line_of_text( $win, "Your profile has Apache $pretty chosen to build.", $wye );
            $wye++;
        }

        if ( !$did_cur ) {
            if ( defined $cur ) {
                if ( $cur eq $v ) {
                    my $pretty_running = $easy->_get_httpd_version();
                    __line_of_text( $win, "Your currently running apache is Apache $pretty_running.", $wye );
                    $wye++;
                    $did_cur++;
                }
            }
            else {
                __line_of_text( $win, 'Sorry, I could not determine the currently running apache version.', $wye );
                $wye++;
                $did_cur++;
            }
        }
    }

    $wye++;

    # get onchange event for Cpanel::Easy::Apache
    my $onchange = _onchange_radio( $easy, $win, 'Cpanel::Easy::Apache', $index );

    my ( $radio, $onpress, $wye2 ) = __get_generic_radio( $easy, $win, $wye, $wid, $ap_order_ar, $ap_labels, $index, $onchange );

    __focus_and_add_next_button( $cui, $win, $radio, $onpress, $wid, $wye2, $easy, undef, undef, undef, \%support_info, "Apache versions" );

    $profile_setup->{'Apache'}{'version'} = $easy->get_param($wid);
    $easy->{'working_profile'}->{ $easy->{'state_config'}{'main_name'} }{'version'} = $profile_setup->{'Apache'}{'version'};

    # detects Apache major upgrade/downgrade and reverses Asis, Env, and Version in working_profile directly
    if ( !$easy->get_param('disable_reverse_fix') ) {
        my $status = $easy->check_and_fix_reverse_optmods( 'profile_ref' => $easy->{'working_profile'}, 'wanted_apache' => $easy->{'working_profile'}{'Apache'}{'version'} );
    }

    goto &_dialog_php;
}

sub _dialog_php {
    my ( $self, $easy, $profile, $profile_setup, $cui, $data_pass ) = @_;

    # breadcrumb

    my $wid = 'php_spec';
    my $wye = 1;
    my $win = __get_window( $cui, $wid, 'Please choose which specific PHP version(s) to build' );

    my $preferred_ver;

    if ( $easy->{'_'}{'prefs'}{'always_do_the_oldest_phps'} || $easy->{'_'}{'prefs'}{'always_do_the_latest_phps'} ) {
        my @most_rec;
        my $latest_label;

        my $ns = "Cpanel::Easy::PHP5";
        my $latest = $easy->{'_'}{'prefs'}{'always_do_the_latest_phps'} ? $ns->latest_version() : $ns->oldest_version();
        $preferred_ver = $latest;
        $latest        = "5.$latest";
        $latest =~ s{\_}{.}g;
        push @most_rec, $latest;
        $latest_label = $latest;

        my $current = join ', ', @most_rec;

        my $message =
          $easy->{'_'}{'prefs'}{'always_do_the_latest_phps'}
          ? qq{You have chosen to always use the latest minor version of any major versions of PHP you have chosen.\n\nCurrently the latest minor versions are: $current\n\nTo re-enable the ability to choose the minor versions simply turn off the preference using the button below.}
          : qq{You have chosen to always use the oldest minor version of any major versions of PHP you have chosen.\n\nCurrently the oldest minor versions are: $current\n\nTo re-enable the ability to choose the minor versions simply turn off the preference using the button below.};

        my $special_rc = $cui->dialog(
            '-message'         => $message,
            '-buttonalignment' => 'middle',
            '-selected'        => 0,
            '-buttons'         => [
                {
                    '-label' => '[Use ' . $latest_label . ']',
                    '-value' => 5,
                },
                {
                    '-label' => "[No PHP]",
                    '-value' => 3,
                },
                {
                    '-label' => '[Turn off this preference]',
                    '-value' => 1,
                },
            ],
        );

        if ( $special_rc == 1 ) {
            $easy->{'_'}{'prefs'}{'always_do_the_latest_phps'} = 0;
            $easy->set_param( 'always_do_the_latest_phps', 0 );

            $easy->{'_'}{'prefs'}{'always_do_the_oldest_phps'} = 0;
            $easy->set_param( 'always_do_the_oldest_phps', 0 );

            my $pr_hr = $easy->deserialize( $easy->{'prefs_file'} );
            $pr_hr->{'always_do_the_latest_phps'} = 0;
            $pr_hr->{'always_do_the_oldest_phps'} = 0;

            $easy->serialize( $easy->{'prefs_file'}, $pr_hr );
        }
        elsif ( $special_rc =~ /^[35]$/ ) {
            for my $ns ( keys %$profile_setup ) {
                $easy->set_ns_value_in_profile( $ns, $profile_setup, 0 ) if $ns =~ /::PHP[45](?:\:\:\d+_\d+)?$/;
            }

            if ( $special_rc == 5 ) {
                my $ns = 'Cpanel::Easy::PHP5';
                $easy->set_ns_value_in_profile( $ns,                         $profile_setup, 1 );
                $easy->set_ns_value_in_profile( $ns . "::" . $preferred_ver, $profile_setup, 1 );
            }

            goto &_dialog_basic;
        }
        else {
            $cui->status('Unknown RC from dialog ');
            sleep 1;
        }
    }

    my $cur_php = Cpanel::LoadFile::loadfile('/usr/local/apache/conf/php.version');
    chomp $cur_php;
    my ( $main, @sub ) = split( /\./, $cur_php );
    my $subv = join( '_', @sub );

    # this will make the version be whats installed not whats in the profile: Case 3054:
    # $easy->set_param( 'php' . $main, $subv ) if !$easy->get_param( 'php' . $main );

    if ( my @installed = $self->get_current_php_versions($easy) ) {
        my $text = 'You currently have PHP ' . join( ' and ', @installed ) . ' installed.';
        __line_of_text( $win, $text, $wye, 1 );
        $wye++;
    }

    my %support_info;

    my @onpress;
    my $focus;

    my $parent_ns = 'Cpanel::Easy::PHP5';
    eval qq{require $parent_ns};

    my $index          = 0;
    my $picked         = $easy->get_ns_value_from_profile( $parent_ns, $profile_setup );
    my $latest_version = $parent_ns->latest_version($profile_setup);

    my @select_value = ( 'None', $parent_ns->versions() );
    my %select_label;
    $select_label{'None'} = 'None';

    # set it from profile ...
    for my $idx ( 1 .. @select_value - 1 ) {
        my $sub = $select_value[$idx];
        $index = $idx if $picked && $easy->get_ns_value_from_profile( $parent_ns . "::$sub", $profile_setup );
        $index ||= $idx if $picked && $sub eq $latest_version;

        my $label = "5.$sub";
        $label =~ s{\_}{\.}g;

        my $support_decoration = Cpanel::Easy::PHP5::support_decoration($sub);
        if ($support_decoration) {
            $label .= "   *$support_decoration*";
        }

        $select_label{$sub} = $label;

        my $support_level = Cpanel::Easy::PHP5::support_level($sub);
        if ($support_level) {
            $support_info{$support_level} = '* ' . Cpanel::Easy::PHP5::support_text($sub);
        }
    }

    # now see if the php.version needs used instead
    for my $idx ( 1 .. @select_value - 1 ) {
        $index = $idx if $easy->get_param("php5") eq $select_value[$idx];
    }

    __line_of_text( $win, "PHP 5", $wye, 1 );
    $wye++;

    # get onchange event for Cpanel::Easy::PHP5
    my $onchange = _onchange_radio( $easy, $win, 'Cpanel::Easy::PHP5', $index );

    my ( $radio, $onpress, $wye2 ) = __get_generic_radio( $easy, $win, $wye, 'php5', \@select_value, \%select_label, $index, $onchange );

    $easy->set_param( 'php5', () );
    push @onpress, $onpress;
    $focus = $radio if !$focus;
    $wye = $wye2;

    my $onpress2 = sub {
        for my $code (@onpress) {
            $code->(@_);
        }
    };

    __focus_and_add_next_button( $cui, $win, $focus, $onpress2, $wid, $wye, $easy, undef, undef, undef, \%support_info, "PHP versions" );

    my $any_selected = 0;
    my $param        = $easy->get_param('php5');
    for my $spec ( $parent_ns->versions() ) {
        my $selected = $param eq $spec ? 1 : 0;
        $any_selected ||= $selected;
        $easy->set_ns_value_in_profile( $parent_ns . "::$spec", $profile_setup, $selected );
    }
    $easy->set_ns_value_in_profile( $parent_ns, $profile_setup, $any_selected );

    goto &_dialog_basic;
}

sub _dialog_basic {
    my ( $self, $easy, $profile, $profile_setup, $cui, $data_pass ) = @_;

    my $wid = 'basic';
    my $pos = 1;

    %{ $easy->{'working_profile'} } = %{$profile_setup};

    my $win = __get_window( $cui, $wid, 'Short Options List' );
    my ( $radio, $onpress, $wye2 ) = __get_basic_checkboxes( $self, $easy, $win, $pos, $wid );
    $pos += $wye2;

    $radio->focus();
    $radio->{'-focus'} = 1;
    $win->onFocus( sub { $radio->focus() } );

    my $custom_module_container = add_custom_module_info( $easy, $pos, $win );
    $pos += $custom_module_container->height();

    $win->add(
        $wid . 'finalchoice',
        'Buttonbox',
        '-y'               => $pos,
        '-buttonalignment' => 'middle',
        '-selected'        => 0,
        '-buttons'         => [
            {
                '-label'   => '[Save and Build]',
                '-onpress' => sub {

                    if ( __confirm_build($cui) ) {
                        my %ons;

                      ON:
                        for my $ns ( grep { defined } $radio->get() ) {
                            $ons{$ns} = 1;
                            $easy->set_ns_value_in_profile( $ns, $profile_setup, 1 );
                        }

                        if ( ref $easy->{'cli_ui_basic_opts'} eq 'ARRAY' ) {
                          OFF:
                            for my $ns ( @{ $easy->{'cli_ui_basic_opts'} } ) {
                                next OFF if exists $ons{$ns};
                                $easy->set_ns_value_in_profile( $ns, $profile_setup, 0 );
                            }
                        }
                        _dialog_save_and_build( $self, $easy, $profile, $profile_setup, $cui, $data_pass );
                    }
                },
            },
            {
                '-label'   => '[Exhaustive Options List]',
                '-onpress' => sub {
                    shift->parent->loose_focus();

                    my %ons;

                  ON:
                    for my $ns ( grep { defined } $radio->get() ) {
                        $ons{$ns} = 1;
                        $easy->set_ns_value_in_profile( $ns, $profile_setup, 1 );
                    }

                    if ( ref $easy->{'cli_ui_basic_opts'} eq 'ARRAY' ) {
                      OFF:
                        for my $ns ( @{ $easy->{'cli_ui_basic_opts'} } ) {
                            next OFF if exists $ons{$ns};
                            $easy->set_ns_value_in_profile( $ns, $profile_setup, 0 );
                        }
                    }

                    _dialog_details( $self, $easy, $profile, $profile_setup, $cui, $data_pass );
                },
            },
        ],
    );
    $pos++;

    my $toggle_text = get_toggle_text_for_current_step("options");
    __line_of_text( $win, $toggle_text, $pos, 0 );
    $pos++;

    __line_of_text( $win, 'Press q to exit without any changes', $pos, 0 );
    $pos++;

    __line_of_text( $win, 'Visit http://go.cpanel.net/ea for documentation', $pos, 1 );
    $pos++;

    __line_of_text( $win, $easy->{'powered_by_line'}, $pos, 0 );

    $win->modalfocus();

    # $cui->delete( $wid );
}

sub add_custom_module_info {
    my ( $easy, $y_position, $profile_window ) = @_;

    my $custom_module_container = $profile_window->add(
        'customModuleContainer', 'Container',
        -border    => 1,
        -wrapping  => 1,
        -padbottom => 1,
        -height    => 6,            # message(3) + border(2) + txtoverflow(1)
        -focusable => 0,
        -x         => 2,
        -y         => $y_position
    );

    my $message = "To view custom modules that add additional options to EasyApache, visit our Custom Modules link - http://go.cpanel.net/customea \nAfter you unpack the custom module, restart EasyApache. We do not provide support for custom modules.";
    my $text    = $custom_module_container->add(
        'message', 'TextViewer',
        '-text'     => $message,
        '-wrapping' => 1,
    );

    return $custom_module_container;
}

sub _dialog_details {
    my ( $self, $easy, $profile, $profile_setup, $cui, $data_pass ) = @_;

    # breadcrumb

    $easy->{'working_profile'} = $profile_setup;

    my @windows_and_widgets = __get_windows_and_widgets_data_struct( $easy, $profile_setup, $cui, $profile );

    # Page index
    my $current_screen = 0;

    my $button_focus = 0;

    my @first;
    my @last;

    # Page screens
    my @screen;
    my $i = 0;

    # list of  these: ['Label', foo, bar] or { __type => 'Checkbox', foo => bar }

    for my $scr (@windows_and_widgets) {
        next if !grep { ref $_ eq 'HASH' } @{$scr};
        push @screen, add_window( $i, $#windows_and_widgets, @{$scr} );
        $i++;
    }

    my $current_widget = "$current_screen.$first[ $current_screen ]";
    $screen[$current_screen]->focus();

    #### - h - e - l - p - e - r - s - ##

    sub add_window {
        my ( $idx, $last_window_idx, @widgets ) = @_;
        my $pg          = $idx + 1;
        my $last        = $last_window_idx + 1;
        my $no_win_bind = $idx eq $last_window_idx ? 1 : 0;
        my $win         = __get_window( $cui, 'win_' . $idx, "Please choose specific options, pg. $pg/$last (use arrow keys to navigate)", $no_win_bind, $last, $pg );

        # $win->{'-vscrolllen'} = $last;
        # $win->{'-vscrollpos'} = $pg;

        $win->clear_binding('loose-focus');
        $win->set_binding( \&toggle_between_checkboxes_and_buttons, $cui->CUI_TAB(), $cui->KEY_BTAB() );

        # KEY_HOME() => 'goto_first_page',
        # "\cA"      => 'goto_first_page',
        # KEY_END()  => 'goto_last_page',
        # "\cE"      => 'goto_last_page',
        $win->set_binding( \&prev_page, $cui->KEY_LEFT() )  if $idx ne $last_window_idx;
        $win->set_binding( \&next_page, $cui->KEY_RIGHT() ) if $idx ne $last_window_idx;
        $win->set_binding( \&prev_widg, $cui->KEY_UP() );
        $win->set_binding( \&next_widg, $cui->KEY_DOWN() );
        $win->set_binding( \&prev_page, "\cP", $cui->KEY_PPAGE() );    # \cU works but the corresponding
        $win->set_binding( \&next_page, "\cN", $cui->KEY_NPAGE() );    # \cD is already bound special

        my $id  = 0;
        my $chr = 65;
        my $pos = 0;
        for my $widg (@widgets) {

            my $wid = chr($chr);
            $chr++;

            my $type;
            my @rest;
            if ( ref $widg eq 'HASH' ) {
                $first[$idx] = $id if !defined $first[$idx];
                $last[$idx]  = $id;
                $type        = delete $widg->{'__type'};

                # $widg->{'-label'} .= "$idx:-$id-";
                @rest = %{$widg};
                $wid  = $id;

                $id++;
            }
            else {
                $type = shift @{$widg};
                @rest = @{$widg};
            }

            $pos++;
            my $cb = $win->add(
                "$idx.$wid" => $type,
                @rest,
                '-y' => $pos,
            );
            $cb->set_binding( \&Curses::UI::Checkbox::toggle, $cui->KEY_ENTER() );
        }

        $pos++;
        my $nav = $win->add(
            'addsave'          => 'Buttonbox',
            '-buttonalignment' => 'middle',
            '-buttons'         => [
                {
                    '-label'   => '[Next Step]',
                    '-onpress' => sub {
                        _goto_finalchoice( $self, $easy, $profile, $profile_setup, $cui, $data_pass );
                    },
                },
            ],
            '-y' => $pos,
        );
        $pos++;

        $nav->set_binding( \&prev_page, $cui->KEY_LEFT() )  if $idx eq $last_window_idx;
        $nav->set_binding( \&next_page, $cui->KEY_RIGHT() ) if $idx eq $last_window_idx;
        $nav->set_binding( sub { exit; }, 'q', "\cC" ) if $no_win_bind;

        # Add an extra line to provide enough spacing.
        $pos++;

        __line_of_text( $win, 'Prev Screen: Ctrl-P, Arrow Left, Page Up(if your terminal allows binding to it)', $pos, 1 );
        $pos++;

        __line_of_text( $win, 'Next Screen: Ctrl-N, Arrow Right, Page Down(if your terminal allows binding to it)', $pos, 1 );
        $pos++;

        my $toggle_text = get_toggle_text_for_current_step("options");
        __line_of_text( $win, $toggle_text, $pos, 0 );
        $pos++;

        __line_of_text( $win, 'Press q to exit without any changes', $pos, 0 );
        $pos++;

        __line_of_text( $win, 'Visit http://go.cpanel.net/ea for documentation', $pos, 1 );
        $pos++;

        __line_of_text( $win, $easy->{'powered_by_line'}, $pos, 0 );

        return $win;
    }

    sub _goto_finalchoice {
        my ( $self, $easy, $profile, $profile_setup, $cui, $data_pass ) = @_;

        my $win = $cui->getobj('win_finalchoice');
        if ( !$win ) {
            $win = __get_window( $cui, 'win_finalchoice', 'What do you want to do now?' );

            my $buttons = $win->add(
                'finalchoice',
                'Buttonbox',
                '-y'               => 5,
                '-buttonalignment' => 'middle',
                '-selected'        => 0,
                '-buttons'         => [
                    {
                        '-label'   => '[Go Back]',
                        '-onpress' => sub {
                            my $widg = $screen[$current_screen]->getobj($current_widget);
                            $widg->focus();
                            toggle_between_checkboxes_and_buttons();
                        },
                    },
                    {
                        '-label'   => '[Save only (Do NOT build)]',
                        '-onpress' => sub {
                            _dialog_save_only( $self, $easy, $profile, $profile_setup, $cui, $data_pass );
                        },
                    },
                    {
                        '-label'   => '[Save and build]',
                        '-onpress' => sub {
                            if ( __confirm_build($cui) ) {
                                _dialog_save_and_build( $self, $easy, $profile, $profile_setup, $cui, $data_pass );
                            }
                        },
                    },
                ],
            );
            $buttons->focus();
        }
        else {
            my $buttons = $win->getobj('finalchoice');
            $buttons->focus();
        }
        __line_of_text( $win, $easy->{'powered_by_line'}, 7, 0 );
    }

    sub toggle_between_checkboxes_and_buttons {
        if ($button_focus) {
            $button_focus = 0;
            my $widg = $screen[$current_screen]->getobj($current_widget);
            $widg->focus();
        }
        else {
            $button_focus = 1;
            my $widg = $screen[$current_screen]->getobj('addsave');
            $widg->focus();
        }
    }

    sub next_page {
        $current_screen++;
        $current_screen = 0 if $current_screen > last_screen();
        $current_widget = "$current_screen.$first[ $current_screen ]";
        my $widg = $screen[$current_screen]->getobj($current_widget);

        # $cui->status("DEBUG Widget ID: $current_widget");sleep 1;
        $widg->focus();
    }

    sub prev_page {
        $current_screen--;
        $current_screen = last_screen() if $current_screen < 0;
        $current_widget = "$current_screen.$last[ $current_screen ]";
        my $widg = $screen[$current_screen]->getobj($current_widget);

        # $cui->status("DEBUG Widget ID: $current_widget");sleep 1;
        $widg->focus();
    }

    sub next_widg {
        my ( $win, $widg_idx ) = split /\./, $current_widget;
        if ( $widg_idx eq $last[$win] ) {
            &next_page;
        }
        else {
            $current_widget = "$current_screen." . ( $widg_idx + 1 );
            my $widg = $screen[$current_screen]->getobj($current_widget);

            # $cui->status("DEBUG Widget ID: $current_widget");sleep 1;
            $widg->focus();
        }
    }

    sub prev_widg {
        my ( $win, $widg_idx ) = split /\./, $current_widget;
        if ( $widg_idx eq $first[$win] ) {
            &prev_page;
        }
        else {
            $current_widget = "$current_screen." . ( $widg_idx - 1 );
            my $widg = $screen[$current_screen]->getobj($current_widget);

            # $cui->status("DEBUG Widget ID: $current_widget");sleep 1;
            $widg->focus();
        }
    }

    sub last_screen { return scalar(@screen) - 1 }
}

sub _dialog_save_only {
    my ( $self, $easy, $profile, $profile_setup, $cui, $data_pass ) = @_;

    # breadcrumb

    if ( !$easy->save_prefs_hashref() ) {
        $self->status('Could not save preferences');
        sleep 1;
    }

    my $long = '';

    if ( exists $ENV{ $$ . 'profile: save_as' } ) {
        $ENV{ $$ . 'profile: save_as' } =~ s{\W+}{}g;
        $ENV{ $$ . 'profile: save_as' } =~ s{^cpanel_}{my_cpanel_};

        if ( length( $ENV{ $$ . 'profile: save_as' } ) > 64 ) {
            $cui->status(q{'Profile' to save has more than 64 letters, numbers, or underscores.});
        }
        elsif ( length $ENV{ $$ . 'profile: save_as' } ) {

            $long = $easy->{'profile_custom_dir'} . '/' . $ENV{ $$ . 'profile: save_as' } . '.yaml';
        }
    }

    $easy->profile_revision_check( $profile_setup, $long );

    my ( $changed_hr, $loop_hr ) = $easy->apply_one_pass_implies_logic($profile_setup);
    $easy->handle_one_pass_hashes(
        $changed_hr,
        $loop_hr,
        sub { $cui->status( shift() ); sleep 2; },
        sub { $cui->status( shift() ); sleep 2; },
        sub { $cui->status( shift() ); sleep 2; exit; },
    );

    if ($long) {
        if ( !$easy->save_profile( $profile_setup, $long ) ) {
            $cui->status(qq{Could not save profile as '$long'});
            sleep 1;
        }
        else {
            $cui->status(qq{Profile saved as '$long'});
            sleep 1;
        }
    }

    rename $self->{'profile_main'}, $self->{'profile_prev'};

    if ( $easy->serialize( $easy->{'profile_main'}, $profile_setup ) ) {
        $cui->status("Profile Saved!");
        sleep 2;
    }
    else {
        $cui->status("Could not save profile, stopping...!");
        sleep 2;
        exit;
    }

    my $who = ( caller(1) )[3];
    if (   $who eq 'Cpanel::Easy::Apache::UI::CLI::Config::_dialog_save_and_build'
        || $who eq '_dialog_save_and_build' ) {
        return 0;
    }
    else {
        exit;
    }

    # TODO: do Curses version of Cpanel/Easy/Apache/UI/HTML/Config.pm save_only()
}

sub _dialog_save_and_build {
    my ( $self, $easy, $profile, $profile_setup, $cui, $data_pass ) = @_;

    no strict 'refs';
    my $rc = &{'_dialog_save_only'}(@_);

    if ( $rc ne '0' ) {
        exit;
    }

    $cui->status('Starting build process!');
    sleep 2;
    print WRITE "return_after_fork_for_curses_ui=0\n";

    for my $pref ( keys %{ $easy->{'prefs_details'} } ) {
        if ( $easy->{'prefs_details'}{$pref}{'no_save'} ) {
            print WRITE "set_param: $pref=$easy->{'_'}{'prefs'}{$pref}";
        }
    }
    exit;
}

sub _dialog_upcp {
    my $easy         = shift;
    my $cui          = shift;
    my $return_value = $cui->dialog(
        -title   => 'cPanel Update Available',
        -message => $easy->{'_'}{'cpanel_version_message'} . "\n\nWould you like to update now?",
        -buttons => [ { -label => '[Yes]' }, { -label => '[No]' } ],
    );

    if ( $return_value == 0 ) {
        print WRITE "run_upcp_and_restart\n";
        exit;
    }
}

sub _dialog_last_failed {
    my $easy         = shift;
    my $cui          = shift;
    my $return_value = $cui->dialog(
        -title   => 'Issues last time',
        -message => 'There were errors registered during  the last build. Would you like to review them before proceeding?',
        -buttons => [ { -label => '[Yes]' }, { -label => '[No]' } ],
    );

    if ( $return_value == 0 ) {
        my $text = '';
        if ( open my $fh, '<', $easy->{'last_failed_file'} ) {
            $text = do { local $/; <$fh> };
            close $fh;
        }
        else {
            $text = $easy->maketext( q{Could not open ‘[_1]’ for reading : [_2]}, $easy->{'last_failed_file'}, $! );
        }

        $cui->dialog(
            '-title'           => 'Issues last time',
            '-message'         => $text,
            '-buttonalignment' => 'left',
        );
    }
}

#### _dialog_save_only() utils ##
## TODO: Cpanel/Easy/Apache/UI/HTML/Config.pm 'save_only() utils' as needed by TODO above in _dialog_save_only() ##

#### super internal ##

sub __confirm_build {
    my ($cui) = @_;
    return 1
      if _confirmation_box( $cui, 'Confirm Build', 'Recompile Apache and PHP now?' )
      && _confirmation_box(
        $cui,
        'Please acknowledge',
        'Termination of the build process will result in data loss! The build process is designed to run in the background until complete. Prematurely killing this process will leave your Apache configuration in an unusable state requiring restoration from backups.',
        'I Understand',
        'Go Back'
      );
    return;
}

sub __get_basic_checkboxes {
    my ( $self, $easy, $win, $pos, $wid ) = @_;

    my ( $apache_ar, $php5_ar, $non_php_includes_ar, $prefs_ar ) = $self->get_opt_params_lists($easy);

    my @ns_s = ( @{ $easy->{'apache_ns_mods_in_basic'} }, @{$non_php_includes_ar} );
    my %selected;
    my %labels;
    my $idx = 0;
    my %skips;
    my %onpress_verify;
  NS_S:
    for my $ns (@ns_s) {
        my $ec = $easy->get_easyconfig_hr_from_ns_variations($ns);

        if ( $ec->{'skip'} || $ec->{'display_hide'} ) {
            $skips{$ns} = 1;
            next NS_S;
        }

        my $chk = $easy->get_ns_value_from_profile( $ns, $easy->{'working_profile'} );
        if ( $ec->{'reverse'} ) {
            $chk = $chk ? 0 : 1;
        }
        my $deco;
        if ( defined $ec->{'support'} ) {
            $deco = '*' . get_short_deco( $ec->{'support'} ) . '*';
        }

        $labels{$ns} = $ec->{'name'};
        $labels{$ns} .= "   $deco" if defined $deco;
        $selected{$idx} = $chk;
        for my $ver (qw(verify_on verify_off)) {
            if ( exists $ec->{$ver} ) {
                $onpress_verify{$idx}->{$ver} = $ec->{$ver};
            }
        }

        $idx++;
    }

    @ns_s = map { exists $skips{$_} ? () : $_ } @ns_s;

    my $onchange = sub {
        my $self = shift;

        my $idx = $self->get_active_id();
        my $val = $self->get_active_value();    # NS
        my %idxs;
        @idxs{ $self->id() } = ();              # currently selected items index lookup

        my $checked     = exists $idxs{$idx} ? 1 : 0;
        my $want_verify = 0;
        my $text        = '';

        if ( $checked && $onpress_verify{$idx}->{'verify_on'} ) {
            $want_verify = 1;
            $text        = $onpress_verify{$idx}->{'verify_on'};
        }

        if ( !$checked && $onpress_verify{$idx}->{'verify_off'} ) {
            $want_verify = 1;
            $text        = $onpress_verify{$idx}->{'verify_off'};
        }

        my $ok = $want_verify ? _confirmation_box( $win->parent, 'Confirm Selection', $text, 'Ok', 'Cancel' ) : 1;

        if ( !$ok ) {
            local $self->{'-onchange'} = undef;
            delete $idxs{$idx};

            if ($checked) {
                $self->clear_selection();              # clear all checkboxes. needed ?
                $self->set_selection( keys %idxs );    # re-check the others
            }
            else {
                $self->clear_selection();              # clear all checkboxes, needed ?
                $self->set_selection( keys %idxs, $idx );    # re-check it
            }
        }

    };

    $easy->{'cli_ui_basic_opts'} = \@ns_s;

    return __get_generic_radio( $easy, $win, $pos, $wid, \@ns_s, \%labels, \%selected, $onchange );
}

sub __get_generic_radio {
    my ( $easy, $win, $wye, $wid, $order_ar, $labels, $index, $onchange ) = @_;
    my $type = '-radio';
    if ( ref $index eq 'HASH' ) {
        $type = '-multi';
    }

    my $radio = $win->add(
        $wid . '_radiobuttonbox', 'Listbox',
        $type       => 1,
        '-height'   => scalar( @{$order_ar} ),
        '-values'   => $order_ar,
        '-labels'   => $labels,
        '-selected' => $index,
        '-y'        => $wye,
        '-x'        => 2,
        '-onchange' => $onchange,                # undef ok or: ref $onchange eq 'CODE' ? $onchange : sub {}, ????
    );

    $wye += @{$order_ar};

    my $onpress = sub {
        $easy->set_param( $wid, grep { defined } $radio->get() );

        # shift->status( join(/,/,$easy->get_param( $wid)) );
        shift->parent->loose_focus;
    };

    return ( $radio, $onpress, $wye );
}

sub __get_window {
    my ( $cui, $item, $title, $no_bind, $vscroll, $vpos ) = @_;

    my $win = $cui->add(
        $item     => 'Window',
        '-border' => 1,
        '-y'      => 1,
        '-fg'     => 'blue',
        '-bfg'    => 'green',
        '-tfg'    => 'green',
        '-sfg'    => 'green',
        '-title'  => $title,
        '-vscrollbar' => $vscroll ? 'right'  : 0,
        '-vscrolllen' => $vscroll ? $vscroll : 0,
        '-vscrollpos' => $vscroll ? $vpos    : 0,
    );

    $win->set_binding( sub { exit; }, 'q', "\cC" ) if !$no_bind;
    return $win;
}

##
# This subroutine gets the list name to be added to the toggle instruction
# text that is shown in the lower part of CLI screens.
# returns: Always returns a string with toggle instruction.
##
sub get_toggle_text_for_current_step {
    my ($name) = @_;
    my $toggle_list_name = "";

    if ($name) {
        $toggle_list_name = $name;
    }
    else {
        $toggle_list_name = "the list";
    }

    return "Tab key toggles between $toggle_list_name and menu navigation.";
}

sub __focus_and_add_next_button {
    my ( $cui, $win, $widget, $onpress, $wid, $wye, $easy, $before_button, $after_button, $next_step_text, $support_info, $toggle_list_name ) = @_;
    $next_step_text = 'Next Step' if !$next_step_text;
    $widget->focus();
    $widget->{'-focus'} = 1;
    $win->onFocus( sub { $widget->focus() } );

    my @buttons = (
        {
            '-label'   => "[$next_step_text]",
            '-onpress' => $onpress,
        },
    );

    if ( ref $before_button eq 'ARRAY' ) {
        unshift @buttons, @{$before_button};
    }

    if ( ref $after_button eq 'ARRAY' ) {
        push @buttons, @{$after_button};
    }

    my $why = $wye + 1;
    $win->add(
        'next'             => 'Buttonbox',
        '-buttonalignment' => 'middle',
        '-padbottom'       => 0,
        '-buttons'         => \@buttons,
        '-y'               => $why,
    );
    $why++;

    # Add an extra line to provide enough spacing.
    $why++;

    my $toggle_text = get_toggle_text_for_current_step($toggle_list_name);
    __line_of_text( $win, $toggle_text, $why, 0 );
    $why++;

    __line_of_text( $win, 'Press q to exit without any changes', $why, 0 );
    $why++;

    if ($support_info) {
        __line_of_text( $win, '', $why, 0 );
        $why++;
        for my $k ( sort keys %$support_info ) {
            __line_of_text( $win, $support_info->{$k}, $why, 0 );
            $why++;
        }
        __line_of_text( $win, 'See also ' . $Cpanel::Easy::Apache::Utils::Support::URL, $why, 0 );
        $why++;
        __line_of_text( $win, '', $why, 0 );
        $why++;

    }

    __line_of_text( $win, 'Visit http://go.cpanel.net/ea for documentation', $why, 1 );
    $why++;

    __line_of_text( $win, $easy->{'powered_by_line'}, $why, 0 );

    $win->modalfocus();
    $cui->delete($wid);
}

sub __line_of_text {
    my ( $win, $text, $why, $bold ) = @_;

    $bold = 0 if !$bold;

    return $win->add(
        'line' . rand()  => 'Label',
        '-fg'            => 'green',
        '-tfg'           => 'green',
        '-y'             => $why,
        '-text'          => $text,
        '-bold'          => $bold,
        '-textalignment' => 'middle',
    );
}

sub __get_title_widget_arrayref {
    my ( $cui, $title ) = @_;
    return [
        'Label',
        '-fg'     => 'green',
        '-tfg'    => 'green',
        '-width'  => $cui->{'-width'},
        '-height' => 1,
        '-text'   => $title,
        '-bold'   => 1,
    ];
}

sub __get_blank_line_widget_arrayref {
    my ($cui) = @_;
    my $text = ' ' x ( $cui->{'-width'} - 2 );    # qq(Debug: Blank Line , -width = $cui->{'-width'}),
    return __get_title_widget_arrayref( $cui, $text );
}

sub __get_windows_and_widgets_data_struct {
    my ( $easy, $profile_setup, $cui, $profile ) = @_;

    my $perpage = $cui->{'-height'} - 12;         # adjust for buttons, notes, etc..
    my @windows_and_widgets;

    # %title map from @{ $easy->{'state'}{'order'} };
    my $ap_expr = join( '|', @{ $easy->{'state_config'}{'main_vers'} } );
    my $ap_regx = qr($ap_expr);

    my %title_map = (
        'Apache Built-in Modules' => [
            sort
            grep { $_ !~ m/^Cpanel::Easy::Apache::($ap_regx)$/ } grep( /^Cpanel::Easy::Apache::/, @{ $easy->{'state'}{'order'} } )
        ],
        'Other Modules' => [ sort grep !/PHP(4|5)/, @{ $easy->{'state_config'}{'include'} } ],
    );

    my @titles = ( 'Apache Built-in Modules', 'Other Modules' );

    if ( $easy->get_ns_value_from_profile( 'Cpanel::Easy::PHP5', $profile_setup ) ) {

        # Mask out version modules
        $title_map{'PHP 5.x'} = [
            sort
            grep { $_ !~ m/^Cpanel::Easy::PHP5::\d+_\d+$/ } grep( /^Cpanel::Easy::PHP5::/, @{ $easy->{'state'}{'order'} } )
        ];
        push @titles, 'PHP 5.x';
    }

    my $title = shift @titles;
    my %seen_titles;

    push @titles, 'last placeholder for while loop';

  BUILD_STEP:
    while (@titles) {
        my @widgs;

      WIDGET:
        for my $n ( 1 .. $perpage ) {
            if ( $n == 1 || !exists $seen_titles{$title} ) {
                my $disp_title = exists $seen_titles{$title} ? $title . ' continued' : $title;
                $seen_titles{$title}++;
                push @widgs, __get_title_widget_arrayref( $cui, $disp_title );
            }
            elsif ( scalar( @{ $title_map{$title} } ) == 0 ) {
                push @widgs, __get_blank_line_widget_arrayref($cui);

                if ( @titles == 1 ) {
                    next WIDGET if $n < $perpage;
                    push @windows_and_widgets, \@widgs;
                    last BUILD_STEP;
                }

                $title = shift @titles;
            }
            else {
                my $ns = shift @{ $title_map{$title} };
                my $ec = $easy->get_easyconfig_hr_from_ns_variations($ns);

                $cui->status('Gathering details...');

                redo WIDGET if ( $ec->{'skip'} || $ec->{'display_hide'} );

                my $chk = $easy->get_ns_value_from_profile( $ns, $profile_setup );
                if ( $ec->{'reverse'} ) {
                    $chk = $chk ? 0 : 1;
                }
                my $label = $ec->{'name'};

                if ( defined $ec->{'support'} ) {
                    $label .= ' *' . get_short_deco( $ec->{'support'} ) . '*';
                }

                if ( exists $ec->{'note'} ) {
                    $ec->{'note'} =~ s{[\n\r\t\f]+}{}g;
                    $label .= " ($ec->{'note'})" if $ec->{'note'};
                }

                push @widgs, {
                    '__type'    => 'Checkbox',
                    '-x'        => 2,
                    '-height'   => 1,
                    '-label'    => $label,
                    '-checked'  => $chk,
                    '-onchange' => sub {
                        my $self = shift;
                        my ( $easy, $ns, $ec ) = @{ $self->userdata() };
                        my $checked = $self->get();

                        my $want_verify = 0;
                        my $text        = '';

                        if ( $checked && $ec->{'verify_on'} ) {
                            $want_verify = 1;
                            $text        = $ec->{'verify_on'};
                        }

                        if ( !$checked && $ec->{'verify_off'} ) {
                            $want_verify = 1;
                            $text        = $ec->{'verify_off'};
                        }

                        my $ok = $want_verify ? _confirmation_box( $cui, 'Confirm Selection', $text, 'Ok', 'Cancel' ) : 1;

                        if ($ok) {
                            if ( $ec->{'reverse'} ) {
                                $checked = $checked ? 0 : 1;
                            }

                            $easy->set_ns_value_in_profile( $ns, $profile_setup, $checked );
                        }
                        else {
                            $self->toggle();
                        }
                    },
                    '-userdata' => [ $easy, $ns, $ec ],
                };
            }
        }

        push @windows_and_widgets, \@widgs if grep { ref $_ eq 'HASH' } @widgs;
    }

    # prefs screen here 'Preferences'

    my @pref_input;
    for my $pref ( @{ $easy->{'prefs_details_disp_order'} } ) {

        my $chk = $easy->{'_'}{'prefs'}{$pref} ? 1 : 0;

        if ( $easy->{'prefs_details'}{$pref}{'reverse'} ) {
            $chk = $chk ? 0 : 1;
        }

        push @pref_input, {
            '__type'    => 'Checkbox',
            '-x'        => 2,
            '-height'   => 1,
            '-label'    => $easy->{'prefs_details'}{$pref}{'name'},
            '-checked'  => $chk,
            '-onchange' => sub {
                my ($self) = @_;
                my $checked = $self->get() || 0;

                if ( $easy->{'prefs_details'}{$pref}{'reverse'} ) {
                    $checked = $checked ? 0 : 1;
                }

                $easy->{'_'}{'prefs'}{$pref} = $checked;
            },
        };
    }

    my @prefs_blanks;
    for ( 1 .. ( $perpage - ( scalar(@pref_input) + 1 ) ) ) {
        push @prefs_blanks, __get_blank_line_widget_arrayref($cui);
    }

    push @windows_and_widgets, [ __get_title_widget_arrayref( $cui, 'Preferences' ), @pref_input, @prefs_blanks, ];

    # save as screen here
    my @save_blanks;
    my $save_as_takes_up = 13;
    for ( 1 .. ( $perpage - $save_as_takes_up ) ) {
        push @save_blanks, __get_blank_line_widget_arrayref($cui);
    }

    my $profile_nm = $easy->get_param('profile') || '';
    $profile_nm = '' if $profile_nm =~ m{^cpanel_} || $profile_nm eq $easy->{'profile_main'};

    $ENV{ $$ . 'profile: save_as' } = $profile_nm;

    push @windows_and_widgets, [
        __get_title_widget_arrayref( $cui, 'When you "save" your choices in this interface it is referred to as the' ),
        __get_title_widget_arrayref( $cui, '"last saved version". Here, you can additionally "Save As" a specific name for reuse.' ),
        __get_blank_line_widget_arrayref($cui),

        # save as widgets here (adjust $save_as_takes_up accordingly)

        # Profile
        __get_title_widget_arrayref( $cui, '"Save As" should be a short name of letters, numbers, or underscores only.' ),
        {
            '__type'        => 'TextEditor',
            '-singleline'   => 1,
            '-showoverflow' => 1,
            '-title'        => 'Save As',
            '-border'       => 1,
            '-width'        => 64,
            '-height'       => 2,
            '-text'         => $profile_nm,
            '-pos'          => length($profile_nm),
            '-maxlines'     => 1,
            '-maxlength'    => 64,
            '-onchange'     => sub {
                $ENV{ $$ . 'profile: save_as' } = shift->get();

                # $cui->status("Save as: -$ENV{$$ . 'profile: save_as'}-   ");
            },
        },
        __get_blank_line_widget_arrayref($cui),
        __get_blank_line_widget_arrayref($cui),
        __get_blank_line_widget_arrayref($cui),

        # Name
        {
            '__type'        => 'TextEditor',
            '-singleline'   => 1,
            '-showoverflow' => 1,
            '-title'        => 'Profile Name',
            '-border'       => 1,
            '-width'        => 64,
            '-height'       => 2,
            '-text'         => $profile_setup->{'_meta'}{'name'},
            '-pos'          => length( $profile_setup->{'_meta'}{'name'} ),
            '-maxlines'     => 1,
            '-maxlength'    => 128,
            '-onchange'     => sub {
                $profile_setup->{'_meta'}{'name'} = shift->get() || 'Enter label here';

                # $cui->status("Name: -$profile_setup->{'_meta'}{'name'}-   ");
            },
        },
        __get_blank_line_widget_arrayref($cui),
        __get_blank_line_widget_arrayref($cui),
        __get_blank_line_widget_arrayref($cui),

        # Description
        {
            '__type'        => 'TextEditor',
            '-singleline'   => 0,
            '-showoverflow' => 1,
            '-title'        => 'Description',
            '-border'       => 1,
            '-width'        => 64,
            '-height'       => 2,
            '-text'         => $profile_setup->{'_meta'}{'note'},
            '-pos'          => length( $profile_setup->{'_meta'}{'note'} ),
            '-maxlines'     => 1,
            '-maxlength'    => 0,
            '-onchange'     => sub {
                $profile_setup->{'_meta'}{'note'} = shift->get() || 'Enter desription here';

                # $cui->status("Note: -$profile_setup->{'_meta'}{'note'}-   ");
            },
        },

        @save_blanks,
    ];

    return @windows_and_widgets;
}

sub _confirmation_box {
    my $cui   = shift || die "Curses::UI object missing";
    my $title = shift || 'Title missing';
    my $text  = shift || 'Text missing';
    my $yes   = shift || 'Yes';
    my $no    = shift || 'No';

    my $rc = $cui->dialog(
        '-title'           => $title,
        '-message'         => $text,
        '-buttons'         => [ { '-label' => "[$yes]", }, { '-label' => "[$no]", }, ],
        '-selected'        => 0,
        '-buttonalignment' => 'middle',
    );

    # return value is the array index of the button selected Yes is index 0
    return !$rc;
}

sub _get_profile_as_string {
    my ( $easy, $profile ) = @_;

    my $path = $easy->determine_profile($profile);
    my $hr   = $easy->deserialize($path);

    my %apver = %{ $easy->_apache_versions() };
    $apver{ $hr->{'Apache'}{'version'} } =~ /^(\d+\.\d+)/;

    my $ap_optmod = '';
    for my $opt ( sort keys %{ $hr->{'Apache'}{'optmods'} } ) {
        my $opt_hr = $easy->get_easyconfig_hr_from_ns_variations( 'Cpanel::Easy::Apache::' . $opt );
        $ap_optmod .= _get_option_text_string($opt_hr) if $easy->get_ns_value_from_profile( 'Cpanel::Easy::Apache::' . $opt, $hr );
    }

    for my $non_php ( grep !/PHP/, @{ $easy->{'state_config'}{'include'} } ) {
        my $opt_hr = $easy->get_easyconfig_hr_from_ns_variations($non_php);
        $ap_optmod .= _get_option_text_string($opt_hr) if $easy->get_ns_value_from_profile( $non_php, $hr );
    }

    my $pretty = '5';
    my $par_ns = qq{Cpanel::Easy::PHP5};

    if ( $easy->get_ns_value_from_profile( $par_ns, $hr ) ) {
        my %skip;

        for my $vr ( $par_ns->versions() ) {
            $skip{ $par_ns . "::$vr" }++;
            $pretty = "5.$vr" if $easy->get_ns_value_from_profile( $par_ns . "::$vr", $hr );
        }
        $pretty =~ s{\_}{\.}g;

        $ap_optmod .= "\nPHP $pretty\n";
        for my $ppopt ( grep /PHP5\::/, sort keys %{$hr} ) {
            next if exists $skip{$ppopt};
            my $opt_hr = $easy->get_easyconfig_hr_from_ns_variations($ppopt);
            $ap_optmod .= _get_option_text_string($opt_hr) if $easy->get_ns_value_from_profile( $ppopt, $hr );
        }
        $ap_optmod .= "\n";
    }

    return <<"END_PROFILE";
[$path]

Apache $apver{ $hr->{'Apache'}{'version'} }
$ap_optmod
END_PROFILE
}

# Examines the EasyApache profile for anything that needs to
# occur when a user selects an option.  Currently, this only works
# for radio lists (-multi=>1), not checkbox lists (-multi=>0).
#
# NOTE: It would be nice to modify this in the future to handle
#       both radio and checkbox lists.  That would consolidate the
#       mess in short and exhaustive option lists.
sub _onchange_radio {
    my $easy           = shift;    # easyapache object
    my $win            = shift;    # Curses window object
    my $parent_ns      = shift;    # base namespace used for lookups (e.g. Cpanel::Easy::Apache)
    my $last_selection = shift;    # index number of the option selected before being changed

    my $onchange = sub {
        my $self = shift;
        my $text;                  # what's displayed in dialog box

        my $current_selection = $self->get_active_id();
        my $current_value     = $self->get_active_value();
        my $current_label     = join( ' ', ( split( /\s+/, $self->{'-labels'}->{$current_value} ) )[ 0, 1 ] );
        my $current_ns        = sprintf( '%s::%s', $parent_ns, $current_value );
        my $current_ec        = $easy->get_easyconfig_hr_from_ns_variations($current_ns);

        my $last_value = $self->{'-values'}->[$last_selection];
        my $last_label = join( ' ', ( split( /\s+/, $self->{'-labels'}->{$last_value} ) )[ 0, 1 ] );
        my $last_ns    = sprintf( '%s::%s', $parent_ns, $last_value );
        my $last_ec    = $easy->get_easyconfig_hr_from_ns_variations($last_ns);

        # Piece together a display message.
        #
        # When choosing another option, we have the issue that a 'verify_off'
        # and 'verify_on' information must be displayed to the user.  This could
        # potentially mean that we'll display two dialogs; one for selecting
        # an option that has a 'verify_on', then another when the other 'verify_off'.
        # Instead, we piece together a single dialog that contains one
        # or both messages.

        if ( $last_ec->{'verify_off'} ) {
            my $tmp = sprintf( qq{You unselected %s:\n%s}, $last_label, $last_ec->{'verify_off'} );
            $tmp .= sprintf( q{ (%s)}, $last_ec->{'verify_off_url'} ) if $last_ec->{'verify_off_url'};
            $text = "$tmp\n\n";
        }

        if ( $current_ec->{'verify_on'} ) {
            my $tmp = sprintf( qq{You selected %s:\n%s}, $current_label, $current_ec->{'verify_on'} );
            $tmp .= sprintf( q{ (%s)}, $current_ec->{'verify_on_url'} ) if $current_ec->{'verify_on_url'};
            $text .= $tmp;
        }

        # Something to display.. do it
        if ($text) {
            my $ok = _confirmation_box( $win->parent, 'Confirm Selection', $text, 'Ok', 'Cancel' ) ? 1 : 0;
            local $self->{'-onchange'} = undef;    # temp disable while making changes

            if ($ok) {

                # update last_selection to properly compare states
                $last_selection = $current_selection;
            }
            else {
                # user did not accept selection, reset it
                $self->clear_selection();
                $self->set_selection($last_selection);
            }
        }

        # user selected something that has no verify.. update last selection so that if
        # the user selects something else, it will revert to this one
        else {
            $last_selection = $current_selection;
        }
    };

    return $onchange;
}

sub _get_option_text_string {
    my $opt_hr = shift;
    my $val    = '    ' . $opt_hr->{'name'} . "\n";
    if ( $opt_hr->{'note'} ) {
        $val .= '        ' . $opt_hr->{'note'} . "\n";
    }
    return $val;
}

sub _run_upcp_and_restart {
    my $self = shift;
    my $args = join( ' ', @ARGV );
    my $cmd  = q{/usr/local/cpanel/scripts/upcp && exec /usr/local/cpanel/scripts/easyapache} . ( $args ? q{ } . $args : q{} );
    unlink $self->{'pid_file'} if $self->{'pid_file'};
    exec( '/bin/sh', '-c', $cmd );
    exit;    # never reached
}

sub get_full_apache_version {
    my $xy        = shift;
    my $apache_hr = shift;

    if ( $apache_hr->{$xy}->{'version'} ) {
        return $apache_hr->{$xy}->{'version'};
    }

    if ( $apache_hr->{$xy}->{'src_cd2'} ) {
        my $xyz = $apache_hr->{$xy}->{'src_cd2'};
        $xyz =~ s/^(apache|httpd)[_-]//i;
        return $xyz;
    }

    return $xy;
}

sub get_short_deco {
    my $support_level = shift;
    Cpanel::Easy::Apache::Utils::Support::get_short_deco($support_level);
}

sub get_long_deco {
    my $support_level = shift;
    Cpanel::Easy::Apache::Utils::Support::get_long_deco($support_level);
}

1;
