%#
%# Data flow here:
%#   The page receives a Query from the previous page, and maybe arguments
%#   corresponding to actions.  (If it doesn't get a Query argument, it pulls
%#   one out of the session hash.  Also, it could be getting just a raw query from
%#   Build/Edit.html (Advanced).)
%#
%#   After doing some stuff with default arguments and saved searches, the ParseQuery
%#   function (which is similar to, but not the same as, _parser in lib/RT/Assets.pm)
%#   converts the Query into a RT::Interface::Web::QueryBuilder::Tree.  This mason file
%#   then adds stuff to or modifies the tree based on the actions that had been requested
%#   by clicking buttons.  It then calls GetQueryAndOptionList on the tree to generate
%#   the SQL query (which is saved as a hidden input) and the option list for the Clauses
%#   box in the top right corner.
%#
%#   Worthwhile refactoring: the tree manipulation code for the actions could use some cleaning
%#   up.  The node-adding code is different in the "add" actions from in ParseQuery, which leads
%#   to things like ParseQuery correctly not quoting numbers in numerical fields, while the "add"
%#   action does quote it (this breaks SQLite).
%#
<& /Elements/Header, Title => $title &>
<& /Elements/Tabs, %TabArgs &>

<form method="post" action="Build.html" name="BuildQuery" id="BuildQuery">
<input type="hidden" class="hidden" name="SavedSearchId" value="<% $saved_search{'Id'} %>" />
<input type="hidden" class="hidden" name="Query" value="<% $query{'Query'} %>" />
<input type="hidden" class="hidden" name="Format" value="<% $query{'Format'} %>" />




<div id="pick-criteria">
    <& Elements/PickCriteria, query => $query{'Query'}, catalogs => $catalogs &>
</div>
<& /Elements/Submit,  Label => loc('Add these terms'), SubmitId => 'AddClause', Name => 'AddClause'&>
<& /Elements/Submit, Label => loc('Add these terms and Search'), SubmitId => 'DoSearch', Name => 'DoSearch'&>


<div id="editquery">
<& /Search/Elements/EditQuery,
    %ARGS,
    actions => \@actions,
    optionlist => $optionlist,
    Description => $saved_search{'Description'},
    &>
</div>
<div id="editsearches">
    <& /Search/Elements/EditSearches, %saved_search, Type => 'Asset', CurrentSearch => \%query &>
</div>

<span id="display-options">
<& Elements/DisplayOptions,
    %ARGS, %query,
    AvailableColumns => $AvailableColumns,
    CurrentFormat    => $CurrentFormat,
&>
</span>
<& /Elements/Submit, Label => loc('Update format and Search'), Name => 'DoSearch', id=>"formatbuttons"&>
</form>

<%INIT>
use RT::Interface::Web::QueryBuilder;
use RT::Interface::Web::QueryBuilder::Tree;

my $title = loc("Asset Query Builder");

my %query = (Type => 'Asset');
for( qw(Query Format OrderBy Order RowsPerPage) ) {
    $query{$_} = $ARGS{$_};
}

my %saved_search = (Type => 'Asset');
my @actions = $m->comp( '/Search/Elements/EditSearches:Init', %ARGS, Type => 'Asset', Query => \%query, SavedSearch => \%saved_search);

if ( $NewQuery ) {

    # Wipe all data-carrying variables clear if we want a new
    # search, or we're deleting an old one..
    %query = ();
    %saved_search = ( Id => 'new', Type => 'Asset', );

    # ..then wipe the session out..
    delete $session{'CurrentAssetSearchHash'};

    # ..and the search results.
    $session{'assets'}->CleanSlate if defined $session{'assets'};
}

{ # Attempt to load what we can from the session and preferences, set defaults

    my $current = $session{'CurrentAssetSearchHash'};
    my $default = { Query => '',
                    Format => '',
                    OrderBy => 'Name',
                    Order => 'ASC',
                    RowsPerPage => 50 };

    for( qw(Query Format OrderBy Order RowsPerPage) ) {
        $query{$_} = $current->{$_} unless defined $query{$_};
        $query{$_} = $default->{$_} unless defined $query{$_};
    }

    for( qw(Order OrderBy) ) {
        if (ref $query{$_} eq "ARRAY") {
            $query{$_} = join( '|', @{ $query{$_} } );
        }
    }
    if ( $query{'Format'} ) {
        # Clean unwanted junk from the format
        $query{'Format'} = $m->comp( '/Elements/ScrubHTML', Content => $query{'Format'} );
    }
}

my $ParseQuery = sub {
    my ($string, $results) = @_;

    my $tree = RT::Interface::Web::QueryBuilder::Tree->new('AND');
    @$results = $tree->ParseAssetSQL( Query => $string, CurrentUser => $session{'CurrentUser'} );

    return $tree;
};

