001/*
002 * PlotSquared, a land and world management plugin for Minecraft.
003 * Copyright (C) IntellectualSites <https://intellectualsites.com>
004 * Copyright (C) IntellectualSites team and contributors
005 *
006 * This program is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 *
011 * This program is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014 * GNU General Public License for more details.
015 *
016 * You should have received a copy of the GNU General Public License
017 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
018 */
019package com.plotsquared.core.command;
020
021import com.google.inject.TypeLiteral;
022import com.plotsquared.core.configuration.caption.StaticCaption;
023import com.plotsquared.core.configuration.caption.Templates;
024import com.plotsquared.core.configuration.caption.TranslatableCaption;
025import com.plotsquared.core.permissions.Permission;
026import com.plotsquared.core.player.MetaDataAccess;
027import com.plotsquared.core.player.MetaDataKey;
028import com.plotsquared.core.player.PlotPlayer;
029import com.plotsquared.core.plot.Plot;
030import com.plotsquared.core.plot.comment.CommentInbox;
031import com.plotsquared.core.plot.comment.CommentManager;
032import com.plotsquared.core.plot.comment.PlotComment;
033import com.plotsquared.core.util.StringMan;
034import com.plotsquared.core.util.TabCompletions;
035import com.plotsquared.core.util.task.RunnableVal;
036import net.kyori.adventure.text.Component;
037import net.kyori.adventure.text.TextComponent;
038import net.kyori.adventure.text.minimessage.Template;
039
040import java.util.Collection;
041import java.util.Collections;
042import java.util.LinkedList;
043import java.util.List;
044import java.util.stream.Collectors;
045
046@CommandDeclaration(command = "inbox",
047        usage = "/plot inbox [inbox] [delete <index> | clear | page]",
048        permission = "plots.inbox",
049        category = CommandCategory.CHAT,
050        requiredType = RequiredType.PLAYER)
051public class Inbox extends SubCommand {
052
053    public void displayComments(PlotPlayer<?> player, List<PlotComment> oldComments, int page) {
054        if (oldComments == null || oldComments.isEmpty()) {
055            player.sendMessage(TranslatableCaption.of("comment.inbox_empty"));
056            return;
057        }
058        PlotComment[] comments = oldComments.toArray(new PlotComment[0]);
059        if (page < 0) {
060            page = 0;
061        }
062        // Get the total pages
063        // int totalPages = ((int) Math.ceil(12 *
064        int totalPages = (int) Math.ceil(comments.length / 12);
065        if (page > totalPages) {
066            page = totalPages;
067        }
068        // Only display 12 per page
069        int max = page * 12 + 12;
070        if (max > comments.length) {
071            max = comments.length;
072        }
073        TextComponent.Builder builder = Component.text();
074        builder.append(MINI_MESSAGE.parse(TranslatableCaption.of("list.comment_list_header_paged").getComponent(player) + '\n',
075                Template.of("amount", String.valueOf(comments.length)), Template.of("cur", String.valueOf(page + 1)),
076                Template.of("max", String.valueOf(totalPages + 1)), Template.of("word", "all")
077        ));
078
079        // This might work xD
080        for (int x = page * 12; x < max; x++) {
081            PlotComment comment = comments[x];
082            Component commentColored;
083            if (player.getName().equals(comment.senderName)) {
084                commentColored = MINI_MESSAGE
085                        .parse(
086                                TranslatableCaption.of("list.comment_list_by_lister").getComponent(player),
087                                Template.of("comment", comment.comment)
088                        );
089            } else {
090                commentColored = MINI_MESSAGE
091                        .parse(
092                                TranslatableCaption.of("list.comment_list_by_other").getComponent(player),
093                                Template.of("comment", comment.comment)
094                        );
095            }
096            Template number = Template.of("number", String.valueOf(x));
097            Template world = Template.of("world", comment.world);
098            Template plot_id = Template.of("plot_id", comment.id.getX() + ";" + comment.id.getY());
099            Template commenter = Template.of("commenter", comment.senderName);
100            Template commentTemplate = Template.of("comment", commentColored);
101            builder.append(MINI_MESSAGE
102                    .parse(
103                            TranslatableCaption.of("list.comment_list_comment").getComponent(player),
104                            number,
105                            world,
106                            plot_id,
107                            commenter,
108                            commentTemplate
109                    ));
110        }
111        player.sendMessage(StaticCaption.of(MINI_MESSAGE.serialize(builder.build())));
112    }
113
114    @Override
115    public boolean onCommand(final PlotPlayer<?> player, String[] args) {
116        final Plot plot = player.getCurrentPlot();
117        if (plot == null) {
118            player.sendMessage(TranslatableCaption.of("errors.not_in_plot"));
119            return false;
120        }
121        if (!plot.hasOwner()) {
122            player.sendMessage(TranslatableCaption.of("info.plot_unowned"));
123            return false;
124        }
125        if (args.length == 0) {
126            sendUsage(player);
127            for (final CommentInbox inbox : CommentManager.inboxes.values()) {
128                if (inbox.canRead(plot, player)) {
129                    if (!inbox.getComments(plot, new RunnableVal<>() {
130                        @Override
131                        public void run(List<PlotComment> value) {
132                            if (value != null) {
133                                int total = 0;
134                                int unread = 0;
135                                for (PlotComment comment : value) {
136                                    total++;
137                                    if (comment.timestamp > CommentManager
138                                            .getTimestamp(player, inbox.toString())) {
139                                        unread++;
140                                    }
141                                }
142                                if (total != 0) {
143                                    player.sendMessage(
144                                            TranslatableCaption.of("comment.inbox_item"),
145                                            Template.of("value", inbox + " (" + total + '/' + unread + ')')
146                                    );
147                                    return;
148                                }
149                            }
150                            player.sendMessage(
151                                    TranslatableCaption.of("comment.inbox_item"),
152                                    Template.of("value", inbox.toString())
153                            );
154                        }
155                    })) {
156                        player.sendMessage(
157                                TranslatableCaption.of("comment.inbox_item"),
158                                Template.of("value", inbox.toString())
159                        );
160                    }
161                }
162            }
163            return false;
164        }
165        final CommentInbox inbox = CommentManager.inboxes.get(args[0].toLowerCase());
166        if (inbox == null) {
167            player.sendMessage(
168                    TranslatableCaption.of("comment.invalid_inbox"),
169                    Template.of("list", StringMan.join(CommentManager.inboxes.keySet(), ", "))
170            );
171            return false;
172        }
173        final MetaDataKey<Long> metaDataKey = MetaDataKey.of(
174                String.format("inbox:%s", inbox),
175                new TypeLiteral<>() {
176                }
177        );
178        try (final MetaDataAccess<Long> metaDataAccess = player.accessTemporaryMetaData(metaDataKey)) {
179            metaDataAccess.set(System.currentTimeMillis());
180        }
181        final int page;
182        if (args.length > 1) {
183            switch (args[1].toLowerCase()) {
184                case "delete":
185                    if (!inbox.canModify(plot, player)) {
186                        player.sendMessage(TranslatableCaption.of("comment.no_perm_inbox_modify"));
187                        return false;
188                    }
189                    if (args.length != 3) {
190                        player.sendMessage(
191                                TranslatableCaption.of("commandconfig.command_syntax"),
192                                Template.of("value", "/plot inbox " + inbox + " delete <index>")
193                        );
194                        return true;
195                    }
196                    final int index;
197                    try {
198                        index = Integer.parseInt(args[2]);
199                        if (index < 1) {
200                            player.sendMessage(
201                                    TranslatableCaption.of("comment.not_valid_inbox_index"),
202                                    Templates.of("number", index)
203                            );
204                            return false;
205                        }
206                    } catch (NumberFormatException ignored) {
207                        player.sendMessage(
208                                TranslatableCaption.of("commandconfig.command_syntax"),
209                                Template.of("value", "/plot inbox " + inbox + " delete <index>")
210                        );
211                        return false;
212                    }
213
214                    if (!inbox.getComments(plot, new RunnableVal<>() {
215                        @Override
216                        public void run(List<PlotComment> value) {
217                            if (index > value.size()) {
218                                player.sendMessage(
219                                        TranslatableCaption.of("comment.not_valid_inbox_index"),
220                                        Templates.of("number", index)
221                                );
222                                return;
223                            }
224                            PlotComment comment = value.get(index - 1);
225                            inbox.removeComment(plot, comment);
226                            boolean success = plot.getPlotCommentContainer().removeComment(comment);
227                            if (success) {
228                                player.sendMessage(
229                                        TranslatableCaption.of("comment.comment_removed_success"),
230                                        Template.of("value", comment.comment)
231                                );
232                            } else {
233                                player.sendMessage(
234                                        TranslatableCaption.of("comment.comment_removed_failure"));
235                            }
236                        }
237                    })) {
238                        player.sendMessage(TranslatableCaption.of("errors.not_in_plot"));
239                        return false;
240                    }
241                    return true;
242                case "clear":
243                    if (!inbox.canModify(plot, player)) {
244                        player.sendMessage(TranslatableCaption.of("comment.no_perm_inbox_modify"));
245                    }
246                    inbox.clearInbox(plot);
247                    List<PlotComment> comments = plot.getPlotCommentContainer().getComments(inbox.toString());
248                    if (!comments.isEmpty()) {
249                        player.sendMessage(
250                                TranslatableCaption.of("comment.comment_removed_success"),
251                                Template.of("value", String.valueOf(comments))
252                        );
253                        plot.getPlotCommentContainer().removeComments(comments);
254                    }
255                    return true;
256                default:
257                    try {
258                        page = Integer.parseInt(args[1]);
259                    } catch (NumberFormatException ignored) {
260                        sendUsage(player);
261                        return false;
262                    }
263            }
264        } else {
265            page = 1;
266        }
267        if (!inbox.canRead(plot, player)) {
268            player.sendMessage(TranslatableCaption.of("comment.no_perm_inbox"));
269            return false;
270        }
271        if (!inbox.getComments(plot, new RunnableVal<>() {
272            @Override
273            public void run(List<PlotComment> value) {
274                displayComments(player, value, page);
275            }
276        })) {
277            player.sendMessage(TranslatableCaption.of("info.plot_unowned"));
278            return false;
279        }
280        return true;
281    }
282
283    @Override
284    public Collection<Command> tab(final PlotPlayer<?> player, final String[] args, final boolean space) {
285        if (args.length == 1) {
286            final List<String> completions = new LinkedList<>();
287            if (player.hasPermission(Permission.PERMISSION_INBOX_READ_OWNER)) {
288                completions.add("owner");
289            }
290            if (player.hasPermission(Permission.PERMISSION_INBOX_READ_PUBLIC)) {
291                completions.add("public");
292            }
293            if (player.hasPermission(Permission.PERMISSION_INBOX_READ_REPORT)) {
294                completions.add("report");
295            }
296            final List<Command> commands = completions.stream().filter(completion -> completion
297                            .toLowerCase()
298                            .startsWith(args[0].toLowerCase()))
299                    .map(completion -> new Command(null, true, completion, "", RequiredType.PLAYER, CommandCategory.CHAT) {
300                    }).collect(Collectors.toCollection(LinkedList::new));
301            if (player.hasPermission(Permission.PERMISSION_INBOX) && args[0].length() > 0) {
302                commands.addAll(TabCompletions.completePlayers(player, args[0], Collections.emptyList()));
303            }
304            return commands;
305        }
306        return TabCompletions.completePlayers(player, String.join(",", args).trim(), Collections.emptyList());
307    }
308
309}