%% Copyright 2015 Kolab Systems AG (http://www.kolabsys.com)
%%
%% Aaron Seigo (Kolab Systems) <seigo a kolabsys.com>
%%
%% This program is free software: you can redistribute it and/or modify
%% it under the terms of the GNU General Public License as published by
%% the Free Software Foundation, either version 3 of the License, or
%% (at your option) any later version.
%%
%% This program is distributed in the hope that it will be useful,
%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%% GNU General Public License for more details.
%%
%% You should have received a copy of the GNU General Public License
%% along with this program.  If not, see <http://www.gnu.org/licenses/>.

-module(kolab_guam_rules_SUITE).

% easier than exporting by name
-compile(export_all).

% required for common_test to work
-include_lib("common_test/include/ct.hrl").
-include("../src/rules/kolab_guam_rule_filter_groupware.hrl").

%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% common test callbacks %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Specify a list of all unit test functions
all() -> [
          kolab_guam_rule_filter_groupware_responsefiltering_test,
          kolab_guam_rule_filter_groupware_responsefiltering_multipacket_test
         ].

% required, but can just return Config. this is a suite level setup function.
init_per_suite(Config) ->
        Config.

% required, but can just return Config. this is a suite level tear down function.
end_per_suite(Config) ->
        Config.

% optional, can do function level setup for all functions,
% or for individual functions by matching on TestCase.
init_per_testcase(_TestCase, Config) ->
        Config.

% optional, can do function level tear down for all functions,
% or for individual functions by matching on TestCase.
end_per_testcase(_TestCase, Config) ->
        Config.

% c("apps/kolab_guam/test/kolab_guam_sup_tests.erl"). eunit:test(kolab_guam_sup_tests).

kolab_guam_rule_filter_groupware_responsefiltering_test(_TestConfig) ->
    %% Data to be fed into the test, one tuple per iteration
    %% Tuple format: { [] = folder_blacklist, input_data, correct_output }
    Data = [
        {
            [
             {<<"Calendar">>, <<"Calendar/">>},
             {<<"Calendar/Personal Calendar">>, <<"Calendar/Personal Calendar/">>},
             {<<"Configuration">>, <<"Configuration/">>},
             {<<"Contacts">>, <<"Contacts/">>},
             {<<"Contacts/Personal Contacts">>, <<"Contacts/Personal Contacts/">>},
             {<<"Files">>, <<"Files/">>},
             {<<"Journal">>, <<"Journal/">>},
             {<<"Notes">>, <<"Notes/">>},
             {<<"Tasks">>, <<"Tasks/">>}
            ],
            <<"* LIST (\\Noinferiors \\Subscribed) \"/\" INBOX\r\n* LIST (\\Subscribed) \"/\" Archive\r\n* LIST (\\Subscribed \\HasChildren) \"/\" Calendar\r\n* LIST (\\Subscribed) \"/\" \"Calendar/Personal Calendar\"\r\n* LIST (\\Subscribed) \"/\" Configuration\r\n* LIST (\\Subscribed \\HasChildren) \"/\" Contacts\r\n* LIST (\\Subscribed) \"/\" \"Contacts/Personal Contacts\"\r\n* LIST (\\Subscribed) \"/\" Drafts\r\n* LIST (\\Subscribed) \"/\" Files\r\n* LIST (\\Subscribed) \"/\" Journal\r\n* LIST (\\Subscribed) \"/\" Notes\r\n* LIST (\\Subscribed) \"/\" Sent\r\n* LIST (\\Subscribed) \"/\" Spam\r\n* LIST (\\Subscribed) \"/\" Tasks\r\n* LIST (\\Subscribed) \"/\" Trash\r\n7 OK Completed (0.000 secs 15 calls)\r\n">>,
            <<"* LIST (\\Noinferiors \\Subscribed) \"/\" INBOX\r\n* LIST (\\Subscribed) \"/\" Archive\r\n* LIST (\\Subscribed) \"/\" Drafts\r\n* LIST (\\Subscribed) \"/\" Sent\r\n* LIST (\\Subscribed) \"/\" Spam\r\n* LIST (\\Subscribed) \"/\" Trash\r\n7 OK Completed (0.000 secs 15 calls)\r\n">>
        }
    ],

    %% setup boilerplate
    Config = {},
    State = kolab_guam_rule_filter_groupware:new(Config),
    ServerConfig = kolab_guam_sup:default_imap_server_config(),
    { ok, ImapSession } = eimap:start_link(ServerConfig),

    %% create the rule, ready for testing
    ClientData = <<"7 list (subscribed) \"\" \"*\" return (special-use)">>,
    Split = eimap_utils:split_command_into_components(ClientData),
    { _, ReadyState } = kolab_guam_rule_filter_groupware:apply_to_client_message(ImapSession, ClientData, Split, State),

    %% run the dataset through the rule
    lists:foreach(fun({ Blacklist, Input, Filtered }) ->
                        BlacklistState = ReadyState#state{ blacklist = Blacklist },
                        { Filtered, _NewState } = kolab_guam_rule_filter_groupware:apply_to_server_message(ImapSession, Input, BlacklistState) end,
                  Data).

kolab_guam_rule_filter_groupware_responsefiltering_multipacket_test(_TestConfig) ->
    %% Data to be fed into the test, one tuple per iteration
    %% Tuple format: { [] = folder_blacklist, [] = input_data_packets, correct_output }
    Data = [
        {
            [
             {<<"Calendar">>, <<"Calendar/">>},
             {<<"Calendar/Personal Calendar">>, <<"Calendar/Personal Calendar/">>},
             {<<"Configuration">>, <<"Configuration/">>},
             {<<"Contacts">>, <<"Contacts/">>},
             {<<"Contacts/Personal Contacts">>, <<"Contacts/Personal Contacts/">>},
             {<<"Files">>, <<"Files/">>},
             {<<"Journal">>, <<"Journal/">>},
             {<<"Notes">>, <<"Notes/">>},
             {<<"Tasks">>, <<"Tasks/">>}
            ],
            [
                <<"* LIST (\\Noinferiors \\Subscribed) \"/\" INBOX\r\n* LIST (\\Subscribed) \"/\" Archive\r\n* LIST (\\Subscribed \\HasChildren) \"/\" Calendar\r\n* LIST (\\Subscribed) \"/\" \"Calendar/Personal Calendar\"\r\n* LIST (\\Subscribed) \"/\" Configuration\r\n* LIST (\\Subscribed \\HasChildren) \"/\" Contacts\r\n* LIST (\\Subscribed) \"/\" \"Contacts/Personal Contacts\"\r\n* LIST (\\Subscribed) \"/\" Drafts\r\n* LIST (\\Subscribed) \"/\" Files\r\n* LIST (\\Subscribed) \"/\" Journal\r\n* LIST (\\Subscribed)">>,
                <<" \"/\" Notes\r\n* LIST (\\Subscribed) \"/\" Sent\r\n* LIST (\\Subscribed) \"/\" Spam\r\n* LIST (\\Subscribed) \"/\" Tasks\r\n* LIST (\\Subscribed) \"/\" Trash\r\n7 OK Completed (0.000 secs 15 calls)\r\n">>
            ],
            <<"* LIST (\\Noinferiors \\Subscribed) \"/\" INBOX\r\n* LIST (\\Subscribed) \"/\" Archive\r\n* LIST (\\Subscribed) \"/\" Drafts\r\n* LIST (\\Subscribed) \"/\" Sent\r\n* LIST (\\Subscribed) \"/\" Spam\r\n* LIST (\\Subscribed) \"/\" Trash\r\n7 OK Completed (0.000 secs 15 calls)\r\n">>
        },
        %Filter complete packet
        {
            [
             {<<"Calendar">>, <<"Calendar/">>}
            ],
            [
                <<"* LIST (\\Subscribed \\HasChildren) \"/\" Calendar\r\n">>,
                <<"7 OK Completed (0.000 secs 15 calls)\r\n">>
            ],
            <<"7 OK Completed (0.000 secs 15 calls)\r\n">>
        },
        %Test folder names with square brackes
        {
            [
             {<<"[Calendar]">>,<<"[Calendar]/">>}
            ],
            [
                <<"* LSUB () \".\" INBOX\r\n* LSUB () \".\" \"[Stuff].Sent Mail.Sent\"\r\n* LSUB () \".\" [Calendar]\r\nwgku OK Lsub completed (0.001 + 0.000 secs).\r\n">>
            ],
            <<"* LSUB () \".\" INBOX\r\n* LSUB () \".\" \"[Stuff].Sent Mail.Sent\"\r\nwgku OK Lsub completed (0.001 + 0.000 secs).\r\n">>
        },
        %Split CR and LF
        {
            [
             {<<"Calendar">>, <<"Calendar/">>}
            ],
            [
                <<"* LIST (\\Noinferiors \\Subscribed) \"/\" INBOX\r">>,
                <<"\n* LIST (\\Subscribed \\HasChildren) \"/\" Calendar\r">>,
                <<"\n7 OK Completed (0.000 secs 15 calls)\r\n">>
            ],
            <<"* LIST (\\Noinferiors \\Subscribed) \"/\" INBOX\r\n7 OK Completed (0.000 secs 15 calls)\r\n">>
        },
        %Numeric folders
        {
            [
            ],
            [
                <<"* LIST () \"/\" 2017\r\n">>,
                <<"7 OK Completed (0.000 secs 15 calls)\r\n">>

            ],
            <<"* LIST () \"/\" 2017\r\n7 OK Completed (0.000 secs 15 calls)\r\n">>
        },
        %LIST-EXTENDED + LIST-STATUS response (as used by evolution)
        {
            [
             {<<"Calendar">>, <<"Calendar/">>},
             {<<"Calendar/Calendar2/">>, <<"Calendar/Calendar2/">>}
            ],
            [
                <<"* LIST (\\Noinferiors \\Subscribed) \"/\" INBOX\r\n">>,
                <<"* STATUS \"INBOX\" (MESSAGES 17 UNSEEN 16)\r\n">>,
                <<"* LIST (\\Subscribed \\HasChildren) \"/\" Calendar (CHILDINFO (\"SUBSCRIBED\"))\r\n">>,
                <<"* STATUS \"Calendar\" (MESSAGES 17 UNSEEN 16)\r\n">>,
                <<"* LIST (\\Subscribed \\HasNoChildren) \"/\" Calendar/Calendar2\r\n">>,
                <<"* STATUS \"Calendar/Calendar2\" (MESSAGES 17 UNSEEN 16)\r\n">>,
                <<"7 OK Completed (0.000 secs 15 calls)\r\n">>
            ],
            <<"* LIST (\\Noinferiors \\Subscribed) \"/\" INBOX\r\n* STATUS \"INBOX\" (MESSAGES 17 UNSEEN 16)\r\n7 OK Completed (0.000 secs 15 calls)\r\n">>
        },
        {
            [
             {<<"Calendar">>, <<"Calendar/">>}
            ],
            [
                % The first two lines are enough to trigger the in_blacklist fixme
                <<"* LIST () \".\" INBOX.!gmail\r\n">>,
                <<"* LIST (\\UnMarked) \".\" INBOX.test&AOE-sok\r\n">>,
                <<"* LIST (\\UnMarked) \".\" \"INBOX.SOME THING&AME-MOL&AME-S\"\r\n">>,
                <<"* LIST () \".\" INBOX.!!&APw-test.jean-test\r\n">>
            ],
            <<"* LIST () \".\" INBOX.!gmail\r\n* LIST (\\UnMarked) \".\" INBOX.test&AOE-sok\r\n* LIST (\\UnMarked) \".\" \"INBOX.SOME THING&AME-MOL&AME-S\"\r\n* LIST () \".\" INBOX.!!&APw-test.jean-test\r\n">>
        },
        %String literals
        {
            [
             {<<"Calendar">>, <<"Calendar/">>},
             {<<"Configuration">>, <<"Configuration/">>}
            ],
            [
                <<"* LIST (\Subscribed) \"/\" {8}\r\nCalendar\r\n">>,
                <<"* LIST (\Subscribed) \"/\" {13}\r\n">>,
                <<"Configuration\r\n">>,
                <<"* LIST (\Subscribed) \"/\" {6}\r\n">>,
                <<"Folder\r\n">>
            ],
            <<"* LIST (\Subscribed) \"/\" {6}\r\nFolder\r\n">>
        }
    ],
    %% setup boilerplate
    Config = {},
    State = kolab_guam_rule_filter_groupware:new(Config),
    ServerConfig = kolab_guam_sup:default_imap_server_config(),
    { ok, ImapSession } = eimap:start_link(ServerConfig),

    %% create the rule, ready for testing
    ClientData = <<"7 list (subscribed) \"\" \"*\" return (special-use)">>,
    Split = eimap_utils:split_command_into_components(ClientData),
    { _, ReadyState } = kolab_guam_rule_filter_groupware:apply_to_client_message(ImapSession, ClientData, Split, State),

    %% run the dataset through the rule
    lists:foreach(fun({ Blacklist, Input, Filtered }) ->
                          BlacklistState = ReadyState#state{ blacklist = Blacklist },
                          Filtered = filter_groupware_packets(ImapSession, BlacklistState, Input, <<>>) end,
                  Data).

filter_groupware_packets(_ImapSession, _ReadyState, [], Buffer) -> Buffer;
filter_groupware_packets(ImapSession, ReadyState, [Input|More], Buffer) ->
    { Processed, State } = kolab_guam_rule_filter_groupware:apply_to_server_message(ImapSession, Input, ReadyState),
    filter_groupware_packets(ImapSession, State, More, <<Buffer/binary, Processed/binary>>).