my @parse_results;
my $tree = $ParseQuery->( $query{'Query'}, \@parse_results );

# if parsing went poorly, send them to the edit page to fix it
if ( @parse_results ) {
    push @actions, @parse_results;
    return $m->comp(
        "Edit.html",
        Query => $query{'Query'},
        Format => $query{'Format'},
        SavedSearchId => $saved_search{'Id'},
        actions => \@actions,
    );
}

my @options = $tree->GetDisplayedNodes;
my @current_values = grep defined, @options[@clauses];
my @new_values = ();

my $cf_field_names =
    join "|",
     map quotemeta,
    grep { $RT::Assets::FIELD_METADATA{$_}->[0] eq 'CUSTOMFIELD' }
    sort keys %RT::Assets::FIELD_METADATA;

# Try to find if we're adding a clause
foreach my $arg ( keys %ARGS ) {
    next unless $arg =~ m/^ValueOf(\w+|($cf_field_names).\{.*?\})$/
                && ( ref $ARGS{$arg} eq "ARRAY"
                     ? grep $_ ne '', @{ $ARGS{$arg} }
                     : $ARGS{$arg} ne '' );

    # We're adding a $1 clause
    my $field = $1;

    my ($op, $value);

    #figure out if it's a grouping
    my $keyword = $ARGS{ $field . "Field" } || $field;

    my ( @ops, @values );
    if ( ref $ARGS{ 'ValueOf' . $field } eq "ARRAY" ) {
        # we have many keys/values to iterate over, because there is
        # more than one CF with the same name.
        @ops    = @{ $ARGS{ $field . 'Op' } };
        @values = @{ $ARGS{ 'ValueOf' . $field } };
    }
    else {
        @ops    = ( $ARGS{ $field . 'Op' } );
        @values = ( $ARGS{ 'ValueOf' . $field } );
    }
    $RT::Logger->error("Bad Parameters passed into Query Builder")
        unless @ops == @values;

    for ( my $i = 0; $i < @ops; $i++ ) {
        my ( $op, $value ) = ( $ops[$i], $values[$i] );
        next if !defined $value || $value eq '';

        my $clause = {
            Key   => $keyword,
            Op    => $op,
            Value => $value,
        };

        push @new_values, RT::Interface::Web::QueryBuilder::Tree->new($clause);
    }
}


push @actions, $m->comp('/Search/Elements/EditQuery:Process',
    %ARGS,
    Tree     => $tree,
    Selected => \@current_values,
    New      => \@new_values,
);

# Rebuild $Query based on the additions / movements

my $optionlist_arrayref;
($query{'Query'}, $optionlist_arrayref) = $tree->GetQueryAndOptionList(\@current_values);

my $optionlist = join "\n", map { qq(<option value="$_->{INDEX}" $_->{SELECTED}>) 
                                  . ("&nbsp;" x (5 * $_->{DEPTH}))
                                  . $m->interp->apply_escapes($_->{TEXT}, 'h') . qq(</option>) } @$optionlist_arrayref;


my $catalogs = $tree->GetReferencedCatalogs;

# Deal with format changes
my ( $AvailableColumns, $CurrentFormat );
( $query{'Format'}, $AvailableColumns, $CurrentFormat ) = $m->comp(
    'Elements/BuildFormatString',
    %ARGS,
    catalogs => $catalogs,
    Format => $query{'Format'},
);


# if we're asked to save the current search, save it
push @actions, $m->comp( '/Search/Elements/EditSearches:Save', %ARGS, Type => 'Asset', Query => \%query, SavedSearch => \%saved_search);

# Populate the "query" context with saved search data

if ($ARGS{SavedSearchSave}) {
    $query{'SavedSearchId'} = $saved_search{'Id'};
}

# Push the updates into the session so we don't lose 'em

$session{'CurrentAssetSearchHash'} = {
    %query,
    SearchId    => $saved_search{'Id'},
    Object      => $saved_search{'Object'},
    Description => $saved_search{'Description'},
};


# Show the results, if we were asked.

if ( $ARGS{'DoSearch'} ) {
    my $redir_query_string = $m->comp(
        '/Elements/QueryString',
        %query,
        SavedSearchId => $saved_search{'Id'},
    );
    RT::Interface::Web::Redirect(RT->Config->Get('WebURL') . 'Asset/Search/Results.html?' . $redir_query_string);
    $m->abort;
}


# Build a querystring for the tabs

my %TabArgs = ();
if ($NewQuery) {
    $TabArgs{QueryString} = 'NewQuery=1';
}
elsif ( $query{'Query'} ) {
    $TabArgs{QueryArgs} = \%query;
}

</%INIT>

<%ARGS>
$NewQuery => 0
@clauses => ()
</%ARGS>