11package de .kcodeyt .headsdb .database ;
22
33import cn .nukkit .Player ;
4- import cn .nukkit .Server ;
54import cn .nukkit .form .element .ElementButton ;
5+ import cn .nukkit .form .element .ElementInput ;
6+ import cn .nukkit .form .window .FormWindow ;
7+ import cn .nukkit .form .window .FormWindowCustom ;
68import cn .nukkit .form .window .FormWindowSimple ;
79import cn .nukkit .item .Item ;
10+ import cn .nukkit .utils .TextFormat ;
811import com .google .common .collect .Iterables ;
912import com .google .gson .Gson ;
1013import de .kcodeyt .heads .Heads ;
1114import de .kcodeyt .heads .util .HeadInput ;
15+ import de .kcodeyt .heads .util .ScheduledFuture ;
16+ import de .kcodeyt .headsdb .HeadsDB ;
1217import de .kcodeyt .headsdb .util .FormAPI ;
1318import de .kcodeyt .headsdb .util .HeadRender ;
1419import lombok .Getter ;
1520
16- import java .io .IOException ;
1721import java .io .InputStream ;
1822import java .io .InputStreamReader ;
1923import java .io .Reader ;
2024import java .net .HttpURLConnection ;
2125import java .net .URL ;
2226import java .util .*;
27+ import java .util .concurrent .CompletionException ;
2328import java .util .stream .Collectors ;
2429
2530@ Getter
@@ -31,96 +36,229 @@ public class Database {
3136 private final Map <String , Integer > pageCount ;
3237 private final List <Category > categories ;
3338 private final List <HeadEntry > headEntries ;
39+ private ScheduledFuture <Boolean > loadFuture ;
3440
3541 public Database () {
3642 this .pageCount = new HashMap <>();
3743 this .categories = new ArrayList <>();
3844 this .headEntries = new ArrayList <>();
3945 }
4046
41- public boolean reload () {
42- this .categories . clear ();
43- this .headEntries . clear () ;
44- return this .load ();
47+ public ScheduledFuture < Boolean > reload () {
48+ if ( this .loadFuture != null )
49+ return this .loadFuture ;
50+ return this .load (true );
4551 }
4652
47- public boolean load () {
48- try {
49- for (final CategoryEnum category : CategoryEnum .values ()) {
50- final HttpURLConnection connection = (HttpURLConnection ) new URL (API_URL + "?cat=" + category .getIdentifier ()).openConnection ();
51- connection .setRequestProperty ("User-Agent" , "Chrome" );
52- connection .connect ();
53-
54- if (connection .getResponseCode () == 200 ) {
55- try (final InputStream inputStream = connection .getInputStream ();
56- final Reader reader = new InputStreamReader (inputStream )) {
57- final List <Map <String , String >> values = GSON .<List <Map <String , String >>>fromJson (reader , List .class );
58- final List <HeadEntry > headEntries = values .stream ().map (map -> new HeadEntry (map .get ("name" ), map .get ("uuid" ), map .get ("value" ))).collect (Collectors .toList ());
59- this .headEntries .addAll (headEntries );
60- this .categories .add (new Category (category , category .getDisplayName (), Iterables .getLast (headEntries ).getTexture (), Collections .unmodifiableList (headEntries )));
53+ public ScheduledFuture <Boolean > load () {
54+ return this .load (false );
55+ }
56+
57+ private ScheduledFuture <Boolean > load (boolean clear ) {
58+ if (this .loadFuture != null )
59+ return this .loadFuture ;
60+ final List <HeadEntry > localHeadEntries = new ArrayList <>();
61+ final List <Category > localCategories = new ArrayList <>();
62+ return this .loadFuture = ScheduledFuture .supplyAsync (() -> {
63+ try {
64+ for (final CategoryEnum category : CategoryEnum .values ()) {
65+ final HttpURLConnection connection = (HttpURLConnection ) new URL (API_URL + "?cat=" + category .getIdentifier () + "&tags=true" ).openConnection ();
66+ connection .setRequestProperty ("User-Agent" , "Chrome" );
67+ connection .connect ();
68+
69+ if (connection .getResponseCode () == 200 ) {
70+ try (final InputStream inputStream = connection .getInputStream ();
71+ final Reader reader = new InputStreamReader (inputStream )) {
72+ final List <Map <String , String >> values = GSON .<List <Map <String , String >>>fromJson (reader , List .class );
73+ final List <HeadEntry > headEntries = values .stream ().
74+ map (map -> new HeadEntry (map .get ("name" ), map .get ("uuid" ), map .get ("value" ), map .get ("tags" ))).
75+ sorted (Comparator .comparing (HeadEntry ::getName )).
76+ collect (Collectors .toList ());
77+ localHeadEntries .addAll (headEntries );
78+ localCategories .add (new Category (category , category .getDisplayName (), Iterables .getLast (headEntries ).getTexture (), Collections .unmodifiableList (headEntries )));
79+ }
6180 }
81+
82+ connection .disconnect ();
6283 }
6384
64- connection .disconnect ();
85+ return true ;
86+ } catch (Exception e ) {
87+ throw new CompletionException (e );
6588 }
89+ }).whenComplete ((result , error ) -> {
90+ this .loadFuture = null ;
91+ if (result ) {
92+ if (clear ) {
93+ this .categories .clear ();
94+ this .headEntries .clear ();
95+ }
6696
67- return true ;
68- } catch (IOException e ) {
69- Server .getInstance ().getLogger ().error ("Error whilst loading heads from database api!" , e );
70- e .printStackTrace ();
71- return false ;
72- }
97+ this .headEntries .addAll (localHeadEntries );
98+ this .categories .addAll (localCategories );
99+ }
100+ });
73101 }
74102
75- private List <List <HeadEntry >> toPages (Category category , int count ) {
76- final List <HeadEntry > headEntries = category .getEntries ();
77- final int dbSize = headEntries .size ();
103+ private <T > List <List <T >> toPages (List <T > entries , int count ) {
104+ final int dbSize = entries .size ();
78105 if (dbSize <= 0 )
79106 return Collections .emptyList ();
80- final List <List <HeadEntry >> pages = new ArrayList <>();
107+ final List <List <T >> pages = new ArrayList <>();
81108 final int chunks = (dbSize - 1 ) / count ;
82109 for (int i = 0 ; i <= chunks ; i ++)
83- pages .add (headEntries .subList (i * count , i == chunks ? dbSize : (i + 1 ) * count ));
110+ pages .add (entries .subList (i * count , i == chunks ? dbSize : (i + 1 ) * count ));
84111 return Collections .unmodifiableList (pages );
85112 }
86113
87114 public void showForm (Player player ) {
88- final FormWindowSimple categoriesForm = new FormWindowSimple ("Select a category" , "" );
115+ final FormWindowSimple categoriesForm = new FormWindowSimple ("§lSelect a category" , "" );
89116 final List <Category > categories = Collections .unmodifiableList (new ArrayList <>(this .categories ));
117+ categoriesForm .addButton (new ElementButton ("Search a head" , HeadRender .createButtonImageById (HeadsDB .MHF_QUESTION_TEXTURE_ID )));
90118 for (final Category category : categories )
91119 categoriesForm .addButton (new ElementButton (category .getDisplayName (), HeadRender .createButtonImage (category .getDisplaySkin ())));
92120 FormAPI .create (player , categoriesForm , () -> {
93121 if (categoriesForm .wasClosed ())
94122 return ;
95- final Category category = categories .get (categoriesForm .getResponse ().getClickedButtonId ());
96- if (category == null )
97- return ;
98- final FormWindowSimple pagesForm = new FormWindowSimple ("Select a page" , "" );
99- final List <List <HeadEntry >> pages = this .toPages (category , this .pageCount .getOrDefault (player .getName (), 40 ));
123+
124+ if (categoriesForm .getResponse ().getClickedButtonId () == 0 ) {
125+ final FormWindowCustom searchForm = new FormWindowCustom ("§lSearch a head" );
126+ searchForm .addElement (new ElementInput ("Search head..." ));
127+ FormAPI .create (player , searchForm , () -> {
128+ if (searchForm .wasClosed ()) {
129+ FormAPI .createLast (player , categoriesForm );
130+ return ;
131+ }
132+
133+ final String searchInput = TextFormat .clean (Objects .toString (searchForm .getResponse ().getInputResponse (0 ), "" )).trim ();
134+ if (searchInput .isEmpty ()) {
135+ player .sendMessage ("§cCould not search with empty search input!" );
136+ return ;
137+ }
138+
139+ final String [] searchArgs = Arrays .stream (searchInput .split ("," )).
140+ map (String ::trim ).map (String ::toLowerCase ).toArray (String []::new );
141+
142+ final List <HeadEntry > foundEntries = new ArrayList <>();
143+ next_head :
144+ for (final HeadEntry headEntry : this .headEntries ) {
145+ final String name = headEntry .getName ().toLowerCase (Locale .ROOT );
146+ final String [] tags = headEntry .getTags () != null ?
147+ Arrays .stream (headEntry .getTags ().split ("," )).
148+ map (String ::toLowerCase ).toArray (String []::new ) : null ;
149+
150+ for (final String searchArg : searchArgs ) {
151+ if (name .startsWith (searchArg ) || name .endsWith (searchArg ) || name .contains (searchArg )) {
152+ foundEntries .add (headEntry );
153+ continue next_head ;
154+ } else if (tags != null )
155+ for (final String tag : tags ) {
156+ if (tag .startsWith (searchArg ) || tag .endsWith (searchArg ) || tag .contains (searchArg )) {
157+ foundEntries .add (headEntry );
158+ continue next_head ;
159+ }
160+ }
161+ }
162+ }
163+
164+ this .showForm (player , searchForm , foundEntries , "§lSearch: " + searchInput );
165+ });
166+ } else {
167+ final Category category = categories .get (categoriesForm .getResponse ().getClickedButtonId () - 1 );
168+ if (category == null )
169+ return ;
170+ this .showForm (player , categoriesForm , category .getEntries (), "§l" + category .getDisplayName ());
171+ }
172+ });
173+ }
174+
175+ private void showForm (Player player , FormWindow lastWindow , List <HeadEntry > headEntries , String title ) {
176+ final int pageCount = this .pageCount .getOrDefault (player .getName (), 40 );
177+ if (headEntries .size () > pageCount * pageCount ) {
178+ final FormWindowSimple pagesForm = new FormWindowSimple ("§lSelect a page" , "" );
179+ final List <List <List <HeadEntry >>> pages = this .toPages (this .toPages (headEntries , pageCount ), pageCount );
180+ for (int i = 0 ; i < pages .size (); i ++)
181+ pagesForm .addButton (new ElementButton ("Page " + (i + 1 ), HeadRender .createButtonImage (Iterables .getLast (Iterables .getLast (pages .get (i ))).getTexture ())));
182+ FormAPI .create (player , pagesForm , () -> {
183+ if (pagesForm .wasClosed ()) {
184+ FormAPI .createLast (player , lastWindow );
185+ return ;
186+ }
187+
188+ final FormWindowSimple subPagesForm = new FormWindowSimple ("§lSelect a subpage" , "" );
189+ final List <List <HeadEntry >> subPages = pages .get (pagesForm .getResponse ().getClickedButtonId ());
190+ for (int i = 0 ; i < subPages .size (); i ++)
191+ subPagesForm .addButton (new ElementButton ("Subpage " + (i + 1 ), HeadRender .createButtonImage (Iterables .getLast (subPages .get (i )).getTexture ())));
192+ FormAPI .create (player , subPagesForm , () -> {
193+ if (subPagesForm .wasClosed ()) {
194+ FormAPI .createLast (player , pagesForm );
195+ return ;
196+ }
197+
198+ final List <HeadEntry > headEntries0 = subPages .get (subPagesForm .getResponse ().getClickedButtonId ());
199+ if (headEntries0 == null )
200+ return ;
201+ final FormWindowSimple subForm = new FormWindowSimple (title , "" );
202+ for (final HeadEntry headEntry : headEntries0 )
203+ subForm .addButton (new ElementButton (headEntry .getName (), HeadRender .createButtonImage (headEntry .getTexture ())));
204+ FormAPI .create (player , subForm , () -> {
205+ if (subForm .wasClosed ()) {
206+ FormAPI .createLast (player , subPagesForm );
207+ return ;
208+ }
209+
210+ final HeadEntry headEntry = headEntries0 .get (subForm .getResponse ().getClickedButtonId ());
211+ if (headEntry == null )
212+ return ;
213+ this .giveItem (player , headEntry );
214+ });
215+ });
216+ });
217+ } else if (headEntries .size () > pageCount ) {
218+ final FormWindowSimple pagesForm = new FormWindowSimple ("§lSelect a page" , "" );
219+ final List <List <HeadEntry >> pages = this .toPages (headEntries , pageCount );
100220 for (int i = 0 ; i < pages .size (); i ++)
101221 pagesForm .addButton (new ElementButton ("Page " + (i + 1 ), HeadRender .createButtonImage (Iterables .getLast (pages .get (i )).getTexture ())));
102222 FormAPI .create (player , pagesForm , () -> {
103223 if (pagesForm .wasClosed ()) {
104- this . showForm (player );
224+ FormAPI . createLast (player , lastWindow );
105225 return ;
106226 }
107227
108- final List <HeadEntry > headEntries = pages .get (pagesForm .getResponse ().getClickedButtonId ());
109- if (headEntries == null )
228+ final List <HeadEntry > headEntries0 = pages .get (pagesForm .getResponse ().getClickedButtonId ());
229+ if (headEntries0 == null )
110230 return ;
111- final FormWindowSimple subForm = new FormWindowSimple (category . getDisplayName () , "" );
112- for (final HeadEntry headEntry : headEntries )
231+ final FormWindowSimple subForm = new FormWindowSimple (title , "" );
232+ for (final HeadEntry headEntry : headEntries0 )
113233 subForm .addButton (new ElementButton (headEntry .getName (), HeadRender .createButtonImage (headEntry .getTexture ())));
114234 FormAPI .create (player , subForm , () -> {
115- if (subForm .wasClosed ())
235+ if (subForm .wasClosed ()) {
236+ FormAPI .createLast (player , pagesForm );
116237 return ;
117- final HeadEntry headEntry = headEntries .get (subForm .getResponse ().getClickedButtonId ());
238+ }
239+
240+ final HeadEntry headEntry = headEntries0 .get (subForm .getResponse ().getClickedButtonId ());
118241 if (headEntry == null )
119242 return ;
120243 this .giveItem (player , headEntry );
121244 });
122245 });
123- });
246+ } else {
247+ final FormWindowSimple subForm = new FormWindowSimple (title , "" );
248+ for (final HeadEntry headEntry : headEntries )
249+ subForm .addButton (new ElementButton (headEntry .getName (), HeadRender .createButtonImage (headEntry .getTexture ())));
250+ FormAPI .create (player , subForm , () -> {
251+ if (subForm .wasClosed ()) {
252+ FormAPI .createLast (player , lastWindow );
253+ return ;
254+ }
255+
256+ final HeadEntry headEntry = headEntries .get (subForm .getResponse ().getClickedButtonId ());
257+ if (headEntry == null )
258+ return ;
259+ this .giveItem (player , headEntry );
260+ });
261+ }
124262 }
125263
126264 public void giveItem (Player player , HeadEntry headEntry ) {
0 commit comments